Powershell script help with error handling, sccm queries

Ron Shorts
Ron Shorts used Ask the Experts™
on
Trying to accomplish the following, oBdA provided me with the process script, but I'd like to build on it.  If anyone can help, it would be greatly appreciated!

Have a powershell script that can be called from a vbscript and pass the parameter defined in the vbscript to the powershell script. In vbscript, I can call with this code, but not sure how to pass the parameter (computer) for example defined in vbscript...
'Set objShell = CreateObject("Wscript.Shell")
'Objshell.Run("powershell.exe -noexit .\test.ps1")

Open in new window

Powershell Script I'd like to do a few things:
• Check if the computer is online or offline.
• if computer is is offline, exit and send an email
• if the computer is online, run the below script that will check for running processes.
• if the below has anything other than return code 0 (meaning process is still running), have send email
• if the below has a return code of 0 (meaning no processing running), run an executable and invoke some SCCM commands.
 $computerName = 'RemoteMachine' ## Set to $null to run locally
$processList = @'
    "Name",             "Expected",     "Running"
    "cmd",              "1",            "0"
    "powershell",       "1",            "0"
    "OfficeClickToRun", "2",            "0"
'@ | ConvertFrom-Csv | ForEach-Object {$_.Expected = [int]$_.Expected; $_}

$splat = @{}
If ($computerName) {
    $splat['ComputerName'] = $computerName
    Write-Host "Testing processes on $($computerName)" -ForegroundColor Yellow
}
$cursorTop = [console]::cursorTop + 1
Do  {
    $processList | ForEach-Object {
        $_.Running = @(Get-Process $_.Name @splat -ErrorAction SilentlyContinue).Count
    }
    [console]::SetCursorPosition(0, $cursorTop)
    ($processList | Format-Table -AutoSize | Out-String).Split("`r`n", [StringSplitOptions]::RemoveEmptyEntries) | Write-Host
    If ($running = @($processList | Where-Object {$_.Running -ge $_.Expected}).Count) {
        Start-Sleep -Seconds 5
    }
} Until (-not $running)

return 0 

Open in new window

 
• SCCM commands I haven't worked with before, but looking for any failed updates or pending updates. Looking to have some sort of reporting or audit for these (send email) and invoke commands to trigger install. Below are some PS commands I found for these:

If you are interested in seeing which updates are missing:
get-wmiobject -query "SELECT * FROM CCM_SoftwareUpdate WHERE ComplianceState = 0" -namespace "ROOT\ccm\ClientSDK"

Open in new window

If you would like to see if there are updates applying, if true they are running:
$CCMUpdate = get-wmiobject -query "SELECT * FROM CCM_SoftwareUpdate" -namespace "ROOT\ccm\ClientSDK"
if(@($CCMUpdate | where { $_.EvaluationState -eq 2 -or $_.EvaluationState -eq 3 -or $_.EvaluationState -eq 4 -or $_.EvaluationState -eq 5 -or $_.EvaluationState -eq 6 -or $_.EvaluationState -eq 7 -or $_.EvaluationState -eq 11 }).length -ne 0) { $SCCMUpdate = $true } else {$SCCMUpdate = $false }

Open in new window

Here is a nice addition if you only want to install specific update(s) you just have to modify the select statement:
([wmiclass]'ROOT\ccm\ClientSDK:CCM_SoftwareUpdatesManager').InstallUpdates([System.Management.ManagementObject[]] (get-wmiobject -query 'SELECT * FROM CCM_SoftwareUpdate' -namespace 'ROOT\ccm\ClientSDK'))

Open in new window

Trigger install of all updates:
([wmiclass]'ROOT\ccm\ClientSDK:CCM_SoftwareUpdatesManager').InstallUpdates()

Open in new window

Triggering an update scan on a client:
([wmiclass]'ROOT\ccm:SMS_Client').TriggerSchedule('{00000000-0000-0000-0000-000000000113}')

Open in new window

