SCCM PShell Scripts

SCCM Client Actions



Contents



Have you ever needed to invoke SCCM 2012 client actions on a bunch of clients through WinRM or PSEXEC?

Lets imagine remote management is configured and running on all clients and we have no firewall restrictions. Then use the "SCCM Client Actions with WRM" script below.

If you either do not have WinRm configured, or have firewall restrictions but have psexec available than "SCCM Client Actions with PSEXEC" is the one you need.

NOTE:
On either script you can edit the action you need by changing the value of the trigger.

SCCM Client Actions with WRM

## Begining of Script ##
$scriptfunction = "SCCMclientHWinvWMI"
$hostnames = "C:\zTemp\Scripts\hostnames2.txt"
$logpath = "C:\zTemp\Scripts\Logs\"
$logfile = "$logpath$scriptfunction$(get-date -format `"dd-MM-yyyy_hhmmtt`").txt"

Function HWI() #Hardware Inventory
   {
   write-host $_
   $SMSCli = [wmiclass] "\\$_\root\ccm:sms_client"
   $SMSCli.TriggerSchedule("{00000000-0000-0000-0000-000000000001}") 

   ## Edit the above value as required ##
   # 00000000-0000-0000-0000-000000000001 = Hardware Inventory Collection Task
   # 00000000-0000-0000-0000-000000000002 = Software Inventory Collection Task
   # 00000000-0000-0000-0000-000000000003 = Heartbeat Discovery Cycle
   # 00000000-0000-0000-0000-000000000010 = Software Inventory File Collection Task
   # 00000000-0000-0000-0000-000000000021 = Request Machine Policy Assignments
   # 00000000-0000-0000-0000-000000000022 = Evaluate Machine Policy Assignments
   # 00000000-0000-0000-0000-000000000024 = Refresh Location Services Task
   # 00000000-0000-0000-0000-000000000032 = Source Update Manage Update Cycle
   # 00000000-0000-0000-0000-000000000042 = Request Machine Policy Assignments
   # 00000000-0000-0000-0000-000000000108 = Software Updates Agent Assignment Evaluation Cycle
   # 00000000-0000-0000-0000-000000000113 = Force Update Scan
   }

function log($string)
   {
   write-host $string
   $string | out-file -Filepath $logfile -append
   }
### Use text file with hostnames as input
$a = Get-Content $hostnames

### Loop through hostnames
$a | % {
       $PingTest = (Test-Connection $_ -Count 1 -ea 0 -Quiet)
       If ($PingTest -eq "True")
          {
          HWI $_
          }
       else
          {
          log "$_ - NOT RESPONDING"
          }
       }
## END of Script ##

SCCM Client Actions with PSEXEC


## Begining of Script ##
$scriptfunction = "SCCMclientHWinvPSEXEC"
$hostnames = "C:\zTemp\Scripts\hostnames2.txt"
$logpath = "C:\zTemp\Scripts\Logs\"
$logfile = "$logpath$scriptfunction$(get-date -format `"dd-MM-yyyy_hhmmtt`").txt"

## Edit the value below as required ##
# 00000000-0000-0000-0000-000000000001 = Hardware Inventory Collection Task
# 00000000-0000-0000-0000-000000000002 = Software Inventory Collection Task
# 00000000-0000-0000-0000-000000000003 = Heartbeat Discovery Cycle
# 00000000-0000-0000-0000-000000000010 = Software Inventory File Collection Task
# 00000000-0000-0000-0000-000000000021 = Request Machine Policy Assignments
# 00000000-0000-0000-0000-000000000022 = Evaluate Machine Policy Assignments
# 00000000-0000-0000-0000-000000000024 = Refresh Location Services Task
# 00000000-0000-0000-0000-000000000032 = Source Update Manage Update Cycle
# 00000000-0000-0000-0000-000000000042 = Request Machine Policy Assignments
# 00000000-0000-0000-0000-000000000108 = Software Updates Agent Assignment Evaluation Cycle
# 00000000-0000-0000-0000-000000000113 = Force Update Scan


