Public/Invoke-CCMClientAction.ps1
function Invoke-CCMClientAction { <# .SYNOPSIS Invokes CM Client actions on local or remote machines .DESCRIPTION This script will allow you to invoke a set of CM Client actions on a machine (with optional credentials), providing a list of the actions and an optional delay betweens actions. The function will attempt for a default of 5 minutes to invoke the action, with a 10 second delay inbetween attempts. This is to account for invoke-cimmethod failures. .PARAMETER Schedule Define the schedules to run on the machine - 'HardwareInv', 'FullHardwareInv', 'SoftwareInv', 'UpdateScan', 'UpdateEval', 'MachinePol', 'AppEval', 'DDR', 'SourceUpdateMessage', 'SendUnsentStateMessage' .PARAMETER Delay Specify the delay in seconds between each schedule when more than one is ran - 0-30 seconds .PARAMETER Timeout Specifies the timeout in minutes after which any individual computer will stop attempting the schedules. Default is 5 minutes. .PARAMETER CimSession Provides CimSessions to invoke actions on .PARAMETER ComputerName Provides computer names to invoke actions on .EXAMPLE C:\PS> Invoke-CCMClientAction -Schedule MachinePol,HardwareInv Start a machine policy eval and a hardware inventory cycle .NOTES FileName: Invoke-CCMClientAction.ps1 Author: Cody Mathis Contact: @CodyMathis123 Created: 2018-11-20 Updated: 2020-01-11 #> [CmdletBinding(SupportsShouldProcess, DefaultParameterSetName = 'ComputerName')] param ( [parameter(Mandatory = $true)] [ValidateSet('HardwareInv', 'FullHardwareInv', 'SoftwareInv', 'UpdateScan', 'UpdateEval', 'MachinePol', 'AppEval', 'DDR', 'SourceUpdateMessage', 'SendUnsentStateMessage')] [ValidateNotNullOrEmpty()] [string[]]$Schedule, [parameter(Mandatory = $false)] [ValidateRange(0, 30)] [ValidateNotNullOrEmpty()] [int]$Delay = 0, [parameter(Mandatory = $false)] [ValidateNotNullOrEmpty()] [int]$Timeout = 5, [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'CimSession')] [Microsoft.Management.Infrastructure.CimSession[]]$CimSession, [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'ComputerName')] [Alias('Connection', 'PSComputerName', 'PSConnectionName', 'IPAddress', 'ServerName', 'HostName', 'DNSHostName')] [string[]]$ComputerName = $env:ComputerName ) begin { $TimeSpan = New-TimeSpan -Minutes $Timeout $connectionSplat = @{ } $invokeClientActionSplat = @{ } $getFullHINVSplat = @{ Namespace = 'root\ccm\invagt' ClassName = 'InventoryActionStatus' ErrorAction = 'Stop' } $invokeCIMPowerShellSplat = @{ FunctionsToLoad = 'Invoke-CCMClientAction', 'Invoke-CCMTriggerSchedule' } } process { foreach ($Connection in (Get-Variable -Name $PSCmdlet.ParameterSetName -ValueOnly)) { $Computer = switch ($PSCmdlet.ParameterSetName) { 'ComputerName' { Write-Output -InputObject $Connection switch ($Connection -eq $env:ComputerName) { $false { if ($ExistingCimSession = Get-CimSession -ComputerName $Connection -ErrorAction Ignore) { Write-Verbose "Active CimSession found for $Connection - Passing CimSession to CIM cmdlets" $connectionSplat.Remove('ComputerName') $connectionSplat['CimSession'] = $ExistingCimSession } else { Write-Verbose "No active CimSession found for $Connection - falling back to -ComputerName parameter for CIM cmdlets" $connectionSplat.Remove('CimSession') $connectionSplat['ComputerName'] = $Connection } } $true { $connectionSplat.Remove('CimSession') $connectionSplat.Remove('ComputerName') Write-Verbose 'Local computer is being queried - skipping computername, and cimsession parameter' } } } 'CimSession' { Write-Verbose "Active CimSession found for $Connection - Passing CimSession to CIM cmdlets" Write-Output -InputObject $Connection.ComputerName $connectionSplat.Remove('ComputerName') $connectionSplat['CimSession'] = $Connection } } $Result = [System.Collections.Specialized.OrderedDictionary]::new() $Result['ComputerName'] = $Computer foreach ($Option in $Schedule) { if ($PSCmdlet.ShouldProcess("[ComputerName = '$Computer'] [Schedule = '$Option']", "Invoke Schedule")) { $Result['Action'] = $Option $Action = switch -Regex ($Option) { '^HardwareInv$|^FullHardwareInv$' { '{00000000-0000-0000-0000-000000000001}' } 'SoftwareInv' { '{00000000-0000-0000-0000-000000000002}' } 'UpdateScan' { '{00000000-0000-0000-0000-000000000113}' } 'UpdateEval' { '{00000000-0000-0000-0000-000000000108}' } 'MachinePol' { '{00000000-0000-0000-0000-000000000021}' } 'AppEval' { '{00000000-0000-0000-0000-000000000121}' } 'DDR' { '{00000000-0000-0000-0000-000000000003}' } 'SourceUpdateMessage' { '{00000000-0000-0000-0000-000000000032}' } 'SendUnsentStateMessage' { '{00000000-0000-0000-0000-000000000111}' } } $StopWatch = [System.Diagnostics.Stopwatch]::StartNew() do { try { Remove-Variable MustExit -ErrorAction SilentlyContinue Remove-Variable Invocation -ErrorAction SilentlyContinue if ($Option -eq 'FullHardwareInv') { $getFullHINVSplat['Filter'] = "InventoryActionID ='$Action'" Write-Verbose "Attempting to delete Hardware Inventory history for $Computer as a FullHardwareInv was requested" $HWInv = Get-CimInstance @getFullHINVSplat @connectionSplat if ($null -ne $HWInv) { Remove-CimInstance -InputObject $HWInv Write-Verbose "Hardware Inventory history deleted for $Computer" } else { Write-Verbose "No Hardware Inventory history to delete for $Computer" } } $invokeClientActionSplat['ScheduleID'] = $Action Write-Verbose "Triggering a $Option Cycle on $Computer via the 'TriggerSchedule' CIM method" $Invocation = switch ($Computer -eq $env:ComputerName) { $true { Invoke-CCMTriggerSchedule @invokeClientActionSplat } $false { $ScriptBlock = [string]::Format('Invoke-CCMClientAction -Schedule {0} -Delay {1} -Timeout {2}', $Option, $Delay, $Timeout) $invokeCIMPowerShellSplat['ScriptBlock'] = [scriptblock]::Create($ScriptBlock) Invoke-CIMPowerShell @invokeCIMPowerShellSplat @connectionSplat } } } catch [System.UnauthorizedAccessException] { Write-Error -Message "Access denied to $Computer" -Category AuthenticationError -Exception $_.Exception $MustExit = $true } catch { Write-Warning "Failed to invoke the $Option cycle via CIM. Will retry every 10 seconds until [StopWatch $($StopWatch.Elapsed) -ge $Timeout minutes] Error: $($_.Exception.Message)" Start-Sleep -Seconds 10 } } until ($Invocation -or $StopWatch.Elapsed -ge $TimeSpan -or $MustExit) if ($Invocation) { Write-Verbose "Successfully invoked the $Option Cycle on $Computer via the 'TriggerSchedule' CIM method" $Result['Invoked'] = $true Start-Sleep -Seconds $Delay } elseif ($StopWatch.Elapsed -ge $TimeSpan) { Write-Error "Failed to invoke $Option cycle via CIM after $Timeout minutes of retrying." $Result['Invoked'] = $false } $StopWatch.Reset() [pscustomobject]$Result } } } } end { Write-Verbose "Following actions invoked - $Schedule" } } |