Comment
Watch Question

Do more with

Expert Office
EXPERT OFFICE® is a registered trademark of EXPERTS EXCHANGE®
Top Expert 2016

Commented:
as of 1810 you can run powershell scripts nativiley https://docs.microsoft.com/en-us/sccm/apps/deploy-use/create-deploy-scripts#script-output-pre-1810

function Send-Email {
  <#
      .SYNOPSIS
      Describe purpose of "Send-Email" in 1-2 sentences.

      .DESCRIPTION
      Add a more complete description of what the function does.

      .EXAMPLE
      Send-Email
      Describe what this call does

      .NOTES
      Place additional notes here.

      .LINK
      URLs to related sites
      The first link is opened by Get-Help -Online Send-Email

      .INPUTS
      List of input types that are accepted by this function.

      .OUTPUTS
      List of output types produced by this function.
  #>

}
Function Send-Email2 {
  <#
      .SYNOPSIS
      Describe purpose of "Send-Email2" in 1-2 sentences.

      .DESCRIPTION
      Add a more complete description of what the function does.

      .EXAMPLE
      Send-Email2
      Describe what this call does

      .NOTES
      Place additional notes here.

      .LINK
      URLs to related sites
      The first link is opened by Get-Help -Online Send-Email2

      .INPUTS
      List of input types that are accepted by this function.

      .OUTPUTS
      List of output types produced by this function.
  #>

}

function script:Test-Running
{
  <#
      .SYNOPSIS
      Short Description
      .DESCRIPTION
      Detailed Description
      .EXAMPLE
      Get-Something
      explains how to use the command
      can be multiple lines
      .EXAMPLE
      Get-Something
      another example
      can have as many examples as you like
  #>
  [CmdletBinding()]
  param
  (
    [Parameter(Mandatory=$false, Position=0)]
    [System.String]
    $computerName = $env:COMPUTERNAME
    )
  $processList = @'
    "Name",             "Expected",     "Running"
    "cmd",              "1",            "0"
    "powershell",       "1",            "0"
    "OfficeClickToRun", "2",            "0"
'@ | ConvertFrom-Csv | ForEach-Object {$_.Expected = [int]$_.Expected; $_}

  $splat = @{}
  If ($computerName) {
    $splat['ComputerName'] = $computerName
    Write-Host "Testing processes on $($computerName)" -ForegroundColor Yellow
  }
  #$cursorTop = [console]::cursorTop + 1
  Do  {
    $processList | ForEach-Object {
        $_.Running = @(Get-Process $_.Name @splat -ErrorAction SilentlyContinue).Count
    }
    #   [console]::SetCursorPosition(0, $cursorTop)
    ($processList | Format-Table -AutoSize | Out-String).Split("`r`n", [StringSplitOptions]::RemoveEmptyEntries) | Write-Host
    If ($running = @($processList | Where-Object {$_.Running -ge $_.Expected}).Count) {
        Start-Sleep -Seconds 5
    }
  } Until (-not $running)

  return 0 
}

function do-something
{

  [CmdletBinding()]
  param
  (
    [Parameter(Mandatory=$false, Position=0)]
    [System.String]
    $computerName = $env:COMPUTERNAME
    )
    if (!( Test-Connection -ComputerName $computerName -Count 2 ))
     {
      send-email -Computername $computername
    }
    else
    {
    $result = test-running -computerName $computernae 
    if ($result -eq 0){
      send-email2 -computername $computername
    } 
  }
    
    
    }


do-something -computerName $computerName

Open in new window


you don't need the loop or the output from the loop (it won't be visible)
you will have to flesh out the 2 send-emails  which can actually call an really-send_email where they only contain the $body text



'Set objShell = CreateObject("Wscript.Shell")
'Objshell.Run("powershell.exe -noexit .\test.ps1 -computername thecomputername")

Open in new window

Author

