We help IT Professionals succeed at work.

Powershell Script - Increment Counter?

Hi Experts,

Looking for some expertise with this script.

Here is what the script does:

1) Reads from a text file with computer names and remotely runs a SCCM advertisement.

2) The advertisement opens some processes - which I have in a loop in this script.  

3) When the processes read 0 the loop is completed, then moves to the next command to RESTART the computer.

4) Is there any way to have this run 3 times, and then shutdown on the 3rd time?

$OutArray = @()
workflow foreachrerun {
    param([string[]]$computers)
    foreach –parallel ($computer in $computers) {
       InlineScript {
Function Start-CCMRerunAdvertisement {
    [CmdLetBinding()]Param(
        [Parameter(Mandatory=$true)][string]$computerName,
        [Parameter(Mandatory=$false)][string]$advertisementId = "*",
        [Parameter(Mandatory=$false)][string]$packageId = "*",
        [Parameter(Mandatory=$false)][int]$maxRun = 1
        #[Parameters(Mandatory=$false)][switch]$moreThanPing = $false
    )
    if($advertisementId -eq "*" -and $packageId -eq "*") {
        Write-Error "You must supply either an AdvertisementID or a PackageID"
        return "Missing Parameters"
        break
    }
    $searchString = "$advertisementId-$packageId-*" 
    if(!(Test-Connection -ComputerName $computername -ErrorAction SilentlyContinue)) {
        if($moreThanPing) { 
            if(!(Get-ChildItem "\\$computername\c$" -ErrorAction SilentlyContinue)) {
                Write-Error "System Offline"
                Return "System Offline"
                break
            }
        } else {
            Return "$Computername is Offline"
            break
        }
    }
    Write-Verbose "Getting ID of ScheduleMessage on $computername"
    $schMsgs = Get-WmiObject -ComputerName $computername -Namespace "root\ccm\policy\machine\actualconfig" -Class CCM_Scheduler_ScheduledMessage
    $thisMsg = $schMsgs | ? { $_.ScheduledMessageID -like $searchString } | Sort ActiveTime -Descending | select -First $maxRun
    if(!$thisMsg) {
        Write-Verbose "Cannot Find Advertisement/Package on Target Computer"
        Return "Cannot Find Advertisment"
        break
    }
    $thisMsg | % {
        [xml]$activeMessage = $_.activeMessage
        $amProgramId = $activeMessage.SoftwareDeploymentMessage.ProgramID
        $amAdvId = $activeMessage.SoftwareDeploymentMessage.AdvertisementID
        $amPkgId = $activeMessage.SoftwareDeploymentMessage.PackageID
        $ScheduledMessageId = $_.ScheduledMessageId
        Write-Verbose  "Restarting $amArogramId (ADV=$amAdvId) (PKG=$amPkgId) for Schedule Message $ScheduledMessageId"
        $softwareDist = Get-WmiObject -ComputerName $computername -Namespace "root\ccm\policy\machine\actualconfig" -Class CCM_SoftwareDistribution -Filter "ADV_AdvertisementID = '$amAdvId' and PKG_PackageID = '$amPkgId'"
        $original_Rerun = $softwareDist.ADV_RepeatRunBehavior
        if($original_Rerun -ne "RerunAlways") {
            write-verbose "Changing Rerun Status from $original_Rerun to RerunAlways"
            $softwareDist.ADV_RepeatRunBehavior = "RerunAlways"
            $softwareDist.put() | Out-Null
        }
        Write-Verbose "Triggering Schedule on $computername"
        Invoke-WmiMethod -ComputerName $computername -Namespace "root\ccm" -Class "SMS_CLIENT" -Name TriggerSchedule $ScheduledMessageId | Out-Null
        Write-Verbose "Sleeping for 5 seconds"
        Start-Sleep -Seconds 5
        if($original_Rerun -ne "RerunAlways") {
            Write-Verbose "Changing Rerun Status back to $original_Rerun"
            $softwareDist.ADV_RepeatRunBehavior = "$original_Rerun"
            $softwareDist.put() | Out-Null
        }
        Return "Advert Reran on succesfully on $computername"
    }
}
function Send-EmailOffline {
    Send-MailMessage -From “admin@domain.com" -To "admin1@domain.com" -SMTPServer mail.domain.com -Subject “$($computername) is Offline" -Body "$($ComputerName) is Offline, please power on and re-run script."
}
function Send-EmailKickit {
    Send-MailMessage -From “admin@domain.com" -To "admin1@domain.com" -SMTPServer mail.domain.com -Subject “$($computername) Completed." -Body "$($ComputerName) Completed and is rebooting."
}
function Test-MyConnection {
    param (
        [Parameter(Mandatory=$true)]
        [System.String]
        $ComputerName
    )
    if (Test-Connection $computername -Quiet -Count 2) {
        Write-Output "Ping successful on $($computerName)"      
        Test-Running -ComputerName $ComputerName 
    }
    else {
        Send-EmailOffline -ComputerName $computername   
    }
  }   
function Test-Running {
    param (
        [Parameter(Mandatory=$true)]
        [System.String]$ComputerName
    )
$processList = @'
    "Name",             "Expected",     "Running"
    "cmd",              "1",            "0"
    "TrustedInstaller", "1",            "0"
    "TIWorker",         "1",            "0"
    "OfficeClickToRun", "2",            "0"
'@ | ConvertFrom-Csv | ForEach-Object {$_.Expected = [int]$_.Expected; $_}
    $splat = @{}
    $splat['ComputerName'] = $computerName
    #Write-Output "Monitoring processes on $($computerName)" 
    Do  {
      $processList | ForEach-Object {
          $_.Running = @(Get-Process $_.Name @splat -ErrorAction SilentlyContinue).Count
      }
      ($processList | Format-Table -AutoSize | Out-String).Split("`r`n", [StringSplitOptions]::RemoveEmptyEntries) | Write-Output
      If ($running = @($processList | Where-Object {$_.Running -ge $_.Expected}).Count) {
          Start-Sleep -Seconds 5
      }
      Write-Output "Monitoring processes on $($computerName)" 
    } Until (-not $running)
    Send-EmailKickit -ComputerName $computername 
    $Today = (Get-Date).ToString('MM-dd-yyyy'),
    $ExactTime = Get-Date -Format "MM-dd-yyyy HHmm tt"
    $Logfile = "c:\input\Log.csv"
    $myobj = "" | Select "Computer", "ExactTime"
    $myobj.Computer = $ComputerName
    $myobj.ExactTime = $($(Get-Date).ToString('yyyy-MM-dd::hh:mm:ss'))

    $OutArray += $myobj
    $OutArray | Export-Csv $Logfile -NoTypeInformation -Append 
    Return "Process count finished on $computername"

# force reboot, after all process requirements met
Restart-Computer -Computer $Computername -Force

} 
            Start-CCMRerunAdvertisement –ComputerName $using:Computer -AdvertisementID "TMC73737" 
            Test-MyConnection -ComputerName $using:Computer
        }
    }
}
$ComputerList  = Get-Content "c:\input\computerlist.txt"
foreachrerun -Computers $ComputerList