function log($string)
   {
   write-host $string
   $string | out-file -Filepath $logfile -append
   }
### Use text file with hostnames as input
$a = Get-Content $hostnames

### Loop hostname through routine
$a | % {
       $PingTest = (Test-Connection $_ -Count 1 -ea 0 -Quiet)
       If ($PingTest -eq "True")
          {
          Try
             {
             $command = '(WMIC /namespace:\\root\ccm path sms_client CALL TriggerSchedule "{00000000-0000-0000-0000-000000000001}" /NOINTERACTIVE)'
             $valuex = psexec -d \\$_ cmd /c $command -ErrorAction Stop
             }
          Catch
             {
             $ErrorMessage = $_.Exception.Message
             $FailedItem = $_.Exception.ItemName
             log $FailedItem $ErrorMessage
             }
          }
          else
          {
          log "$_ - NOT RESPONDING"
          }
       }
## END of Script ##

SCCM Distribution Point Group Housekeeping

This is one of those scripts I wish I had more time to spend on. Yes it was done in a rush and I think its reflects that but it does do the job.

 A client running SCCM 2012 R2 has well over 600 DP's (Distribution Points) divided across 5 different collections each representing a fase. Company policy is that deployments should be phased according to these collections so in summary I have to ensure that I have a DPGroup per phase and that each DPGroup has the same members as its counterpart collection.

 
This is simple in theory but when you have hundreds of DP's and new ones get added on a regular basis, this quickly becomes a bit of a headache.

So this script will look at a single collection and make sure that the DPGroup has the same members.
Since I have 5 collections I have 5 scripts with the variables edited for the correct Collection and DPGroup.

Logs

The log created by this script will contain the following entries:

%DPserver% - Does Not Seem to have Distribution Point Role Installed
The above output is generated when a servers registry does not contain: SOFTWARE\Microsoft\SMS\Operations Management\SMS Server Role\SMS Distribution Point suggesting that the DP role is not installed.

%DPserver% - is not known by SCCM as a DP server
The above output is generated when "Get-CMDistributionPoint -SiteSystemServerName %DPserver%" does not return a value. If no value is returned, either the DPserver has a configuration issue or your security scopes are restricted.

%DPserver% - Already Exists in DP Group %DPGROUP% And will be Reassigned
The above output is generated when the script has identified a DPserver in a different DPGroup. The DPserver will be removed from that group and assigned to the DPGroup defined by the variable $DPGroupName.

%DPserver% - inserted into DPGroup %DPGroup%
The above output is generated when the script has associated a new DPserver to a DPGroup.


Variables Explained
$scriptfunction - Its value will be used to name the log file.
$CollectionName - Collection that will be enumerated by the script.
$DPGroupName - DPGroup where collection members will be added.
$ignore - You might have a DPgroup that already contains ALL of your DPS. If so, use this variable so that this DPGroup gets ignored.


## BEGINING OF SCRIPT 

