Invoking Your SCCM 2012 Client Remotely – With PowerShell!

powershell3I actually wrote about this some time ago when I first started PowerShell’ing.  But I’ve since improved my script, and I’m sharing it with you now.

Sometimes we need to make a change to our CM clients’, or the environment that they reside in that requires us to trigger an action on the CM client itself.  Be it pull in an updated hardware inventory, force the machine to run a Software Updates Scan, or a Machine Policy to get a machine to detect a new software deployment; it can be a painful experience for an administrator to have to go from machine to machine to run an action.

There are the right-click tools for those that have access to the console, but not everyone does, and not every company will allow you to install them.  Wouldn’t it be nice to have a function that you could include in a module you’ve built, that has the ability to invoke a CM client?  Well, now I have something to offer you:
Function Invoke-CMClient{
<# .SYNOPSIS Invoke commands remotely on an SCCM Client for a system or systems. .DESCRIPTION This function allows you to remotely trigger some of the more common actions that you would find on the local Configuration Manager console. .PARAMETER -ComputerName <string[]> Specifies the target computer for the management operation. Enter a fully qualified domain name, a NetBIOS name, or an IP address. When the remote computer is in a different domain than the local computer, the fully qualified domain name is required. This command defaults to localhost. .PARAMETER -Action Specifies the action to be taken on the SCCM Client. The available actions are as follows: HardwareInv - Runs a Hardware Inventory Cycle on the target machine. SoftwareInv - Runs a Software Inventory Cycle on the target machine. UpdateScan - Runs a Software Updates Scan Cycle on the target machine. MachinePol - Runs a Machine Policy Retrieval and Evaluation Cycle on the target machine. UserPolicy - Runs a User Policy Retrieval and Evaluation Cycle on the target machine. FileCollect - Runs a File Collection Cycle on the target machine. .INPUTS You can pipe a computer name to Invoke-CMClient .EXAMPLE Invoke-CMClientAction -ComputerName server01 -Action HardwareInv The above command will invoke the Configuration Manager Client's Hardware Inventory Cycle on the targeted computer. The return will look like the following: __GENUS : 1 __CLASS : __PARAMETERS __SUPERCLASS : __DYNASTY : __PARAMETERS __RELPATH : __PARAMETERS __PROPERTY_COUNT : 1 __DERIVATION : {} __SERVER : server01 __NAMESPACE : ROOT\ccm __PATH : \\server01\ROOT\ccm:__PARAMETERS ReturnValue : PSComputerName : server01 .NOTES Created by Will Anderson. https://lastwordinnerd.com/category/posts/powershell-scripting/ This script is provided AS IS without warranty of any kind. #>

PARAM(
[Parameter(Mandatory=$True,ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True)]
[string[]]$ComputerName = $env:COMPUTERNAME,

[Parameter(Mandatory=$True)]
[ValidateSet('HardwareInv','SoftwareInv','UpdateScan','MachinePol','UserPolicy','DiscoveryInv','FileCollect')]
[string]$Action

)#Close Param

#$Action...actions...actions...
SWITCH ($action) {
'HardwareInv' {$_action = "{00000000-0000-0000-0000-000000000001}"}
'SoftwareInv' {$_action = "{00000000-0000-0000-0000-000000000002}"}
'UpdateScan' {$_action = "{00000000-0000-0000-0000-000000000113}"}
'MachinePol' {$_action = "{00000000-0000-0000-0000-000000000021}"}
'UserPolicy' {$_action = "{00000000-0000-0000-0000-000000000027}"}
'FileCollect' {$_action = "{00000000-0000-0000-0000-000000000010}"}
} #switch

FOREACH ($Computer in $ComputerName){
if ($PSCmdlet.ShouldProcess("$action $computer")) {

Invoke-WmiMethod -ComputerName $Computer -Namespace root\CCM -Class SMS_Client -Name TriggerSchedule -ArgumentList "$_action"

}#if
}#End FOREACH Statement

}#Close Function Invoke-CMClient
This function allows you to invoke an SCCM Client action remotely on a number of computers simultaneously.   I’ve included the actions most commonly used from my perspective, but you can add more if you like.  You can get a good list of them here, or just explore the SMS_Client class for a full list.

What You See Is Not Always What You Get-Member

powershell3Recently, I was approached by a co-worker to solve an issue they were having in PowerShell.  They wanted to get the full-path of all of the directories in a folder using Get-ChildItem.  By default, Get-ChildItem will display the permissions (Mode), LastWriteTime, Length, and Name columns.  However, the name column only displays the name.  So they asked me if I knew a good way to get the full path.