Open in new window

Comment
Watch Question

Darrell PorterEnterprise Business Process Architect

Commented:
$OutArray = @()
workflow foreachrerun {
    param([string[]]$computers)
    foreach –parallel ($computer in $computers) {
       InlineScript {
        Function Start-CCMRerunAdvertisement {
         [CmdLetBinding()]Param(
           [Parameter(Mandatory=$true)][string]$computerName,
           [Parameter(Mandatory=$false)][string]$advertisementId = "*",
           [Parameter(Mandatory=$false)][string]$packageId = "*",
           [Parameter(Mandatory=$false)][int]$maxRun = 1
         #[Parameters(Mandatory=$false)][switch]$moreThanPing = $false
        )
        if($advertisementId -eq "*" -and $packageId -eq "*") {
            Write-Error "You must supply either an AdvertisementID or a PackageID"
            return "Missing Parameters"
            break
        }
        $searchString = "$advertisementId-$packageId-*" 
        if(!(Test-Connection -ComputerName $computername -ErrorAction SilentlyContinue)) {
            if($moreThanPing) { 
                if(!(Get-ChildItem "\\$computername\c$" -ErrorAction SilentlyContinue)) {
                    Write-Error "System Offline"
                    Return "System Offline"
                    break
                }
            } else {
                Return "$Computername is Offline"
                break
            }
        }
        Write-Verbose "Getting ID of ScheduleMessage on $computername"
        $schMsgs = Get-WmiObject -ComputerName $computername -Namespace "root\ccm\policy\machine\actualconfig" -Class CCM_Scheduler_ScheduledMessage
        $thisMsg = $schMsgs | ? { $_.ScheduledMessageID -like $searchString } | Sort ActiveTime -Descending | select -First $maxRun
        if(!$thisMsg) {
            Write-Verbose "Cannot Find Advertisement/Package on Target Computer"
            Return "Cannot Find Advertisment"
            break
        }
        $thisMsg | % {
            [xml]$activeMessage = $_.activeMessage
            $amProgramId = $activeMessage.SoftwareDeploymentMessage.ProgramID
            $amAdvId = $activeMessage.SoftwareDeploymentMessage.AdvertisementID
            $amPkgId = $activeMessage.SoftwareDeploymentMessage.PackageID
            $ScheduledMessageId = $_.ScheduledMessageId
            Write-Verbose  "Restarting $amArogramId (ADV=$amAdvId) (PKG=$amPkgId) for Schedule Message $ScheduledMessageId"
            $softwareDist = Get-WmiObject -ComputerName $computername -Namespace "root\ccm\policy\machine\actualconfig" -Class CCM_SoftwareDistribution -Filter "ADV_AdvertisementID = '$amAdvId' and PKG_PackageID = '$amPkgId'"
            $original_Rerun = $softwareDist.ADV_RepeatRunBehavior
            if($original_Rerun -ne "RerunAlways") {
                write-verbose "Changing Rerun Status from $original_Rerun to RerunAlways"
                $softwareDist.ADV_RepeatRunBehavior = "RerunAlways"
                $softwareDist.put() | Out-Null
            }
            Write-Verbose "Triggering Schedule on $computername"
            Invoke-WmiMethod -ComputerName $computername -Namespace "root\ccm" -Class "SMS_CLIENT" -Name TriggerSchedule $ScheduledMessageId | Out-Null
            Write-Verbose "Sleeping for 5 seconds"
            Start-Sleep -Seconds 5
            if($original_Rerun -ne "RerunAlways") {
                Write-Verbose "Changing Rerun Status back to $original_Rerun"
                $softwareDist.ADV_RepeatRunBehavior = "$original_Rerun"
                $softwareDist.put() | Out-Null
            }
            Return "Advert Reran on succesfully on $computername"
        }
    } # End Function Start-CCMRerunAdvertisement
    function Send-EmailOffline {
        Send-MailMessage -From “admin@domain.com" -To "admin1@domain.com" -SMTPServer mail.domain.com -Subject “$($computername) is Offline" -Body "$($ComputerName) is Offline, please power on and re-run script."
    }
    function Send-EmailKickit {
        Send-MailMessage -From “admin@domain.com" -To "admin1@domain.com" -SMTPServer mail.domain.com -Subject “$($computername) Completed." -Body "$($ComputerName) Completed and is rebooting."
    }
    function Test-MyConnection {
        param (
            [Parameter(Mandatory=$true)]
            [System.String]
            $ComputerName
        )
        if (Test-Connection $computername -Quiet -Count 2) {
            Write-Output "Ping successful on $($computerName)"      
            Test-Running -ComputerName $ComputerName 
        }
        else {
            Send-EmailOffline -ComputerName $computername   
        }
    }   
    function Test-Running {
        param (
            [Parameter(Mandatory=$true)]
            [System.String]$ComputerName
        )
        $processList = @'
            "Name",             "Expected",     "Running"
            "cmd",              "1",            "0"
            "TrustedInstaller", "1",            "0"
            "TIWorker",         "1",            "0"
            "OfficeClickToRun", "2",            "0"
'@ | ConvertFrom-Csv | ForEach-Object {$_.Expected = [int]$_.Expected; $_}
        $splat = @{}
        $splat['ComputerName'] = $computerName
        #Write-Output "Monitoring processes on $($computerName)" 
        Do  {
          $processList | ForEach-Object {
              $_.Running = @(Get-Process $_.Name @splat -ErrorAction SilentlyContinue).Count
          }
          ($processList | Format-Table -AutoSize | Out-String).Split("`r`n", [StringSplitOptions]::RemoveEmptyEntries) | Write-Output
          If ($running = @($processList | Where-Object {$_.Running -ge $_.Expected}).Count) {
              Start-Sleep -Seconds 5
          }
          Write-Output "Monitoring processes on $($computerName)" 
        } Until (-not $running)
        Send-EmailKickit -ComputerName $computername 
        $Today = (Get-Date).ToString('MM-dd-yyyy'),
        $ExactTime = Get-Date -Format "MM-dd-yyyy HHmm tt"
        $Logfile = "c:\input\Log.csv"
        $myobj = "" | Select "Computer", "ExactTime"
        $myobj.Computer = $ComputerName
        $myobj.ExactTime = $($(Get-Date).ToString('yyyy-MM-dd::hh:mm:ss'))

        $OutArray += $myobj
        $OutArray | Export-Csv $Logfile -NoTypeInformation -Append 
        Return "Process count finished on $computername"

        # force reboot, after all process requirements met
        Restart-Computer -Computer $Computername -Force

    } 
    Start-CCMRerunAdvertisement –ComputerName $using:Computer -AdvertisementID "TMC73737" 
    Test-MyConnection -ComputerName $using:Computer
    }
       }
    } # End of initial ForEachRerun workflow
