Chapters/cross-platform-scripting/Stop-RemoteProcess.ps1
#requires -version 5.1 Function Stop-RemoteProcess { [cmdletbinding(DefaultParameterSetName = "computer")] Param( [Parameter( ParameterSetName = "computer", Mandatory, Position = 0, ValueFromPipeline, ValueFromPipelineByPropertyName, HelpMessage = "Enter the name of a computer to query." )] [ValidateNotNullOrEmpty()] [Alias("cn")] [string[]]$ComputerName, [Parameter( ParameterSetName = "computer", HelpMessage = "Enter a credential object or username." )] [Alias("RunAs")] [PSCredential]$Credential, [Parameter(ParameterSetName = "computer")] [switch]$UseSSL, [Parameter( ParameterSetName = "session", ValueFromPipeline )] [ValidateNotNullOrEmpty()] [System.Management.Automation.Runspaces.PSSession[]]$Session, [ValidateScript( {$_ -ge 0})] [int32]$ThrottleLimit = 32, [Parameter(Mandatory,HelpMessage = "Specify the process to stop.")] [ValidateNotNullOrEmpty()] [string]$ProcessName, [Parameter(HelpMessage = "Write the stopped process to the pipeline")] [switch]$Passthru, [Parameter(HelpMessage = "Run the remote command with -WhatIf")] [switch]$WhatIfRemote ) DynamicParam { #Add an SSH dynamic parameter if in PowerShell 7 if ($isCoreCLR) { $paramDictionary = New-Object -Type System.Management.Automation.RuntimeDefinedParameterDictionary #a CSV file with dynamic parameters to create #this approach doesn't take any type of parameter validation into account $data = @" Name,Type,Mandatory,Default,Help HostName,string[],1,,"Enter the remote host name." UserName,string,0,,"Enter the remote user name." Subsystem,string,0,"powershell","The name of the ssh subsystem. The default is powershell." Port,int32,0,,"Enter an alternate SSH port" KeyFilePath,string,0,,"Specify a key file path used by SSH to authenticate the user" SSHTransport,switch,0,,"Use SSH to connect." "@ $data | ConvertFrom-Csv | ForEach-Object -begin { } -process { $attributes = New-Object System.Management.Automation.ParameterAttribute $attributes.Mandatory = ([int]$_.mandatory) -as [bool] $attributes.HelpMessage = $_.Help $attributes.ParameterSetName = "SSH" $attributeCollection = New-Object -Type System.Collections.ObjectModel.Collection[System.Attribute] $attributeCollection.Add($attributes) $dynParam = New-Object -Type System.Management.Automation.RuntimeDefinedParameter($_.name, $($_.type -as [type]), $attributeCollection) $dynParam.Value = $_.Default $paramDictionary.Add($_.name, $dynParam) } -end { return $paramDictionary } } } #dynamic param Begin { $start = Get-Date #the first verbose message uses a pseudo timespan to reflect the idea we're just starting Write-Verbose "[00:00:00.0000000 BEGIN ] Starting $($myinvocation.mycommand)" #a script block to be run remotely Write-Verbose "[$(New-TimeSpan -start $start) BEGIN ] Defining the scriptblock to be run remotely" $sb = { param([string]$ProcessName,[bool]$Passthru,[string]$VerbPref = "SilentlyContinue", [bool]$WhatPref) $VerbosePreference = $VerbPref $WhatIfPreference = $WhatPref Try { Write-Verbose "[$(New-TimeSpan -start $using:start) REMOTE ] Getting Process $ProcessName on $([System.Environment]::MachineName)" $procs = Get-Process -Name $ProcessName -ErrorAction stop Try { Write-Verbose "[$(New-TimeSpan -start $using:start) REMOTE ] Stopping $($procs.count) Processes on $([System.Environment]::MachineName)" $procs | Stop-Process -ErrorAction Stop -PassThru:$Passthru } Catch { Write-Warning "[$(New-TimeSpan -start $using:start) REMOTE ] Failed to stop Process $ProcessName on $([System.Environment]::MachineName). $($_.Exception.message)." } } Catch { Write-Verbose "[$(New-TimeSpan -start $using:start) REMOTE ] Process $ProcessName not found on $([System.Environment]::MachineName)" } } #scriptblock #parameters to splat to Invoke-Command Write-Verbose "[$(New-TimeSpan -start $start) BEGIN ] Defining parameters for Invoke-Command" #remove my parameters from PSBoundparameters because they can't be used with New-PSSession $myparams = "ProcessName","WhatIfRemote","passthru" foreach ($my in $myparams) { if ($PSBoundParameters.ContainsKey($my)) { [void]($PSBoundParameters.remove($my)) } } $icmParams = @{ Scriptblock = $sb Argumentlist = @($ProcessName,$Passthru,$VerbosePreference,$WhatIfRemote) HideComputerName = $False ThrottleLimit = $ThrottleLimit ErrorAction = "Stop" Session = $null } #initialize an array to hold session objects [System.Management.Automation.Runspaces.PSSession[]]$All = @() If ($Credential.username) { Write-Verbose "[$(New-TimeSpan -start $start) BEGIN ] Using alternate credential for $($credential.username)" } } #begin Process { Write-Verbose "[$(New-TimeSpan -start $start) PROCESS] Detected parameter set $($pscmdlet.ParameterSetName)." $remotes = @() if ($PSCmdlet.ParameterSetName -match "computer|ssh") { if ($pscmdlet.ParameterSetName -eq 'ssh') { $remotes += $PSBoundParameters.HostName $param = "HostName" } else { $remotes += $PSBoundParameters.ComputerName $param = "ComputerName" } foreach ($remote in $remotes) { $PSBoundParameters[$param] = $remote $PSBoundParameters["ErrorAction"] = "Stop" Try { #create a session one at a time to better handle errors Write-Verbose "[$(New-TimeSpan -start $start) PROCESS] Creating a temporary PSSession to $remote" #save each created session to $tmp so it can be removed at the end $all += New-PSSession @PSBoundParameters -OutVariable +tmp } #Try Catch { #TODO: Decide what you want to do when the new session fails Write-Warning "Failed to create session to $remote. $($_.Exception.Message)." #Write-Error $_ } #catch } #foreach remote } Else { #only add open sessions foreach ($sess in $session) { if ($sess.state -eq 'opened') { Write-Verbose "[$(New-TimeSpan -start $start) PROCESS] Using session for $($sess.ComputerName.toUpper())" $all += $sess } #if open } #foreach session } #else sessions } #process End { $icmParams["session"] = $all Try { Write-Verbose "[$(New-TimeSpan -start $start) END ] Querying $($all.count) computers" Invoke-Command @icmParams | ForEach-Object { #TODO: PROCESS RESULTS FROM EACH REMOTE CONNECTION IF NECESSARY $_ } #foreach result } #try Catch { Write-Error $_ } #catch if ($tmp) { Write-Verbose "[$(New-TimeSpan -start $start) END ] Removing $($tmp.count) temporary PSSessions" $tmp | Remove-PSSession } Write-Verbose "[$(New-TimeSpan -start $start) END ] Ending $($myinvocation.mycommand)" } #end } #close function |