##Variables
$scriptfunction = "Add_DPs_To_DPGROUP_Test01"
$CollectionName = "My DPs" # Collection Name ## MUST EDIT
$DPGroupName = "Test02" # DPGroup you want to add too # MUST EDIT   
$SiteCode = "SS1" # SITE CODE # MUST EDIT 
$SCCMSrv = "SCCM01" # SCCM HOSTNAME # MUST EDIT
$domain = ".lab.local" # DomainName ####### MUST EDIT 
$logpath = "C:\Scripts\logs\" # Log Path ####### MUST EDIT 
$ignore = "Test05" # DPGroup you want to ignore
$NS = "root\sms\site_" + $SiteCode
$logfile = "$logpath $scriptfunction -$(get-date -format `"dd-MM-yyyy_hhmmtt`").txt"

## Log function
function log($logfunction)
{
  write-host $logfunction
  $logfunction | out-file -Filepath $logfile -append
}
## Check Remote Registry
function getreg()
{
Try{
    $baseKey = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey([Microsoft.Win32.RegistryHive]"LocalMachine",$DP,[Microsoft.Win32.RegistryView]::Registry64)
    $key = $baseKey.OpenSubKey("SOFTWARE\Microsoft\SMS\Operations Management\SMS Server Role\SMS Distribution Point\")
    $subkeys = $key.GetValue("Availability State")  
    $key.Close()
    $baseKey.Close()
    Return $subkeys 
   } Catch{write-host "$DP - 01 - SMS Server Role registry key not found. Check if DP role is installed."}
}
## Check if Server is a known DP to SCCM
function getdpstate()
{
$dpst = Get-CMDistributionPoint -SiteSystemServerName $DP$domain | Select-Object -exp RoleName
Return $dpst
}
## Enumerate DPGroups
function checkdpgp()
{
$DPGroup = Get-CMDistributionPointGroup
$DPGroup | % {$DPG = $_.Name.ToString()
              $DPG | % { 
              if ($DPG -eq $ignore){return}
              Write-Host $DPG
                       $dpgp01 = Get-WmiObject -Class SMS_DPGroupInfo -Filter "Name = '$DPG'" -ComputerName $SCCMSrv -Namespace $NS 
                       $gid = $dpgp01.GroupID 
                       $dpgp02 = Get-WmiObject -Class "SMS_DPGroupMembers" -Filter "GroupID= '$gid'" -ComputerName $SCCMSrv -Namespace $NS | Select DPNALPath
                       $dpgp02 | % {
                                   If ($_ -like "*" + $DP + "*") {
                                                                 $result = "True"
                                                                 Return $DPG,$result                 
                                                                 }
                                   }
                       }
             }
}

$Collection = Get-WmiObject -ComputerName $SCCMSrv  -Namespace $NS -Class 'SMS_Collection'
$MyCollection = $Collection | Where-Object { $_.Name -eq $CollectionName }
$MyCollectionMembers = Get-WmiObject  -ComputerName $SCCMSrv -Namespace $NS -Query "select * from SMS_CM_RES_COLL_$($MyCollection.CollectionID)"

$MyCollectionMembers | % {
$DP = $_.Name.ToString()
$DP | % {getreg}
$AvailState = getreg
if ($AvailState -eq 0)
    {
    $dpstate = getdpstate
    if ($dpstate -eq "SMS Distribution Point") 
       {
        $dpgexist0,$dpgexist1 = checkdpgp
        if ($dpgexist1 -ne "True")
        {
         log "$DP - inserted into DPGroup - $DPGroupName"
         Add-CMDistributionPointToGroup –DistributionPointName $DP$domain –DistributionPointGroupName $DPGroupName
        }
        else
         {
         if ($dpgexist0 -ne $DPGroupName)
            {
            log "$DP - Already Exists in DP Group - $dpgexist0 - And Will Be Reassigned"
            Remove-CMDistributionPointFromGroup -DistributionPointGroupName $dpgexist0 -DistributionPointName $DP$domain -Force
            log "$DP - Added to DPGroup - $DPGroupName"
            Add-CMDistributionPointToGroup –DistributionPointName $DP$domain –DistributionPointGroupName $DPGroupName
            }
         }
       }
    else
    {log "$DP - is not known by SCCM as a DP server"}
    }
else
{log "$DP - Does Not Seem to have DistributionPoint Role Installed"}
}
## END OF SCRIPT


Running Powershell Script on SCCM


I'm just going to quickly go through the steps and highlight some bits & tips.
NOTES: I called the script Ad_DP2_DPGroup.ps1
  • Launch SCCM Powershell.
      

  •  Place the ps1 script and host-list (DPListDPGroup01.txt) somewhere on the host that is running the SCCM console. In this example that will be "c:\temp\"
  • Now call your script as in the image: 
  • If you get the following execution policy error: 
  • Then chances are you will get the following when you try "Set-ExecutionPolicy Unrestricted" 

  • List the execution policy by:

  • If you have the same as in the image then:  

  • Now if you list the execution policy again you should get:


  •  If you get "The command cannot be run from the current drive..." Then "CD" to your site as shown in the next image (my test site is called SS1):
  •   Now try and run the script