$ComputerList  = Get-Content "c:\input\computerlist.txt"
foreachrerun -Computers $ComputerList

Open in new window

I have tab-corrected (mostly) the above code and added comments where I felt appropriate.

Do you just want the workflow to run 3 times or a subset of options within the workflow?  Do you want the machines to perform the work 3 times, then reboot 3 times and then shutdown?

Author

Commented:

@Darrell Porter - thank you.  


Ideally, I would want the machines to perform the work 3 times, with a reboot after each run.


The specific advert forces software and patch updates for QA of new builds. I find on average, this needs to be run 3 times with a reboot after each run for all of the updates to get installed.  When the advert is run, I'm monitoring the processes so I know all updates are completed, then rebooting.


So the workflow would be:


1)  Run the SCCM advert (for patches & updates) on each computer.

2)  Monitor the process count, when count meets expected (updates are complete)

3)  Take action to reboot, send emails, write to logs  (this is where I think I may need to have a sleep command to let the computer fully boot for moving through the workflow too quick).

4)  After reboot, run workflow again, ie; advert, process loop, reboot

5)  Repeat one more time, then shutdown computer


Ideally, an email would only be sent with the final step and shutdown, but not necessary.  


It's not perfect, I know a query to detect if any patches or updates are needed would be more precise, but this is more of a brute force bulk method and then checking the computer in client center for missing patches as a final QA step.


