-
-
Save ducke/9f64a82b0311a0d8ce61f5a7fc4463ba to your computer and use it in GitHub Desktop.
Simple script to use the new sccm feature called "Run Script". The builtin Invoke-CMScript does not accept parameters.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function Invoke-SCCMRunScript { | |
param( | |
[Parameter(Mandatory=$true)] | |
[ValidateNotNullOrEmpty()] | |
[string]$SiteServer, | |
[Parameter(Mandatory=$true)] | |
[ValidateNotNullOrEmpty()] | |
[string]$Namespace, | |
[Parameter(Mandatory=$true)] | |
[ValidateNotNullOrEmpty()] | |
[string]$ScriptName, | |
[Array]$InputParameters = @(), | |
[string]$TargetCollectionID = "", | |
[Array]$TargetResourceIDs = @() | |
) | |
# if something goes wrong, we want to stop! | |
$ErrorActionPreference = "Stop" | |
# We can not run on all members of a collection AND selected resources | |
if (-not ([string]::IsNullOrEmpty($TargetCollectionID)) -and $TargetResourceIDs -gt 0) { | |
throw "Use either TargetCollectionID or TargetResourceIDs, not both!" | |
} | |
if (([string]::IsNullOrEmpty($TargetCollectionID)) -and $TargetResourceIDs -lt 1) { | |
throw "We need some resources (devices) to run the script!" | |
} | |
# Get the script | |
$Script = [wmi](Get-WmiObject -class SMS_Scripts -Namespace $Namespace -ComputerName $SiteServer -Filter "ScriptName = '$ScriptName'").__PATH | |
if (-not $Script) { | |
throw "Could not find script with name '$ScriptName'" | |
} | |
# Parse the parameter definition | |
$Parameters = [xml]([string]::new([Convert]::FromBase64String($Script.ParamsDefinition))) | |
$Parameters.ScriptParameters.ChildNodes | % { | |
# In the case of a missing required parameter, bail! | |
if ($_.IsRequired -and $InputParameters.Count -lt 1) { | |
throw "Script 'ScriptName' has required parameters but no parameters was passed." | |
} | |
if ($_.Name -notin $InputParameters.Name) { | |
throw "Parameter '$($_.Name)' has not been passed in InputParamters!" | |
} | |
} | |
# GUID used for parametergroup | |
$ParameterGroupGUID = $(New-Guid) | |
if ($InputParameters.Count -le 0) { | |
# If no ScriptParameters: <ScriptParameters></ScriptParameters> and an empty hash | |
$ParametersXML = "<ScriptParameters></ScriptParameters>" | |
$ParametersHash = "" | |
} | |
else { | |
foreach ($Parameter in $InputParameters) { | |
$InnerParametersXML = "$InnerParametersXML<ScriptParameter ParameterGroupGuid=`"$ParameterGroupGUID`" ParameterGroupName=`"PG_$ParameterGroupGUID`" ParameterName=`"$($Parameter.Name)`" ParameterType=`"$($Parameter.Type)`" ParameterValue=`"$($Parameter.Value)`"/>" | |
} | |
$ParametersXML = "<ScriptParameters>$InnerParametersXML</ScriptParameters>" | |
$SHA256 = [System.Security.Cryptography.SHA256Cng]::new() | |
$Bytes = ($SHA256.ComputeHash(([System.Text.Encoding]::Unicode).GetBytes($ParametersXML))) | |
$ParametersHash = ($Bytes | ForEach-Object ToString X2) -join '' | |
} | |
$RunScriptXMLDefinition = "<ScriptContent ScriptGuid='{0}'><ScriptVersion>{1}</ScriptVersion><ScriptType>{2}</ScriptType><ScriptHash ScriptHashAlg='SHA256'>{3}</ScriptHash>{4}<ParameterGroupHash ParameterHashAlg='SHA256'>{5}</ParameterGroupHash></ScriptContent>" | |
$RunScriptXML = $RunScriptXMLDefinition -f $Script.ScriptGuid,$Script.ScriptVersion,$Script.ScriptType,$Script.ScriptHash,$ParametersXML,$ParametersHash | |
# Get information about the class instead of fetching an instance | |
# WMI holds the secret of what parameters that needs to be passed and the actual order in which they have to be passed | |
$MC = [WmiClass]"\\$SiteServer\$($Namespace):SMS_ClientOperation" | |
# Get the parameters of the WmiMethod | |
$MethodName = 'InitiateClientOperationEx' | |
$InParams = $MC.psbase.GetMethodParameters($MethodName) | |
# Information about the script is passed as the parameter 'Param' as a BASE64 encoded string | |
$InParams.Param = ([Convert]::ToBase64String(([System.Text.Encoding]::UTF8).GetBytes($RunScriptXML))) | |
# Hardcoded to 0 in certain DLLs | |
$InParams.RandomizationWindow = "0" | |
# If we are using a collection, set it. TargetCollectionID can be empty string: "" | |
$InParams.TargetCollectionID = $TargetCollectionID | |
# If we have a list of resources to run the script on, set it. TargetResourceIDs can be an empty array: @() | |
# Criteria for a "valid" resource is IsClient=$true and IsBlocked=$false and IsObsolete=$false and ClientType=1 | |
$InParams.TargetResourceIDs = $TargetResourceIDs | |
# Run Script is type 135 | |
$InParams.Type = "135" | |
# Everything should be ready for processing, invoke the method! | |
$R = $MC.InvokeMethod($MethodName, $InParams, $null) | |
# The result contains the client operation id of the execution | |
$R | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment