Hello Everybody
Here is a little xmas present from Team CTGlobal
This script is an improved version of the script used by MDT to for the “Execute Runbook” Step
I has the following improvements:
- Success/Failed check
- Retry if the webservice call fails
- Retrieval of the correct result, when a runbook has failed over to one or more servers.
- The currently included script in MDt, will accidently get the first result, which is the failed result, while the newst result is the succeded one.
This improves stability of the step by far.
This has been tested in a scenario that contains around 800 servers in each patch windows.
Each server executes around 20 runbooks in the TS.
Including retry/check status, this is about 20 calls per step.
So a total executions of 800*20*20 = 320,000 calls in a single Patch window!
It has been running for around 8 months, without any issues!
So we can call this Tested!
Hopefully this script can be included in a future version of MDT.
I will make sure that the correct people get the message, but for now you can get it here and replace the file in your MDT package.
[download id=”12884″]
ZTIExecuteRunbook.wsf:
<job id="ZTIExecuteRunbook"> <script language="VBScript" src="ZTIUtility.vbs"/> <script language="VBScript"> ' // *************************************************************************** ' // ' // This is a csutom version of the MDT toolkit script to enable retry ' // ' // Authorered by CTGlobal - 2018/02/19 ' // ' // *************************************************************************** ' // *************************************************************************** ' // ' // Copyright (c) Microsoft Corporation. All rights reserved. ' // ' // Microsoft Deployment Toolkit Solution Accelerator ' // ' // File: ZTIExecuteRunbook.wsf ' // ' // Version: 6.3.8443.1000 ' // ' // Purpose: Execute a System Center Orchestrator 2012 runbook ' // ' // Usage: cscript ZTIExecuteRunbook.wsf [/debug:true] ' // ' // *************************************************************************** Option Explicit RunNewInstance '//---------------------------------------------------------------------------- '// Main Class '//---------------------------------------------------------------------------- Class ZTIExecuteRunbook '//---------------------------------------------------------------------------- '// Global constant and variable declarations '//---------------------------------------------------------------------------- Dim iRetVal '//---------------------------------------------------------------------------- '// Constructor to initialize needed global objects '//---------------------------------------------------------------------------- Private Sub Class_Initialize End Sub '//---------------------------------------------------------------------------- '// Main routine '//---------------------------------------------------------------------------- Function Main Dim sURL Dim sMode Dim i, iCount Dim sID, sVar, sVal Dim sParameters Dim sRequest Dim oResult Dim oJob Dim sJob Dim bFinish Dim oStatus Dim oInstances Dim oInstance Dim oParameters Dim oParameter Dim sName Dim sValue ' Local Variables iRetVal = Success ' Build the Orchestrator server URL If Left(LCase(oEnvironment.Item("OrchestratorServer")), 7) = "http://" or Left(LCase(oEnvironment.Item("OrchestratorServer")), 8) = "https://" then If Right(LCase(oEnvironment.Item("OrchestratorServer")), 4) = ".svc" then sUrl = oEnvironment.Item("OrchestratorServer") & "/Jobs" Else sUrl = oEnvironment.Item("OrchestratorServer") & "/Orchestrator2012/Orchestrator.svc/Jobs" End if ElseIf Instr(oEnvironment.Item("OrchestratorServer"),":") > 0 then sURL = "http://" & oEnvironment.Item("OrchestratorServer") & "/Orchestrator2012/Orchestrator.svc/Jobs" Else sURL = "http://" & oEnvironment.Item("OrchestratorServer") & ":81/Orchestrator2012/Orchestrator.svc/Jobs" End if oLogging.CreateEntry "Orchestrator server URL = " & sURL, LogTypeInfo oLogging.CreateEntry "Runbook name = " & oEnvironment.Item("RunbookName"), LogTypeInfo oLogging.CreateEntry "Runnbook ID = " & oEnvironment.Item("RunbookID"), LogTypeInfo sMode = UCase(oEnvironment.Item("RunbookParameterMode")) oLogging.CreateEntry "Runbook parameter mode = " & sMode, LogTypeInfo ' Build the parameters If oEnvironment.Item("RunbookParameters0ParameterID") <> "" then sParameters = "<Data>" ' Loop through all possible parameters and add them to the request For i = 0 to 999 ' If the ID can't be found, we're out of parameters, so exit the loop sID = oEnvironment.Item("RunbookParameters" & CStr(i) & "ParameterID") If sID = "" then Exit For End if ' Get the name and value sVar = oEnvironment.Item("RunbookParameters" & CStr(i) & "ParameterName") If sMode = "AUTO" then sVal = oEnvironment.Item(sVar) Else sVal = oEnvironment.Item("RunbookParameters" & CStr(i) & "ParameterValue") End if ' Add it to the request sParameters = sParameters & "<Parameter><Name>" & sVar & "</Name><ID>{" & sID & "}</ID><Value>" & oEnvironment.Substitute(sVal) & "</Value></Parameter>" oLogging.CreateEntry "Added parameter " & sVar & " (" & sID & ")", LogTypeInfo Next sParameters = sParameters & "</Data>" End if ' Build the job request sRequest = "<?xml version=""1.0"" encoding=""utf-8"" standalone=""yes""?>" & _ "<entry xmlns:d=""http://schemas.microsoft.com/ado/2007/08/dataservices"" xmlns:m=""http://schemas.microsoft.com/ado/2007/08/dataservices/metadata"" xmlns=""http://www.w3.org/2005/Atom"">" & _ "<content type=""application/xml"">" & _ "<m:properties>" & _ " <d:RunbookId m:type=""Edm.Guid"">" & oEnvironment.Item("RunbookID") & "</d:RunbookId>" & _ " <d:Parameters>" & sParameters & "</d:Parameters>" & _ "</m:properties>" & _ "</content>" & _ "</entry>" oLogging.CreateEntry sRequest, LogTypeVerbose ' Invoke the job Set oResult = Invoke(sURL, "POST", sRequest) If oResult is Nothing then oLogging.ReportFailure "Unable to create Orchestrator job for the specified runbook.", 10801 End if ' Get the job ID oLogging.CreateEntry oResult.XML, LogTypeVerbose Set oJob = oResult.SelectSingleNode("//atom:entry/atom:id") If oJob is Nothing then oLogging.ReportFailure "Unable to find job.", 10802 End if sJob = oJob.Text oLogging.CreateEntry "Orchestrator job created: " & sJob, LogTypeInfo ' Are we waiting for the runbook to complete? If not, exit If UCase(oEnvironment.Item("WaitUntilComplete")) <> "TRUE" then oLogging.CreateEntry "No need to wait for job to complete.", LogTypeInfo Main = Success Exit Function End if ' Wait for the runbook i = 1 bFinish = False Do While not bFinish Set oJob = Invoke(sJob, "GET", "") If oJob is Nothing then oLogging.ReportFailure "Unable to get Orchestrator job status.", 10803 End if Set oStatus = oJob.SelectSingleNode("//atom:entry/atom:content/m:properties/d:Status") If not (oStatus is Nothing) then If UCase(oStatus.Text) <> "PENDING" and UCase(oStatus.Text) <> "RUNNING" then bFinish = True Else WScript.Sleep i * 1000 If i <= 32 then i = i * 2 End if oLogging.CreateEntry "Job status: " & oStatus.Text, LogTypeInfo End if End if Loop oLogging.CreateEntry "Final job status: " & oStatus.Text, LogTypeInfo ' Get the instance ID and retry if no result as instances sometimes is a bit delayed.. Dim iTryIteration, bReTry iTryIteration = 0 bRetry = False Do Set oInstances = Invoke(sJob & "/Instances?$orderby=CompletionTime desc", "GET", "") Set oInstance = oInstances.SelectSingleNode("//atom:entry/atom:id") If oInstance is Nothing Then If iTryIteration = 10 Then oLogging.ReportFailure "Unable to get Orchestrator job runbook instance id.", 10804 Else oLogging.CreateEntry "Unable to get Orchestrator job runbook instance. retrying", LogTypeInfo End If bRetry = True iTryIteration = iTryIteration + 1 WScript.Sleep(2000) 'Wait 2 secs before retrying End If Loop While (bRetry Or iTryIteration = 10) iTryIteration = 0 bRetry = False Do Dim oInstanceStatus Set oInstanceStatus = oInstances.SelectSingleNode("//atom:entry/atom:content/m:properties/d:Status") If oInstance is Nothing Then If iTryIteration = 10 Then oLogging.ReportFailure "Unable to get Orchestrator job runbook instance status.", 10804 Else oLogging.CreateEntry "Unable to get the selected Orchestrator job runbook instance status. retrying", LogTypeInfo End If bRetry = True iTryIteration = iTryIteration + 1 WScript.Sleep(2000) 'Wait 2 secs before retrying End If Loop While (bRetry Or iTryIteration = 10) iTryIteration = 0 bRetry = False Do ' Retrieve and process the parameters oLogging.CreateEntry "Processing any out parameters.", LogTypeInfo Set oParameters = Invoke(oInstance.Text & "/Parameters", "GET", "") If oParameters is Nothing Then If iTryIteration = 10 Then oLogging.ReportFailure "Unable to get Orchestrator job runbook instance parameters.", 10805 Else oLogging.CreateEntry "Unable to get Orchestrator job runbook instance parameters. retrying", LogTypeInfo End If bRetry = True iTryIteration = iTryIteration + 1 WScript.Sleep(2000) 'Wait 2 secs before retrying End If Loop While (bRetry Or iTryIteration = 10) For each oParameter in oParameters.SelectNodes("//atom:entry") If UCase(oParameter.selectSingleNode("atom:content/m:properties/d:Direction").Text) = "OUT" then oLogging.CreateEntry "Processing output parameter " & oParameter.selectSingleNode("atom:content/m:properties/d:Name").Text, LogTypeInfo sName = Replace(oParameter.selectSingleNode("atom:content/m:properties/d:Name").Text, " ", "") sValue = oParameter.selectSingleNode("atom:content/m:properties/d:Value").Text oEnvironment.Item(sName) = sValue End if Next ' Force a failure if the runbook did not complete successfully If UCase(oStatus.Text) <> "COMPLETED" then oLogging.ReportFailure "Runbook did not complete successfully, final status = " & oStatus.Text, 10806 End if ' Force a failure if the runbook instance ends with warning or failed. If UCase(oInstanceStatus.Text) <> "SUCCESS" then oLogging.ReportFailure "Runbook instance did not complete successfully, final status = " & oInstanceStatus.Text, 10807 End if oLogging.CreateEntry "Runbook executed successfully.", LogTypeInfo ' Done Main = iRetVal End Function Function Invoke(sURL, sMethod, sEnvelope) Dim oHTTP Dim sReturn Dim oReturn Dim oNode Dim sUserID, sPassword Dim iRetryIteration Dim iMaxIteration iMaxIteration = 30 For iRetryIteration = 1 to iMaxIteration Set oHTTP = CreateObject("MSXML2.ServerXMLHTTP") Set oReturn = oUtility.GetMSXMLDOMDocument oReturn.setProperty "SelectionNamespaces", "xmlns:atom='http://www.w3.org/2005/Atom' xmlns:d='http://schemas.microsoft.com/ado/2007/08/dataservices' xmlns:m='http://schemas.microsoft.com/ado/2007/08/dataservices/metadata'" Set Invoke = oReturn ' Set timeouts to infinite for name resolution, 60 seconds for connect, send, and receive oHTTP.setTimeouts 0, 60000, 60000, 60000 ' Ignore SSL errors (avoids having to deal with certificates) oHTTP.SetOption 2, 13056 ' Issue the web service call Dim bNAACred Dim iTryIteration iTryIteration = 0 ' number of creds to get bNAACred = oUtility.GetNextNAACred(0) If Not bNAACred Then oLogging.CreateEntry "No NAA credentials specified. Using default.", LogTypeVerbose Else oLogging.CreateEntry "NAA credentials have been specified.", LogTypeVerbose End if Do While ((iTryIteration = 0 Or bNAACred)) sUserID = oEnvironment.Item("UserDomain") & "\" & oEnvironment.Item("UserID") sPassword = oEnvironment.Item("UserPassword") oLogging.CreateEntry "About to execute web service call using method " & sMethod & " to " & sURL & ": " & sEnvelope, LogTypeVerbose oLogging.CreateEntry " --Attempt #" & CStr(iTryIteration + 1), LogTypeVerbose oHTTP.open sMethod, sURL, False, sUserID, sPassword oHTTP.setRequestHeader "Content-Type", "application/atom+xml" On Error Resume Next oHTTP.send sEnvelope If Err then oLogging.CreateEntry "Error executing web service " & sURL & ": " & Err.Description & " (" & Err.Number & ")", LogTypeError if iRetryIteration <= iMaxIteration Then Exit Do Else 'Exit if number of retries is Set Invoke = Nothing Exit Function End If End If On Error Goto 0 If oHTTP.status = 200 or oHTTP.status = 201 then oLogging.CreateEntry "Response from web service: " & oHTTP.status & " " & oHTTP.StatusText, LogTypeVerbose ' Process the results oReturn.loadXML oHTTP.responseText oLogging.CreateEntry "Successfully executed the web service.", LogTypeVerbose Exit Function ElseIf bNAACred Then iTryIteration = iTryIteration + 1 oLogging.CreateEntry "Unexpected response from web service: " & oHTTP.status & " " & oHTTP.StatusText & vbCrLf & oHTTP.responseText, LogTypeError 'oLogging.CreateEntry "Web service returned unauthorized: " & oHTTP.status & " " & oHTTP.StatusText & vbCrLf & oHTTP.responseText, LogTypeWarning bNAACred = oUtility.GetNextNAACred(iTryIteration) If bNAACred Then ' We will try another account Else ' All accounts have been tried and been denied oLogging.CreateEntry "All network access accounts failed to be authorized/connect.", LogTypeError if iRetryIteration <= iMaxIteration Then Exit Do Else 'Exit if number of retries is Set Invoke = Nothing Exit Function End If End If Else oLogging.CreateEntry "Unexpected response from web service: " & oHTTP.status & " " & oHTTP.StatusText & vbCrLf & oHTTP.responseText, LogTypeError if iRetryIteration <= iMaxIteration Then Exit Do Else 'Exit if number of retries is Set Invoke = Nothing Exit Function End If End If Loop oLogging.CreateEntry "Wait 10 secs before retry - Attempt: " & iRetryIteration, LogTypeWarning WScript.Sleep(10000) 'Wait 10 secs before retrying Set Invoke = Nothing Next End Function End Class </script> </job>
[…] Source: CTGlobal […]