I had a wrapper to run this for 30 minutes with a sleep every 10 minutes, but all this is really doing is stopping the script every 10 minutes so I don't think I'm headed in the right direction.


$timeout = new-timespan -Minutes 30
$sw = [diagnostics.stopwatch]::StartNew()
while ($sw.elapsed -lt $timeout){
  .\Scipt.ps1
  start-sleep -seconds 600
}
write-host "Script cycle complete."


I really appreciate any help!

Darrell PorterEnterprise Business Process Architect

Commented:
I believe this is what you are looking for - additional comments and code added - loop is located between lines 128 and 131

$OutArray = @()
workflow foreachrerun {
    param([string[]]$computers)
    foreach –parallel ($computer in $computers) {
       InlineScript {
         $n = 3 # Number of iterations through the patch process
         $m = 5 # Number of minutes for the script to pause between iterations of the loop
         Function Start-CCMRerunAdvertisement {
         [CmdLetBinding()]Param(
           [Parameter(Mandatory=$true)][string]$computerName,
           [Parameter(Mandatory=$false)][string]$advertisementId = "*",
           [Parameter(Mandatory=$false)][string]$packageId = "*",
           [Parameter(Mandatory=$false)][int]$maxRun = 1
         #[Parameters(Mandatory=$false)][switch]$moreThanPing = $false
        )
        if($advertisementId -eq "*" -and $packageId -eq "*") {
            Write-Error "You must supply either an AdvertisementID or a PackageID"
            return "Missing Parameters"
            break
        }
        $searchString = "$advertisementId-$packageId-*" 
        if(!(Test-Connection -ComputerName $computername -ErrorAction SilentlyContinue)) {
            if($moreThanPing) { 
                if(!(Get-ChildItem "\\$computername\c$" -ErrorAction SilentlyContinue)) {
                    Write-Error "System Offline"
                    Return "System Offline"
                    break
                }
            } else {
                Return "$Computername is Offline"
                break
            }
        }
        Write-Verbose "Getting ID of ScheduleMessage on $computername"
        $schMsgs = Get-WmiObject -ComputerName $computername -Namespace "root\ccm\policy\machine\actualconfig" -Class CCM_Scheduler_ScheduledMessage
        $thisMsg = $schMsgs | ? { $_.ScheduledMessageID -like $searchString } | Sort ActiveTime -Descending | select -First $maxRun
        if(!$thisMsg) {
            Write-Verbose "Cannot Find Advertisement/Package on Target Computer"
            Return "Cannot Find Advertisment"
            break
        }
        $thisMsg | % {
            [xml]$activeMessage = $_.activeMessage
            $amProgramId = $activeMessage.SoftwareDeploymentMessage.ProgramID
            $amAdvId = $activeMessage.SoftwareDeploymentMessage.AdvertisementID
            $amPkgId = $activeMessage.SoftwareDeploymentMessage.PackageID
            $ScheduledMessageId = $_.ScheduledMessageId
            Write-Verbose  "Restarting $amArogramId (ADV=$amAdvId) (PKG=$amPkgId) for Schedule Message $ScheduledMessageId"
            $softwareDist = Get-WmiObject -ComputerName $computername -Namespace "root\ccm\policy\machine\actualconfig" -Class CCM_SoftwareDistribution -Filter "ADV_AdvertisementID = '$amAdvId' and PKG_PackageID = '$amPkgId'"
            $original_Rerun = $softwareDist.ADV_RepeatRunBehavior
            if($original_Rerun -ne "RerunAlways") {
                write-verbose "Changing Rerun Status from $original_Rerun to RerunAlways"
                $softwareDist.ADV_RepeatRunBehavior = "RerunAlways"
                $softwareDist.put() | Out-Null
            }
            Write-Verbose "Triggering Schedule on $computername"
            Invoke-WmiMethod -ComputerName $computername -Namespace "root\ccm" -Class "SMS_CLIENT" -Name TriggerSchedule $ScheduledMessageId | Out-Null
            Write-Verbose "Sleeping for 5 seconds"
            Start-Sleep -Seconds 5
            if($original_Rerun -ne "RerunAlways") {
                Write-Verbose "Changing Rerun Status back to $original_Rerun"
                $softwareDist.ADV_RepeatRunBehavior = "$original_Rerun"
                $softwareDist.put() | Out-Null
            }
            Return "Advert Reran on succesfully on $computername"
        }
    } # End Function Start-CCMRerunAdvertisement
         function Send-EmailOffline {
            Send-MailMessage -From “admin@domain.com" -To "admin1@domain.com" -SMTPServer mail.domain.com -Subject “$($computername) is Offline" -Body "$($ComputerName) is Offline, please power on and re-run script."
         }
         function Send-EmailKickit {
            Send-MailMessage -From “admin@domain.com" -To "admin1@domain.com" -SMTPServer mail.domain.com -Subject “$($computername) Completed." -Body "$($ComputerName) Completed and is rebooting."
         }
         function Test-MyConnection {
            param (
            [Parameter(Mandatory=$true)]
            [System.String]
            $ComputerName
            )
            if (Test-Connection $computername -Quiet -Count 2) {
                Write-Output "Ping successful on $($computerName)"      
                Test-Running -ComputerName $ComputerName 
            }
            else {
                Send-EmailOffline -ComputerName $computername   
            }
        }   
        function Test-Running {
            param (
            [Parameter(Mandatory=$true)]
            [System.String]$ComputerName
            )
            $processList = @'
                "Name",             "Expected",     "Running"
                "cmd",              "1",            "0"
                "TrustedInstaller", "1",            "0"
                "TIWorker",         "1",            "0"
                "OfficeClickToRun", "2",            "0"
'@ | ConvertFrom-Csv | ForEach-Object {$_.Expected = [int]$_.Expected; $_}
            $splat = @{}
            $splat['ComputerName'] = $computerName
            #Write-Output "Monitoring processes on $($computerName)" 
            Do  {
              $processList | ForEach-Object {
              $_.Running = @(Get-Process $_.Name @splat -ErrorAction SilentlyContinue).Count
              }
              ($processList | Format-Table -AutoSize | Out-String).Split("`r`n", [StringSplitOptions]::RemoveEmptyEntries) | Write-Output
              If ($running = @($processList | Where-Object {$_.Running -ge $_.Expected}).Count) {
                  Start-Sleep -Seconds 5
              }
              Write-Output "Monitoring processes on $($computerName)" 
            } Until (-not $running)
            Send-EmailKickit -ComputerName $computername 
            $Today = (Get-Date).ToString('MM-dd-yyyy'),
            $ExactTime = Get-Date -Format "MM-dd-yyyy HHmm tt"
            $Logfile = "c:\input\Log.csv"
            $myobj = "" | Select "Computer", "ExactTime"
            $myobj.Computer = $ComputerName
            $myobj.ExactTime = $($(Get-Date).ToString('yyyy-MM-dd::hh:mm:ss'))

            $OutArray += $myobj
            $OutArray | Export-Csv $Logfile -NoTypeInformation -Append 
            Return "Process count finished on $computername"

            # force reboot, after all process requirements met
            Restart-Computer -Computer $Computername -Force

        }
        For ($i=1; $i -le $n; $i++) { # Begin looping $n times - $n is defined at the top of the InlineScript
            Start-CCMRerunAdvertisement –ComputerName $using:Computer -AdvertisementID "TMC73737"
            Start-Sleep -Seconds ($m*60) # Wait $m minutes for computer to reboot - $m is defined at the top of the InlineScript
            Test-MyConnection -ComputerName $using:Computer
        }

       } # End of InlineScript
    } # End of ForEach -parallel
} # End of initial ForEachRerun workflow
$ComputerList  = Get-Content "c:\input\computerlist.txt"
foreachrerun -Computers $ComputerList

Open in new window

Author

Commented:

Thanks so much for taking the time on this.  So it looks like the iterations and timing worked for both the advert and the process loop - however, none of the reboots occurred.  Any ideas?  


Here is the timeline logged, I had two computers in the list I ran this against and also validated the advert ran on both.


Started @ 12:51pm - process loop began @ 12:56pm...
Got email on computer99 @ 12:58pm, but didn't reboot...
Got email on computer20 @ 1:00pm, but didn't reboot...
Got email on computer99 @ 1:02pm, but didn't reboot...
Got email on computer20 @ 1:05pm, but didn't reboot...
Count finished on computer99 @ 1:09pm, email sent.. still no reboot...
Count finished on computer20 @ 1:10pm, email sent.. still no reboot...


If it helps at all, here are the commands in the advert itself I'm running:

Trigger DataDiscoverRecord (DDR) WMIC.exe /namespace:\root\ccm path sms_client CALL TriggerSchedule "{00000000-0000-0000-0000-000000000003}" /NOINTERACTIVE
Trigger Hardware Inventory WMIC.exe /namespace:\root\ccm path sms_client CALL TriggerSchedule "{00000000-0000-0000-0000-000000000001}" /NOINTERACTIVE
Trigger Software Inventory WMIC.exe /namespace:\root\ccm path sms_client CALL TriggerSchedule "{00000000-0000-0000-0000-000000000113}" /NOINTERACTIVE
Trigger Software Updates WMIC.exe /namespace:\root\ccm path sms_client CALL TriggerSchedule "{00000000-0000-0000-0000-000000000113}" /NOINTERACTIVE
Trigger Software Updates Assignments WMIC.exe /namespace:\root\ccm path sms_client CALL TriggerSchedule "{00000000-0000-0000-0000-000000000108}" /NOINTERACTIVE 
Trigger Request Machine Assignments WMIC.exe /namespace:\root\ccm path sms_client CALL TriggerSchedule "{00000000-0000-0000-0000-000000000021}" /NOINTERACTIVE
Darrell PorterEnterprise Business Process Architect