Commented:
David, really appreciate this.  Some questions - To test this, I modified some of the code (below) and put a variable for the $computername.  Instead of getting the environment variable for local computer, I would be passing the computer names from the vbscript (attached) and running this from a terminal server to remote computers.  This, I believe would pass the computername, or maybe listcomputers? (since this building an array from a SQL database).

Looking at the vb attached, you may  see an obvious and easier way, but if possible, I'd like to first Test-Connection first and email if no connection and have this quit or move on to the next computer in the array before processing any other code.  Thanks again

  $computername = 'computer01'

function Send-Email {
  <#
      .SYNOPSIS
      Describe purpose of "Send-Email" in 1-2 sentences.

      .DESCRIPTION
      Add a more complete description of what the function does.

      .EXAMPLE
      Send-Email
      Describe what this call does

      .NOTES
      Place additional notes here.

      .LINK
      URLs to related sites
      The first link is opened by Get-Help -Online Send-Email

      .INPUTS
      List of input types that are accepted by this function.

      .OUTPUTS
      List of output types produced by this function.
  #>

}
Function Send-Email2 {
  <#
      .SYNOPSIS
      Describe purpose of "Send-Email2" in 1-2 sentences.

      .DESCRIPTION
      Add a more complete description of what the function does.

      .EXAMPLE
      Send-Email2
      Describe what this call does

      .NOTES
      Place additional notes here.

      .LINK
      URLs to related sites
      The first link is opened by Get-Help -Online Send-Email2

      .INPUTS
      List of input types that are accepted by this function.

      .OUTPUTS
      List of output types produced by this function.
  #>

}

function script:Test-Running
{
  <#
      .SYNOPSIS
      Short Description
      .DESCRIPTION
      Detailed Description
      .EXAMPLE
      Get-Something
      explains how to use the command
      can be multiple lines
      .EXAMPLE
      Get-Something
      another example
      can have as many examples as you like
  <#
    <#[CmdletBinding()]
  param
  (
    [Parameter(Mandatory=$false, Position=0)]
    [System.String]
    $computerName = $env:COMPUTERNAME
    )
  #>

  $processList = @'
    "Name",             "Expected",     "Running"
    "cmd",              "1",            "0"
    "powershell",       "1",            "0"
    "OfficeClickToRun", "2",            "0"
'@ | ConvertFrom-Csv | ForEach-Object {$_.Expected = [int]$_.Expected; $_}

  $splat = @{}
  If ($computerName) {
    $splat['ComputerName'] = $computerName
    Write-Host "Testing processes on $($computerName)" -ForegroundColor Yellow
  }
  #$cursorTop = [console]::cursorTop + 1
  Do  {
    $processList | ForEach-Object {
        $_.Running = @(Get-Process $_.Name @splat -ErrorAction SilentlyContinue).Count
    }
    #   [console]::SetCursorPosition(0, $cursorTop)
    ($processList | Format-Table -AutoSize | Out-String).Split("`r`n", [StringSplitOptions]::RemoveEmptyEntries) | Write-Host
    If ($running = @($processList | Where-Object {$_.Running -ge $_.Expected}).Count) {
        Start-Sleep -Seconds 5
    }
  } Until (-not $running)

  return 0 
}

function do-something
{
  <#
  [CmdletBinding()]
  param
  (
    [Parameter(Mandatory=$false, Position=0)]
    [System.String]
    $computerName = $env:COMPUTERNAME
    )
  #>
    if (!( Test-Connection $computername -Count 2 ))
     {
      #send-email $computername
      Write-Host "Ping not successful on $($computerName)" -ForegroundColor Red
    }
    else
    {
    $result = test-running -computerName $computername 
    if ($result -eq 0){
      send-email2 $computername
      Write-Host "Ping successful on $($computerName)" -ForegroundColor Green
    } 
  }
    
    
    }

do-something $computerName

Open in new window

Do more with

Expert Office
Submit tech questions to Ask the Experts™ at any time to receive solutions, advice, and new ideas from leading industry professionals.

Start 7-Day Free Trial