As administrators and engineers that have been in the industry for 15-20 years, it’s often hard for us to remember that PowerShell isn’t the WYSIWYG (‘What You See Is What You Get’ for you younger engineers. 🙂 ); that we’re moving .NET objects through the pipeline, and while PowerShell displays the most commonly pertinent data in a given command, it’s not everything.  Get-Member is a good way to give us a list of all of the data that’s passed down the pipe from one object to the next, and get the bits that we really need.

So let’s explore!

Get-ChildItem -Path "C:\Windows" -Directory | Get-Member

When you run a command against an object in the pipe, and pipe that to Get-Member, PowerShell looks at the object and displays the properties and methods associated with that object.  For example:

PS C:\> Get-ChildItem -Path "C:\Windows" -Directory | Get-Member

   TypeName: System.IO.DirectoryInfo

Name                      MemberType     Definition
----                      ----------     ---------- 
Mode                      CodeProperty   System.String Mode{get=Mode;}
Create                    Method         void Create(), void Create(System.Security.AccessControl.DirectorySecurity directorySecurity)
CreateObjRef              Method         System.Runtime.Remoting.ObjRef CreateObjRef(type requestedType)
CreateSubdirectory        Method         System.IO.DirectoryInfo CreateSubdirectory(string path), System.IO.DirectoryInfo CreateSubdirector...
Delete                    Method         void Delete(), void Delete(bool recursive)
EnumerateDirectories      Method         System.Collections.Generic.IEnumerable[System.IO.DirectoryInfo] EnumerateDirectories(), System.Col...
EnumerateFiles            Method         System.Collections.Generic.IEnumerable[System.IO.FileInfo] EnumerateFiles(), System.Collections.Ge...
EnumerateFileSystemInfos  Method         System.Collections.Generic.IEnumerable[System.IO.FileSystemInfo] EnumerateFileSystemInfos(), Syste...
Equals                    Method         bool Equals(System.Object obj)
GetAccessControl          Method         System.Security.AccessControl.DirectorySecurity GetAccessControl(), System.Security.AccessControl....
GetDirectories            Method         System.IO.DirectoryInfo[] GetDirectories(), System.IO.DirectoryInfo[] GetDirectories(string search...
GetFiles                  Method         System.IO.FileInfo[] GetFiles(string searchPattern), System.IO.FileInfo[] GetFiles(string searchPa...
GetFileSystemInfos        Method         System.IO.FileSystemInfo[] GetFileSystemInfos(string searchPattern), System.IO.FileSystemInfo[] Ge...
GetHashCode               Method         int GetHashCode()
GetLifetimeService        Method         System.Object GetLifetimeService()
GetObjectData             Method         void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serializati...
GetType                   Method         type GetType()
InitializeLifetimeService Method         System.Object InitializeLifetimeService()
MoveTo                    Method         void MoveTo(string destDirName)
Refresh                   Method         void Refresh()
SetAccessControl          Method         void SetAccessControl(System.Security.AccessControl.DirectorySecurity directorySecurity)
ToString                  Method         string ToString()
PSChildName               NoteProperty   System.String PSChildName=addins
PSDrive                   NoteProperty   System.Management.Automation.PSDriveInfo PSDrive=C
PSIsContainer             NoteProperty   System.Boolean PSIsContainer=True
PSParentPath              NoteProperty   System.String PSParentPath=Microsoft.PowerShell.Core\FileSystem::C:\Windows
PSPath                    NoteProperty   System.String PSPath=Microsoft.PowerShell.Core\FileSystem::C:\Windows\addins
PSProvider                NoteProperty   System.Management.Automation.ProviderInfo PSProvider=Microsoft.PowerShell.Core\FileSystem
Attributes                Property       System.IO.FileAttributes Attributes {get;set;}
CreationTime              Property       datetime CreationTime {get;set;}
CreationTimeUtc           Property       datetime CreationTimeUtc {get;set;}
Exists                    Property       bool Exists {get;}
Extension                 Property       string Extension {get;}
FullName                  Property       string FullName {get;}
LastAccessTime            Property       datetime LastAccessTime {get;set;}
LastAccessTimeUtc         Property       datetime LastAccessTimeUtc {get;set;}
LastWriteTime             Property       datetime LastWriteTime {get;set;}
LastWriteTimeUtc          Property       datetime LastWriteTimeUtc {get;set;}
Name                      Property       string Name {get;}
Parent                    Property       System.IO.DirectoryInfo Parent {get;}
Root                      Property       System.IO.DirectoryInfo Root {get;}
BaseName                  ScriptProperty System.Object BaseName {get=$this.Name;}

That’s a lot of information, and something very promising:

PSPath     NoteProperty     System.String PSPath=Microsoft.PowerShell.Core\FileSystem::C:\Windows\addins

So let’s take a look at this:

PS C:\>Get-ChildItem-Path"C:\Windows"-Directory|Select-ObjectPSPath

PSPath
------
Microsoft.PowerShell.Core\FileSystem::C:\Windows\addins
Microsoft.PowerShell.Core\FileSystem::C:\Windows\AppCompat
Microsoft.PowerShell.Core\FileSystem::C:\Windows\apppatch
Microsoft.PowerShell.Core\FileSystem::C:\Windows\assembly
Microsoft.PowerShell.Core\FileSystem::C:\Windows\AUInstallAgent
Microsoft.PowerShell.Core\FileSystem::C:\Windows\Boot
Microsoft.PowerShell.Core\FileSystem::C:\Windows\CbsTemp
Microsoft.PowerShell.Core\FileSystem::C:\Windows\CCM

Well, that’s not quite what I was looking for.  Let’s look at some of the other properties. FullName looks promising.

PS C:\>Get-ChildItem-Path"C:\Windows"-Directory|Select-ObjectFullName

FullName
--------
C:\Windows\addins
C:\Windows\AppCompat
C:\Windows\apppatch
C:\Windows\assembly
C:\Windows\AUInstallAgent
C:\Windows\Boot
C:\Windows\CbsTemp
C:\Windows\CCM
C:\Windows\ccmcache

Well now.  That looks a lot better!

Let’s try another.

Get-CimInstance -ClassName Win32_NetworkAdapterConfiguration

Returns

ServiceName      DHCPEnabled    Index          Description
-----------      -----------    -----          -----------
Rasl2tp          False          0              WAN Miniport (L2TP)
RasSstp          False          1              WAN Miniport (SSTP)
RasAgileVpn      False          2              WAN Miniport (IKEv2)
PptpMiniport     False          3              WAN Miniport (PPTP)
RasPppoe         False          4              WAN Miniport (PPPOE)
NdisWan          False          5              WAN Miniport (IP)
NdisWan          False          6              WAN Miniport (IPv6)
NdisWan          False          7              WAN Miniport (Network Monitor)
kdnic            True           8              Microsoft Kernel Debug Network Adapter
AsyncMac         False          9              RAS Async Adapter
NETwNe64         True           10             Intel(R) Centrino(R) Ultimate-N 6300 AGN
e1iexpress       True           11             Intel(R) 82579LM Gigabit Network Conne...
BthPan           True           14             Bluetooth Device (Personal Area Network)
vwifimp          True           15             Microsoft Wi-Fi Direct Virtual Adapter
tunnel           False          16             Microsoft ISATAP Adapter
tunnel           False          17             Microsoft ISATAP Adapter
tunnel           False          18             Microsoft Teredo Tunneling Adapter
tunnel           False          19             Microsoft ISATAP Adapter
tunnel           False          20             Microsoft ISATAP Adapter
tunnel           False          21             Microsoft ISATAP Adapter
tunnel           False          22             Microsoft ISATAP Adapter
tunnel           False          23             Microsoft ISATAP Adapter
tunnel           False          24             Microsoft ISATAP Adapter

Good information, sure enough.  But it’s lacking in some key information that you might want to see, such as the IP Address.  So let’s take a look with the following command.

Get-CimInstance -ClassName Win32_NetworkAdapterConfiguration | Get-Member

The results of this are pretty long, so I’m not going to post it here.  But you’ll find a ton of information regarding the adapter configuration including the IP Address, IP Subnet, MAC Address, DNS information, and more.  So with a quick review, you can quickly craft a command to pull some real, pertinent data.  While we’re at it, I only want to see the adapters that have an actual IP Address as well.

PS C:\> Get-CimInstance -ClassName Win32_NetworkAdapterConfiguration | Where-Object IPAddress -NE $null | Select-Object Description,ServiceName,IPAddress,DNSServerSearchOrder

Description                         ServiceName     IPAddress            DNSServerSearchOrder              
-----------                         -----------     ---------            --------------------              
Intel(R) Centrino(R) Ultimate-N ... NETwNe64        {192.168.0.51}   {192.168.0.2, 192.168.0.3}

And there you have it!  Just remember, if you ever feel like you’re looking in the right place, but just can’t seem to find what you’re looking for, Get-Member should be the next place you visit.