Commented:
Yes - I failed to see in what order the functions were called.
$OutArray = @()
workflow foreachrerun {
    param([string[]]$computers)
    foreach –parallel ($computer in $computers) {
       InlineScript {
         $n = 3 # Number of iterations through the patch process
         $m = 5 # Number of minutes for the script to pause between iterations of the loop
         Function Start-CCMRerunAdvertisement {
         [CmdLetBinding()]Param(
           [Parameter(Mandatory=$true)][string]$computerName,
           [Parameter(Mandatory=$false)][string]$advertisementId = "*",
           [Parameter(Mandatory=$false)][string]$packageId = "*",
           [Parameter(Mandatory=$false)][int]$maxRun = 1
         #[Parameters(Mandatory=$false)][switch]$moreThanPing = $false
        )
        if($advertisementId -eq "*" -and $packageId -eq "*") {
            Write-Error "You must supply either an AdvertisementID or a PackageID"
            return "Missing Parameters"
            break
        }
        $searchString = "$advertisementId-$packageId-*" 
        if(!(Test-Connection -ComputerName $computername -ErrorAction SilentlyContinue)) {
            if($moreThanPing) { 
                if(!(Get-ChildItem "\\$computername\c$" -ErrorAction SilentlyContinue)) {
                    Write-Error "System Offline"
                    Return "System Offline"
                    break
                }
            } else {
                Return "$Computername is Offline"
                break
            }
        }
        Write-Verbose "Getting ID of ScheduleMessage on $computername"
        $schMsgs = Get-WmiObject -ComputerName $computername -Namespace "root\ccm\policy\machine\actualconfig" -Class CCM_Scheduler_ScheduledMessage
        $thisMsg = $schMsgs | ? { $_.ScheduledMessageID -like $searchString } | Sort ActiveTime -Descending | select -First $maxRun
        if(!$thisMsg) {
            Write-Verbose "Cannot Find Advertisement/Package on Target Computer"
            Return "Cannot Find Advertisment"
            break
        }
        $thisMsg | % {
            [xml]$activeMessage = $_.activeMessage
            $amProgramId = $activeMessage.SoftwareDeploymentMessage.ProgramID
            $amAdvId = $activeMessage.SoftwareDeploymentMessage.AdvertisementID
            $amPkgId = $activeMessage.SoftwareDeploymentMessage.PackageID
            $ScheduledMessageId = $_.ScheduledMessageId
            Write-Verbose  "Restarting $amArogramId (ADV=$amAdvId) (PKG=$amPkgId) for Schedule Message $ScheduledMessageId"
            $softwareDist = Get-WmiObject -ComputerName $computername -Namespace "root\ccm\policy\machine\actualconfig" -Class CCM_SoftwareDistribution -Filter "ADV_AdvertisementID = '$amAdvId' and PKG_PackageID = '$amPkgId'"
            $original_Rerun = $softwareDist.ADV_RepeatRunBehavior
            if($original_Rerun -ne "RerunAlways") {
                write-verbose "Changing Rerun Status from $original_Rerun to RerunAlways"
                $softwareDist.ADV_RepeatRunBehavior = "RerunAlways"
                $softwareDist.put() | Out-Null
            }
            Write-Verbose "Triggering Schedule on $computername"
            Invoke-WmiMethod -ComputerName $computername -Namespace "root\ccm" -Class "SMS_CLIENT" -Name TriggerSchedule $ScheduledMessageId | Out-Null
            Write-Verbose "Sleeping for 5 seconds"
            Start-Sleep -Seconds 5
            if($original_Rerun -ne "RerunAlways") {
                Write-Verbose "Changing Rerun Status back to $original_Rerun"
                $softwareDist.ADV_RepeatRunBehavior = "$original_Rerun"
                $softwareDist.put() | Out-Null
            }
            Return "Advert Reran on succesfully on $computername"
        }
    } # End Function Start-CCMRerunAdvertisement
         function Send-EmailOffline {
            Send-MailMessage -From “admin@domain.com" -To "admin1@domain.com" -SMTPServer mail.domain.com -Subject “$($computername) is Offline" -Body "$($ComputerName) is Offline, please power on and re-run script."
         }
         function Send-EmailKickit {
            Send-MailMessage -From “admin@domain.com" -To "admin1@domain.com" -SMTPServer mail.domain.com -Subject “$($computername) Completed." -Body "$($ComputerName) Completed and is rebooting."
         }
        function Test-Running {
            param (
            [Parameter(Mandatory=$true)]
            [System.String]$ComputerName
            )
            $processList = @'
                "Name",             "Expected",     "Running"
                "cmd",              "1",            "0"
                "TrustedInstaller", "1",            "0"
                "TIWorker",         "1",            "0"
                "OfficeClickToRun", "2",            "0"
'@ | ConvertFrom-Csv | ForEach-Object {$_.Expected = [int]$_.Expected; $_}
            $splat = @{}
            $splat['ComputerName'] = $computerName
            #Write-Output "Monitoring processes on $($computerName)" 
            Do  {
              $processList | ForEach-Object {
              $_.Running = @(Get-Process $_.Name @splat -ErrorAction SilentlyContinue).Count
              }
              ($processList | Format-Table -AutoSize | Out-String).Split("`r`n", [StringSplitOptions]::RemoveEmptyEntries) | Write-Output
              If ($running = @($processList | Where-Object {$_.Running -ge $_.Expected}).Count) {
                  Start-Sleep -Seconds 5
              }
              Write-Output "Monitoring processes on $($computerName)" 
            } Until (-not $running)
            Send-EmailKickit -ComputerName $computername 
            $Today = (Get-Date).ToString('MM-dd-yyyy'),
            $ExactTime = Get-Date -Format "MM-dd-yyyy HHmm tt"
            $Logfile = "c:\input\Log.csv"
            $myobj = "" | Select "Computer", "ExactTime"
            $myobj.Computer = $ComputerName
            $myobj.ExactTime = $($(Get-Date).ToString('yyyy-MM-dd::hh:mm:ss'))

            $OutArray += $myobj
            $OutArray | Export-Csv $Logfile -NoTypeInformation -Append 
            Return "Process count finished on $computername"

            # force reboot, after all process requirements met
            Restart-Computer -Computer $Computername -Force
        }

        function Test-MyConnection {
            param (
            [Parameter(Mandatory=$true)]
            [System.String]
            $ComputerName
            )
            if (Test-Connection $computername -Quiet -Count 2) {
                Write-Output "Ping successful on $($computerName)"      
                Test-Running -ComputerName $ComputerName 
            }
            else {
                Send-EmailOffline -ComputerName $computername   
            }
        } # End Test-MyConnection function
           
        For ($i=1; $i -le $n; $i++) { # Begin looping $n times - $n is defined at the top of the InlineScript
            Start-CCMRerunAdvertisement –ComputerName $using:Computer -AdvertisementID "TMC73737"
            Start-Sleep -Seconds ($m*60) # Wait $m minutes for computer to reboot - $m is defined at the top of the InlineScript
            Test-MyConnection -ComputerName $using:Computer
        }
       } # End of InlineScript
    } # End of ForEach -parallel
} # End of initial ForEachRerun workflow
$ComputerList  = Get-Content "c:\input\computerlist.txt"
foreachrerun -Computers $ComputerList

