I'm a big fan of the Run Script feature in ConfigMgr, since it allows me to run a PowerShell script on any ConfigMgr client without having to worry about remote PowerShell being enabled, or having any special permissions on the target machine itself.
Here is an example on how to invoke a run script via PowerShell on a single machine, and collect the result. It is not very pretty, but it works 🙂 For multiple results, like when targeting a collection of devices, you're better off returning the Run Script result as JSON and then read it. But that's for another day.
In this example the Run Script used retrieved the .NET Framework version on the client, and If you have any ideas for improvements, please let me know in the comments below. Also, you can find the "Get .NET Framework Version" Run Script used here: https://github.com/DeploymentResearch/DRFiles/blob/master/Scripts/GetNetFrameworkVersion.ps1

The InvokeRunScriptOnDevice.ps1 script
# Run a script on device via Run Script, and collect the result
# Parameters
$ScriptName = "Get .NET Framework Version"
$ComputerName = "CHI-W10PEER-008"
$ProviderMachineName = "cm01.corp.viamonstra.com"
$SiteCode = "PS1"
# Import the ConfigurationManager.psd1 module
if((Get-Module ConfigurationManager) -eq $null) {
Import-Module "$($ENV:SMS_ADMIN_UI_PATH)\..\ConfigurationManager.psd1"
}
# Connect to the site's drive if it is not already present
if((Get-PSDrive -Name $SiteCode -PSProvider CMSite -ErrorAction SilentlyContinue) -eq $null) {
New-PSDrive -Name $SiteCode -PSProvider CMSite -Root $ProviderMachineName
}
# Set the current location to be the site code.
Set-Location "$($SiteCode):\"
# Main code
$ScriptGuid = (Get-CMScript -ScriptName $ScriptName -Fast).ScriptGuid
If (!($ScriptGuid)){
Write-Warning "Could not find a Run Script named $ScriptName, aborting..."
Break
}
$CMDevice = Get-CMDevice -Name $ComputerName
$OperationID = (Invoke-CMScript -ScriptGuid $ScriptGuid -Device $CMDevice -PassThru).OperationID
# Get script output, wait a maximum of five minuters
$NumberOfLoops = 30
$LoopInterval = 10 #Seconds
$i = 1
do {
$Operation = Get-CimInstance -Namespace root\SMS\Site_$SiteCode -ClassName SMS_ScriptsExecutionStatus -Filter "ClientOperationID = '$OperationID'"
If ($Operation){
# Got the status, time to exit loop
Break
}
Start-Sleep -Seconds $LoopInterval
$i++
}
while ($i -le $NumberOfLoops)
# Script output
$Operation.ScriptOutput
What exactly does this part do in relation to this script? I know what it actually does, but it seems to be a remnant from removed code or some hold over from another project
[System.Collections.ArrayList]$VMList = @()
Oops, yes, good call, that line is not needed. I have updated the post. Thank you.
/ Johan
Please, I must use this Script by list server in c:\temp\servers.txt, help me
Hi Alex,
You can certainly import that text file, and put a loop around lines 29-50 (foreach loop)
Where is the script executed from? Is it from the ccmcache folder? I ask because we have application white-listing that usually loves blocking useful things like this.
The scripts from the Run Scripts feature as well as CMPivot runs from C:\Windows\CCM\ScriptStore.
/ Johan
Thanks Johan for the post.
While getting operation status I am getting error:
$Operation = Get-CimInstance -Namespace root\SMS\Site_$SiteCode -ClassName SMS_ScriptsExecutionStatus -Filter "ClientOperationID = '$OperationID'"
Error:
Get-CimInstance : Invalid namespace
$Sitecode is correct because it works fine at below step.
# Connect to the site's drive if it is not already present
if((Get-PSDrive -Name $SiteCode -PSProvider CMSite -ErrorAction SilentlyContinue) -eq $null) {
New-PSDrive -Name $SiteCode -PSProvider CMSite -Root $ProviderMachineName
}
A bit hard to tell without seeing your script. Can you upload it to GitHub and share the link?
/ Johan
I get the same error when running from a management-server with the CM-console installed but it does work when run on the siteserver. How can I make it work on another server?
I would guess because line 38 does not specify the provider server (add -ComputerName $ProviderMachineName)