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 […]