Open in new window

Author

Commented:

It still is not issuing the restart command for some reason, is there anything you would suggest to debug?

Darrell PorterEnterprise Business Process Architect

Commented:
Insert line 112 (before the Restart-Computer command):
Write-host "Now restarting computer $computername"

Open in new window

Insert line 132 (after inserting above line - before the Start-CCMRerunAdvertisement within the loop):
Write-Host "Starting Start-CCMRerunAdvertisement iteration $i"

Open in new window

Insert line line 135 (after inserting above 2 lines - before the Test-Connection within the loop):
Write-Host "Starting Test-Connection iteration $i"

Open in new window

Author

Commented:

I had to "Write-Output", had errors with Write-Host with user interaction..


Right after "Process count finished on computer99" it fired an email but no restart...


C:\Scripts\DP2.ps1
Starting Start-CCMRerunAdvertisement iteration 1
Starting Start-CCMRerunAdvertisement iteration 1
Advert Reran on succesfully on computer20
Advert Reran on succesfully on computer99
Starting Test-Connection iteration 1
Starting Test-Connection iteration 1
Ping successful on computer20
Ping successful on computer99
Name             Expected Running
----             -------- -------
cmd                     1       1
TrustedInstaller        1       0
TIWorker                1       0
OfficeClickToRun        2       2
Name             Expected Running
----             -------- -------
cmd                     1       0
TrustedInstaller        1       0
TIWorker                1       0
OfficeClickToRun        2       1
Monitoring processes on computer99
Process count finished on computer99
Starting Start-CCMRerunAdvertisement iteration 2
Monitoring processes on computer20
Name             Expected Running
----             -------- -------
cmd                     1       1
TrustedInstaller        1       0
TIWorker                1       0
OfficeClickToRun        2       2
Advert Reran on succesfully on computer99
Monitoring processes on computer20
Name             Expected Running
----             -------- -------
cmd                     1       1
TrustedInstaller        1       0
TIWorker                1       0
OfficeClickToRun        2       2
Monitoring processes on computer20
Name             Expected Running
----             -------- -------
cmd                     1       1
TrustedInstaller        1       0
TIWorker                1       0
OfficeClickToRun        2       2
Monitoring processes on computer20
Name             Expected Running
----             -------- -------
cmd                     1       1
TrustedInstaller        1       0
TIWorker                1       0
OfficeClickToRun        2       2
Monitoring processes on computer20
Name             Expected Running
----             -------- -------
cmd                     1       1
TrustedInstaller        1       0
TIWorker                1       0
OfficeClickToRun        2       2
Monitoring processes on computer20
Name             Expected Running
----             -------- -------
cmd                     1       1
TrustedInstaller        1       0
TIWorker                1       0
OfficeClickToRun        2       2
Monitoring processes on computer20
Name             Expected Running
----             -------- -------
cmd                     1       1
TrustedInstaller        1       0
TIWorker                1       0
OfficeClickToRun        2       2
Monitoring processes on computer20
Name             Expected Running
----             -------- -------
cmd                     1       1
TrustedInstaller        1       0
TIWorker                1       0
OfficeClickToRun        2       2
Monitoring processes on computer20
Name             Expected Running
----             -------- -------
cmd                     1       1
TrustedInstaller        1       0
TIWorker                1       0
OfficeClickToRun        2       2
Monitoring processes on computer20
Name             Expected Running
----             -------- -------
cmd                     1       1
TrustedInstaller        1       0
TIWorker                1       0
OfficeClickToRun        2       2

Darrell PorterEnterprise Business Process Architect

Commented:
Let's try the following:
Write-Output "Starting Start-CCMRerunAdvertisement iteration $i for computer $computer"

Open in new window

and
Write-Output "Starting Test-Connection iteration $i for computer $computer"

Open in new window

so we can also see the computer's name.

Also insert the following line before line 79 ($processList = @'):
Write-Output "Computer is $computer"

Open in new window

Author

Commented:

Looks like "Computer is" was blank in the beginning between pings.


        function Test-Running {
            param (
            [Parameter(Mandatory=$true)]
            [System.String]$ComputerName
            )
            Write-Output "Computer is $computer"
            $processList = @'
                "Name",             "Expected",     "Running"
                "cmd",              "1",            "0"
                "TrustedInstaller", "1",            "0"
                "TIWorker",         "1",            "0"
                "OfficeClickToRun", "2",            "0"
'@ | ConvertFrom-Csv | ForEach-Object {$_.Expected = [int]$_.Expected; $_}
            $splat = @{}
            $splat['ComputerName'] = $computerName

C:\Scripts\DP2.ps1
Starting Start-CCMRerunAdvertisement iteration 1 for computer
Starting Start-CCMRerunAdvertisement iteration 1 for computer
Advert Reran on succesfully on computer20
Advert Reran on succesfully on computer99
Starting Test-Connection iteration 1 for computer
Starting Test-Connection iteration 1 for computer
Ping successful on computer20
Computer is
Ping successful on computer99
Computer is
Name             Expected Running
----             -------- -------
cmd                     1       0
TrustedInstaller        1       0
TIWorker                1       0
OfficeClickToRun        2       3
Name             Expected Running
----             -------- -------
cmd                     1       0
TrustedInstaller        1       0
TIWorker                1       0
OfficeClickToRun        2       1
Monitoring processes on computer99
Process count finished on computer99
Starting Start-CCMRerunAdvertisement iteration 2 for computer
Monitoring processes on computer20
Name             Expected Running
----             -------- -------
cmd                     1       0
TrustedInstaller        1       0
TIWorker                1       0
OfficeClickToRun        2       3
Advert Reran on succesfully on computer99
Monitoring processes on computer20

Name             Expected Running
----             -------- -------
cmd                     1       0
TrustedInstaller        1       0
TIWorker                1       0
OfficeClickToRun        2       2
Monitoring processes on ws147520
Name             Expected Running
----             -------- -------
cmd                     1       0
TrustedInstaller        1       0
TIWorker                1       0
OfficeClickToRun        2       1
Monitoring processes on computer20
Process count finished on computer20
Starting Start-CCMRerunAdvertisement iteration 2 for computer
Advert Reran on succesfully on computer20
Enterprise Business Process Architect
Commented:
On the Write-output lines, try changing $computer to $using:computer

Author

Commented:

So I got this working by using   $using:computer in the Restart command.  


Is there a way to only send and email, along with using a Shutdown command on Iteration 3?


I really appreciate all of your time and expertise.