Introduction

It has been a long wish of mine to create a PowerShell script that runs all the SCCM Client actions on a batch of computers. Of course, there are already tools available that do the trick, but not always in a convenient way. For example, you cannot run the action 'Application Global Evaluation Task' on remote computers. 

The requirements

There are some must-haves:

  • The script should be run against computer and user collections
  • The script should be run against computers that match a filter
  • No need to install all kinds of PowerShell modules to use this script
  • All the actions must be run, including those that can only run under user context
  • Avoid issues when AppLocker is in place.

The approach

It would be nice if the queries could be run against the SCCM / MECM database. But then, the user running the script must have SELECT rights on the database. If you do not have them, then the script is useless. So, I decided to use WMI queries instead. It is not the quickest way, but it is the best approach in this case.

WMI

Let's review this example: you entered a user-based collection name. 

The starting point are the CollectionType, CollectionID, Name and MemberClassName from the class SMS_Collection.

Get-CimInstance @WMIParam -ClassName SMS_Collection | Select-Object -Property CollectionType, CollectionID,Name,MemberClassName

CollectionType CollectionID Name                                          MemberClassName
-------------- ------------ ----                                          ---------------
             2 SMS00001     All Systems                                   SMS_CM_RES_COLL_SMS00001
             1 SMS00002     All Users                                     SMS_CM_RES_COLL_SMS00002
             1 SMS00003     All User Groups                               SMS_CM_RES_COLL_SMS00003
             1 SMS00004     All Users and User Groups                     SMS_CM_RES_COLL_SMS00004
             0 SMSOTHER     All Custom Resources                          SMS_CM_RES_COLL_SMSOTHER
             2 SMS000US     All Unknown Computers                         SMS_CM_RES_COLL_SMS000US
             2 SMS000PS     All Provisioning Devices                      SMS_CM_RES_COLL_SMS000PS
             2 SMSDM001     All Mobile Devices                            SMS_CM_RES_COLL_SMSDM001
             2 SMSDM003     All Desktop and Server Clients                SMS_CM_RES_COLL_SMSDM003
             2 TST00014     Managed Domain Computers                      SMS_CM_RES_COLL_TST00014
             1 TST00015     All Test User Accounts                        SMS_CM_RES_COLL_TST00015
             2 TST00016     Managed Domain Computers for Feature Releases SMS_CM_RES_COLL_TST00016
             2 SMS000KM     Co-management Eligible Devices                SMS_CM_RES_COLL_SMS000KM
             1 TST00017     Admins                                        SMS_CM_RES_COLL_TST00017
             1 TST00018     Remove Firefox                                SMS_CM_RES_COLL_TST00018
             1 TST00019     Demo - ProjectLibre                           SMS_CM_RES_COLL_TST00019
             1 TST0001A     Demo - NotepadPlusPlus                        SMS_CM_RES_COLL_TST0001A
             1 TST0001B     Demo - Adobe Acrobat DC Pro                   SMS_CM_RES_COLL_TST0001B

 
If the CollectionType is 1, then the collection is user-based. If the CollectionType is 2, then the collection is device-based.
But... a user-based collection can exist of users or groups. So, we have to know each member. That information is stored in the field SMSID of the class SMS_CM_RES_COLL_<CollectionID>. 
Each member can be a group resource or user resource.

 

Get-CimInstance @WMIParam -Class 'SMS_CM_RES_COLL_TST0001B' | Select-Object -Property SMSID

SMSID
-----
DEMO\Demo - Adobe Acrobat DC Pro

Get-CimInstance @WMIParam -Class 'SMS_CM_RES_COLL_TST0001A' | Select-Object -Property SMSID

SMSID
-----
DEMO\Test1
DEMO\Test4
DEMO\Test3
DEMO\Test5
DEMO\Test2
DEMO\Test6
DEMO\Test43

There is one (simple) way to find this: ADSI. Use the command:

([adsi]"WinNT://DEMO/Demo - Adobe Acrobat DC Pro").Properties.GroupType
2

 

([adsi]"WinNT://DEMO/Test1").Properties.GroupType

If the result is a group, we can find each user with the query: select SMS_R_User.UniqueUserName from SMS_R_User where SMS_R_User.UserGroupName LIKE '%Demo - Adobe Acrobat DC Pro'

 

Get-CimInstance @WMIParam -query "select SMS_R_User.UniqueUserName from SMS_R_User where SMS_R_User.UserGroupName LIKE '%Demo - Adobe Acrobat DC Pro'" | Select-Object -Property UniqueUserName

UniqueUserName
--------------
DEMO\Test1
DEMO\Test17
DEMO\Test93

Now, we have all the users we want to target.

Association user and computer

The next step is the association between the user and the computer. 

That can be done via the WMI query

Get-CimInstance @WMIParam -ClassName SMS_CombinedDeviceResources -Filter "Name = 'VM01-W10-22H2'" | Select-Object -Property Name, CurrentLogonUser,LastLogonUser

Name          CurrentLogonUser LastLogonUser
----          ---------------- -------------
VM01-W10-22H2 DEMO\Test7       defaultuser0

Or the other way around if you know the user:

Get-CimInstance @WMIParam -ClassName SMS_CombinedDeviceResources -Filter "CurrentLogonUser = 'DEMO\\Test7'" | Select-Object -Property Name, CurrentLogonUser,LastLogonUser

Name          CurrentLogonUser LastLogonUser
----          ---------------- -------------
VM01-W10-22H2 DEMO\Test7       defaultuser0

Now, we have all the computer names.

Performing all the SCCM Client Actions on each computer (in version v10)

There are a couple of options:

  1. The computer cannot be contacted
  2. The computer is on, but nobody is logged on.
  3. The computer has AppLocker policies assigned to it.
  4. The CurrentLogonUser is logged on.

Ad 1: If the computer is switched off, you will get an error that the computer cannot be reached via WinRM. If the computer name does not exist, nothing is done.

Ad 2: Only a limited set of policies will be refreshed:

  • Hardware Inventory
  • Machine Policy Assignments Request
  • Machine Policy Evaluation
  • Policy Agent Validate Machine Policy / Assignment
  • Application manager policy action

Ad 3: Also, only a limited set of policies will be refreshed

If the computer has AppLocker policies assigned to it, it is impossible to run a script in the user context. Almost with 100% certainty, the script will fail. So, the policies are refreshed under the system account.
If the following two registry keys are present, then AppLocker is effective.
HKLM:\SYSTEM\CurrentControlSet\Control\SRP\GP
HKLM:\SOFTWARE\Policies\Microsoft\Windows\SRPV2

Ad 4: The policies will be refreshed with a scheduled task that runs under the local user context

Creating a scheduled task is straightforward. But running it without a PowerShell window appearing is a bigger challenge. 
One of the options I found was:

mshta vbscript:Execute("CreateObject(""Wscript.Shell"").Run ""powershell -NoLogo -Command """"& 'C:\Example Path That Has Spaces\My Script.ps1'"""""", 0 : window.close")

Source: How to run a PowerShell script without displaying a window? - Stack Overflow

What happens? I received an email from the security officer about what I was doing. It appears that mshta.exe raises all kinds of red flags, so that is a no-go.

Then the alternative: a vbscript that starts the PowerShell script. That works. 

The following is done:
- A vbscript is created and stored under %ProgramData%\SCCMClientActions\PerformAllClientSCCMActions.vbs

On Error Resume Next
strCommand  = "powershell -NoLogo -NoProfile -Command " + chr(34) + "& {$TMPFolder = $($Env:TEMP); Start-Transcript -Path ""$TMPFolder\AllSCCMClientActions.txt""; (New-Object -Comobject CPApplet.CPAppletMgr).GetClientActions() | ForEach-Object {Write-host ""Performing action: $($_.Name)"";$_.PerformAction()}; Stop-Transcript}" + chr(34)
set oWShell = CreateObject("WScript.Shell")
intReturn   = oWShell.Run(strCommand, 0, true)
WScript.Quit intReturn

The scheduled task is created with the following details:

Program / Script: C:\WINDOWS\system32\wscript.exe
Add arguments:    /e:vbscript "C:\ProgramData\SCCMClientActions\PerformAllClientSCCMActions.vbs"

While running the scheduled task, a transcript is created and placed in the users' temp folder. 

**********************
Windows PowerShell transcript start
Start time: 20240525153414
Username: DEMO\test7
RunAs User: DEMO\test7
Configuration Name: 
Machine: VM01-W10-22H2 (Microsoft Windows NT 10.0.19045.0)
Host Application: C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -NoLogo -NoProfile -Command & {$TMPFolder = $($Env:TEMP); Start-Transcript -Path $TMPFolder\AllSCCMClientActions.txt; (New-Object -Comobject CPApplet.CPAppletMgr).GetClientActions() | ForEach-Object {Write-host Performing action: $($_.Name);$_.PerformAction()}; Stop-Transcript}
Process ID: 6520
PSVersion: 5.1.19041.4412
PSEdition: Desktop
PSCompatibleVersions: 1.0, 2.0, 3.0, 4.0, 5.0, 5.1.19041.4412
BuildVersion: 10.0.19041.4412
CLRVersion: 4.0.30319.42000
WSManStackVersion: 3.0
PSRemotingProtocolVersion: 2.3
SerializationVersion: 1.1.0.1
**********************
Transcript started, output file is C:\Users\test7\AppData\Local\Temp\AllSCCMClientActions.txt
Performing action: Software Metering Usage Report Cycle
Performing action: Request & Evaluate Machine Policy
Performing action: Updates Source Scan Cycle
Performing action: Request & Evaluate User Policy
Performing action: Software Inventory Collection Cycle
Performing action: Application Global Evaluation Task
Performing action: Software Updates Assignments Evaluation Cycle
Performing action: MSI Product Source Update Cycle
Performing action: Standard File Collection Cycle
**********************
Windows PowerShell transcript end
End time: 20240525153415
**********************

When the script has been run, that transcript file is read, and the information is stored in the array with the results. 

The end result on the client

It might look like the example below:

User DEMO\Test7 on VM01-W10-22H2 is a regular user. So, all the actions are done.

User DEMO\Test6 on VM02-W10-22H2 logged on on a device with AppLocker enabled. As a result, only the default machine policies are applied.

On the VM03-W10-22H2, nobody is logged on. So, only the default machine policies are applied.

Performing all the SCCM Client Actions on each computer (in version v11 and later)

In version v11, a new method of triggering the per-user deployments is used. Now, I use root\ccm\Policy\machine\ActualConfig for per-system deployments and root\ccm\Policy\<UsersSID>\ActualConfig for the per-user deployments. The class CCM_Scheduler_ScheduledMessage is checked for all the ScheduledMessageID starting with {0000. If that is the case, the action is triggered if the ScheduledMessageID is in a predefined array of actions. 

$WMIAction = ([wmi]"root\ccm\Policy\$PolicyPart\ActualConfig:CCM_Scheduler_ScheduledMessage.ScheduledMessageID='$($Action.ScheduledMessageID)'")
$WMIAction.Triggers=@('SimpleInterval;Minutes=1;MaxRandomDelayMinutes=0')
$WMIAction.Put()

The benefit is that there is no need to worry about bitlocker as the actions are performed under system context.

You can get the output of all the actions directly on screen or in a gridview.

Per-machine and per-user actions
Only the per-user actions.

And you see that in the PolicyAgentProvider:

PolicyAgentProvider.log

 

Get-Help

Get-Help "AllSCCMClientActionsOnComputers_v11.ps1" -Detailed shows the following information:

NAME
    C:\temp\Perform all SCCM Client Actions on remote computers\v11\AllSCCMClientActionsOnComputers_v11.ps1

SYNOPSIS
    Performs all the SCCM Client Actions on remote computers.

SYNTAX
    C:\temp\Perform all SCCM Client Actions on remote computers\v11\AllSCCMClientActionsOnComputers_v11.ps1 [-SCCMServe
    r <String>] [-SCCMSiteCode <String>] [-Groupname <String>] [-CollectionName <String>] [-ComputerNames <String[]>] [
    -ShowSummary] [-ShowOnlyErrors] [-OnlyMachinePolicies] [-OnlyUserPolicies] [-DetailedLogging] [-ResetPolicy <String
    >] [<CommonParameters>]

DESCRIPTION
    This script performs all the SCCM Client actions on remote computers. You can specify a bunch of computers
    or an AD group with users. The the device where the user is logged on will be targetted.

PARAMETERS
    -SCCMServer <String>
    -SCCMSiteCode <String>
    -Groupname <String>
    -CollectionName <String>
    -ComputerNames <String[]>
    -ShowSummary [<SwitchParameter>]
    -ShowOnlyErrors [<SwitchParameter>]
    -OnlyMachinePolicies [<SwitchParameter>]
    -OnlyUserPolicies [<SwitchParameter>]
    -DetailedLogging [<SwitchParameter>]
    -ResetPolicy <String>
    <CommonParameters>
        This cmdlet supports the common parameters: Verbose, Debug,
        ErrorAction, ErrorVariable, WarningAction, WarningVariable,
        OutBuffer, PipelineVariable, and OutVariable. For more information, see
        about_CommonParameters (https:/go.microsoft.com/fwlink/?LinkID=113216).

    -------------------------- EXAMPLE 1 --------------------------

    PS C:\>Performs the client sccm actions on the computers from the users in the given groupname.
    ."AllSCCMClientActionsOnComputers_v11.ps1" -SCCMServer sccm-servername -SCCMSiteCode tst -Groupname SomeGroupName

    -------------------------- EXAMPLE 2 --------------------------
    PS C:\>Performs the client sccm actions on the computers from the users in the given groupname. (with spaces in its
     name)
    ."AllSCCMClientActionsOnComputers_v11.ps1" -SCCMServer sccm-servername -SCCMSiteCode tst -Groupname 'Some - GroupNa
    meWithSpaces'

    -------------------------- EXAMPLE 3 --------------------------
    PS C:\>Performs the client sccm actions on the computers with the name pc-with-a-name and all the computer which name starts with vm-
    ."AllSCCMClientActionsOnComputers_v11.ps1" -SCCMServer sccm-servername -SCCMSiteCode tst -ComputerNamesForClientAct
    ions pc-with-a-name,vm-%

    -------------------------- EXAMPLE 4 --------------------------
    PS C:\>Performs the client sccm actions on the computers with are part of the collection 'User - Collection1'
    ."AllSCCMClientActionsOnComputers_v11.ps1" -SCCMServer sccm-servername -SCCMSiteCode tst -CollectionName 'User - Co
    llection1'

    -------------------------- EXAMPLE 5 --------------------------
    PS C:\>Performs the client sccm actions on the computers with are part of the collection 'DeviceCollection2'
    ."AllSCCMClientActionsOnComputers_v11.ps1" -SCCMServer sccm-servername -SCCMSiteCode tst -CollectionName DeviceColl
    ection2

    -------------------------- EXAMPLE 6 --------------------------

    PS C:\>Performs the client sccm actions on the computers with are part of the collection 'DeviceCollection2'  and o
    nly show the errors in the datagrid.
    ."AllSCCMClientActionsOnComputers_v11.ps1" -SCCMServer sccm-servername -SCCMSiteCode tst -CollectionName DeviceColl
    ection2 -ShowOnlyErrors

    -------------------------- EXAMPLE 7 --------------------------
    PS C:\>Performs the client sccm actions on the computers with are part of the collection 'DeviceCollection2' and re
    set the policy on the given computers
    ."AllSCCMClientActionsOnComputers_v11.ps1" -SCCMServer sccm-servername -SCCMSiteCode tst -CollectionName DeviceColl
    ection2 -ResetPolicy purge

    -------------------------- EXAMPLE 8 --------------------------
    PS C:\>Performs only the machine policy actions the client sccm actions on the computer with the name vm01-w10-22h2
    ."AllSCCMClientActionsOnComputers_v11.ps1" -Computernames vm01-w10-22h2 -OnlyMachinePolicies

    -------------------------- EXAMPLE 9 --------------------------
    PS C:\>Performs only the user policy actions the client sccm actions on the computer with the name vm01-w10-22h2
    ."AllSCCMClientActionsOnComputers_v11.ps1" -Computernames vm01-w10-22h2 -OnlyUserPolicies

REMARKS
    To see the examples, type: "get-help C:\temp\Perform all SCCM Client Actions on remote computers\v11\AllSCCMClientA
    ctionsOnComputers_v11.ps1 -examples".
    For more information, type: "get-help C:\temp\Perform all SCCM Client Actions on remote computers\v11\AllSCCMClient
    ActionsOnComputers_v11.ps1 -detailed".
    For technical information, type: "get-help C:\temp\Perform all SCCM Client Actions on remote computers\v11\AllSCCMC
    lientActionsOnComputers_v11.ps1 -full".

The script:

  <#
.SYNOPSIS
    Performs all the SCCM Client Actions on remote computers.

.DESCRIPTION
    This script performs all the SCCM Client actions on remote computers. You can specify a bunch of computers
    or an AD group with users. The the device where the user is logged on will be targetted.

.EXAMPLE
    Performs the client sccm actions on the computers from the users in the given groupname.
    ."AllSCCMClientActionsOnComputers_v11.ps1" -SCCMServer sccm-servername -SCCMSiteCode tst -Groupname SomeGroupName

.EXAMPLE
    Performs the client sccm actions on the computers from the users in the given groupname. (with spaces in its name)
    ."AllSCCMClientActionsOnComputers_v11.ps1" -SCCMServer sccm-servername -SCCMSiteCode tst -Groupname 'Some - GroupNameWithSpaces'

.EXAMPLE
    Performs the client sccm actions on the computers with the name pc-with-a-name and all the computer which name starts with vm-
    ."AllSCCMClientActionsOnComputers_v11.ps1" -SCCMServer sccm-servername -SCCMSiteCode tst -ComputerNamesForClientActions pc-with-a-name,vm-%

.EXAMPLE
    Performs the client sccm actions on the computers with are part of the collection 'User - Collection1'
    ."AllSCCMClientActionsOnComputers_v11.ps1" -SCCMServer sccm-servername -SCCMSiteCode tst -CollectionName 'User - Collection1'

.EXAMPLE
    Performs the client sccm actions on the computers with are part of the collection 'DeviceCollection2'
    ."AllSCCMClientActionsOnComputers_v11.ps1" -SCCMServer sccm-servername -SCCMSiteCode tst -CollectionName DeviceCollection2

.EXAMPLE
    Performs the client sccm actions on the computers with are part of the collection 'DeviceCollection2'  and only show the errors in the datagrid.
    ."AllSCCMClientActionsOnComputers_v11.ps1" -SCCMServer sccm-servername -SCCMSiteCode tst -CollectionName DeviceCollection2 -ShowOnlyErrors

.EXAMPLE
    Performs the client sccm actions on the computers with are part of the collection 'DeviceCollection2' and reset the policy on the given computers
    ."AllSCCMClientActionsOnComputers_v11.ps1" -SCCMServer sccm-servername -SCCMSiteCode tst -CollectionName DeviceCollection2 -ResetPolicy purge

.EXAMPLE
    Performs only the machine policy actions the client sccm actions on the computer with the name vm01-w10-22h2
    ."AllSCCMClientActionsOnComputers_v11.ps1" -Computernames vm01-w10-22h2 -OnlyMachinePolicies

.EXAMPLE
    Performs only the user policy actions the client sccm actions on the computer with the name vm01-w10-22h2
    ."AllSCCMClientActionsOnComputers_v11.ps1" -Computernames vm01-w10-22h2 -OnlyUserPolicies

.NOTES
    Author:  Willem-Jan Vroom

v0.1:
   * Initial version

v0.2
   * Improved layout
   * Command line options.

v0.3:
   * Improved layout
   * Support for machines with AppLocker
   * Also user policies are supported by using a scheduled task.

v1.0:
   * Final version.

v1.1:
   * Added parameter -ShowSummary to show the summary in the end.
   * No longer the Scheduled action to do all the client actions.
   * AppLocker restrictions have been removed.
   * No use of scheduled tasks to run the user actions.
  #> 

[CmdletBinding(DefaultParameterSetName = 'Default')]

Param
 (
   [Parameter(HelpMessage='Servername SCCM / MECM Site server.')]
   [Parameter(ParameterSetName = 'Default', Mandatory=$True)]    [String]  $SCCMServer,

   [Parameter(HelpMessage='SCCM / MECM Site code.')]
   [Parameter(ParameterSetName = 'Default', Mandatory=$True)]    [String]  $SCCMSiteCode,

   [Parameter(HelpMessage ='Give the groupname (with single quotes if the groupname contains a space)')]
   [Parameter(Mandatory   = $False, ParameterSetName='Default')] [String]   $Groupname,

   [Parameter(HelpMessage ='Give the collectioname (with single quotes if the collectionname contains a space)')]
   [Parameter(Mandatory   = $False, ParameterSetName='Default')] [String]   $CollectionName,

   [Parameter(HelpMessage ='Enter the computername(s) to apply all the SCCM Client Actions on. Use a comma between each computername.')]
   [Parameter(Mandatory   = $False, ParameterSetName='Default')] [String[]] $ComputerNames,

   [Parameter(HelpMessage ='Show the summary in the end.')]
   [Parameter(Mandatory   = $False, ParameterSetName='Default')] [Switch] $ShowSummary,

   [Parameter(HelpMessage ='Show only the errors in the datagrid in the end.')]
   [Parameter(Mandatory   = $False, ParameterSetName='Default')] [Switch] $ShowOnlyErrors,

   [Parameter(HelpMessage ='Perform only the machine policy actions.')]
   [Parameter(Mandatory   = $False, ParameterSetName='Default')] [Switch] $OnlyMachinePolicies,

   [Parameter(HelpMessage ='Perform only the user policy actions.')]
   [Parameter(Mandatory   = $False, ParameterSetName='Default')] [Switch] $OnlyUserPolicies,

   [Parameter(HelpMessage='Enable detailed logging to a log file.')]
   [Parameter(Mandatory   = $False, ParameterSetName='Default')] [Switch] $DetailedLogging,

   [Parameter(HelpMessage='Perform a policy reset.')]
   [ValidateSet("none","full","purge")]
   [Parameter(Mandatory   = $False, ParameterSetName='Default')] [String] $ResetPolicy = "none"
 )

# =============================================================================================================================================
# script block
# =============================================================================================================================================

$ScriptBlock = {

  Param
   (
    [String]   $User,
    [String]   $FullName,
    [String]   $ResetPolicy,
    [String]   $OnlyMachinePolicies,
    [String]   $OnlyUserPolicies,
    [String]   $UserSID
   )

Function Convert-SSCMGuidToActionName
 {
  
  # Source: https://learn.microsoft.com/en-us/mem/configmgr/develop/reference/core/clients/client-classes/triggerschedule-method-in-class-sms_client
  
  Param
   (
   [Parameter(HelpMessage='Enter the GUID.')]
   [Parameter(Mandatory=$True, ParameterSetName='Optional')] [String] $GUIDForAction
   )

   Switch ($GUIDForAction)
   {
   "{00000000-0000-0000-0000-000000000001}" {$ScheduleName = "Hardware Inventory"}
   "{00000000-0000-0000-0000-000000000002}" {$ScheduleName = "Software Inventory"}
   "{00000000-0000-0000-0000-000000000003}" {$ScheduleName = "Data Discovery Record"}
   "{00000000-0000-0000-0000-000000000010}" {$ScheduleName = "File Collection"}
   "{00000000-0000-0000-0000-000000000011}" {$ScheduleName = "IDMIF Collection"}
   "{00000000-0000-0000-0000-000000000012}" {$ScheduleName = "Client Machine Authentication"}
   "{00000000-0000-0000-0000-000000000021}" {$ScheduleName = "Machine Policy Assignments Request"}
   "{00000000-0000-0000-0000-000000000022}" {$ScheduleName = "Machine Policy Evaluation"}
   "{00000000-0000-0000-0000-000000000023}" {$ScheduleName = "Refresh Default MP Task"}
   "{00000000-0000-0000-0000-000000000024}" {$ScheduleName = "LS (Location Service) Refresh Locations Task"}
   "{00000000-0000-0000-0000-000000000025}" {$ScheduleName = "LS (Location Service) Timeout Refresh Task"}
   "{00000000-0000-0000-0000-000000000026}" {$ScheduleName = "Policy Agent Request Assignment (User)"}
   "{00000000-0000-0000-0000-000000000027}" {$ScheduleName = "Policy Agent Evaluate Assignment (User)"}
   "{00000000-0000-0000-0000-000000000031}" {$ScheduleName = "Software Metering Generating Usage Report"}
   "{00000000-0000-0000-0000-000000000032}" {$ScheduleName = "Source Update Message"}
   "{00000000-0000-0000-0000-000000000037}" {$ScheduleName = "Clearing proxy settings cache"}
   "{00000000-0000-0000-0000-000000000040}" {$ScheduleName = "Machine Policy Agent Cleanup"}
   "{00000000-0000-0000-0000-000000000041}" {$ScheduleName = "User Policy Agent Cleanup"}
   "{00000000-0000-0000-0000-000000000042}" {$ScheduleName = "Policy Agent Validate Machine Policy / Assignment"}
   "{00000000-0000-0000-0000-000000000043}" {$ScheduleName = "Policy Agent Validate User Policy / Assignment"}
   "{00000000-0000-0000-0000-000000000051}" {$ScheduleName = "Retrying/Refreshing certificates in AD on MP"}
   "{00000000-0000-0000-0000-000000000061}" {$ScheduleName = "Peer DP Status reporting"}
   "{00000000-0000-0000-0000-000000000062}" {$ScheduleName = "Peer DP Pending package check schedule"}
   "{00000000-0000-0000-0000-000000000063}" {$ScheduleName = "SUM Updates install schedule"}
   "{00000000-0000-0000-0000-000000000101}" {$ScheduleName = "Hardware Inventory Collection Cycle"}
   "{00000000-0000-0000-0000-000000000102}" {$ScheduleName = "Software Inventory Collection Cycle"}
   "{00000000-0000-0000-0000-000000000103}" {$ScheduleName = "Discovery Data Collection Cycle"}
   "{00000000-0000-0000-0000-000000000104}" {$ScheduleName = "File Collection Cycle"}
   "{00000000-0000-0000-0000-000000000105}" {$ScheduleName = "IDMIF Collection Cycle"}
   "{00000000-0000-0000-0000-000000000106}" {$ScheduleName = "Software Metering Usage Report Cycle"}
   "{00000000-0000-0000-0000-000000000107}" {$ScheduleName = "Windows Installer Source List Update Cycle"}
   "{00000000-0000-0000-0000-000000000108}" {$ScheduleName = "Software Updates Assignments Evaluation Cycle"}
   "{00000000-0000-0000-0000-000000000109}" {$ScheduleName = "Branch Distribution Point Maintenance Task"}
   "{00000000-0000-0000-0000-000000000111}" {$ScheduleName = "Send Unsent State Message"}
   "{00000000-0000-0000-0000-000000000112}" {$ScheduleName = "State System policy cache cleanout"}
   "{00000000-0000-0000-0000-000000000113}" {$ScheduleName = "Scan by Update Source"}
   "{00000000-0000-0000-0000-000000000114}" {$ScheduleName = "Update Store Policy"}
   "{00000000-0000-0000-0000-000000000115}" {$ScheduleName = "State system policy bulk send high"}
   "{00000000-0000-0000-0000-000000000116}" {$ScheduleName = "State system policy bulk send low"}
   "{00000000-0000-0000-0000-000000000121}" {$ScheduleName = "Application manager policy action"}
   "{00000000-0000-0000-0000-000000000122}" {$ScheduleName = "Application manager user policy action"}
   "{00000000-0000-0000-0000-000000000123}" {$ScheduleName = "Application manager global evaluation action"}
   "{00000000-0000-0000-0000-000000000131}" {$ScheduleName = "Power management start summarizer"}
   "{00000000-0000-0000-0000-000000000221}" {$ScheduleName = "Endpoint deployment reevaluate"}
   "{00000000-0000-0000-0000-000000000222}" {$ScheduleName = "Endpoint AM policy reevaluate"}
   "{00000000-0000-0000-0000-000000000223}" {$ScheduleName = "External event detection"}
   default {$ScheduleName = "Ohoh.... something went wrong here..."}
   }

   Return $ScheduleName
 }

  $PolicyResetDone            = "-"
  $ArrayWithActions           = @()
  $WMIParam                   = @{"Class"     = "SMS_Client"
                                  "Namespace" = "root\ccm"}
  If ($ResetPolicy -ne "none")
   {
    $ActionName = "-- Performing reset policy. --"
    If ($ResetPolicy -eq "full") {$uFlag = 0} else {$uFlag = 1}
    Try
     {
      (Get-WmiObject @WMIParam -List).ResetPolicy($uFlag)
      $Result = "Ok"
     }
      Catch
     {
      $Result = "Error"
      $ErrorMessage = "[$($_.Exception.Message)] Type [$($_.Exception.GetType().FullName)]"
     }
      Finally
     {
      $Record = [Ordered]@{"User logon name"= $User
                           "Fullname"   = $FullName
                           "Computer"   = $env:COMPUTERNAME
                           "Remark"     = "Policy reset [$ResetPolicy] -> [$Result]"
                           "Error"      = $ErrorMessage}
      $ArrayWithActions += New-Object -TypeName PSObject -Property $Record
     }
   }
  
  $ErrorMessage                 = ""
  $ConditionNoUser              = (-not $User)
  $ConditionOnlyMachinePolicies = $OnlyMachinePolicies -eq "yes"
  $ConditionOnlyUserPolicies    = $OnlyUserPolicies    -eq "yes"
  $Result                       = ""
  [String[]]$RemarkText         = ""


  $ClientActions = @("{00000000-0000-0000-0000-000000000001}",
                     "{00000000-0000-0000-0000-000000000002}",
                     "{00000000-0000-0000-0000-000000000010}",
                     "{00000000-0000-0000-0000-000000000021}",
                     "{00000000-0000-0000-0000-000000000022}",
                     "{00000000-0000-0000-0000-000000000024}",
                     "{00000000-0000-0000-0000-000000000025}",
                     "{00000000-0000-0000-0000-000000000026}",
                     "{00000000-0000-0000-0000-000000000027}",
                     "{00000000-0000-0000-0000-000000000032}",
                     "{00000000-0000-0000-0000-000000000040}",
                     "{00000000-0000-0000-0000-000000000041}",
                     "{00000000-0000-0000-0000-000000000042}",
                     "{00000000-0000-0000-0000-000000000043}",
                     "{00000000-0000-0000-0000-000000000111}",
                     "{00000000-0000-0000-0000-000000000112}",
                     "{00000000-0000-0000-0000-000000000113}",
                     "{00000000-0000-0000-0000-000000000114}",
                     "{00000000-0000-0000-0000-000000000121}",
                     "{00000000-0000-0000-0000-000000000122}")

  If ($ConditionNoUser)                                   
    {$RemarkText += "No user logged on."}
  If (($ConditionOnlyMachinePolicies -or $ConditionNoUser) -and (-not $ConditionOnlyUserPolicies))
    {$RemarkText += "Only the default machine actions are triggered."}
  If ($ConditionOnlyUserPolicies) 
    {$RemarkText += "Only the user policy actions are triggered."}

  $PolicyParts      = @()
  If (-not $ConditionOnlyUserPolicies) 
    {$PolicyParts += "Machine"}
  If ((-not $ConditionOnlyMachinePolicies) -and ($UserSID -ne "-")) 
    {$UserSID     = $UserSID.Replace("-","_")
     $PolicyParts +=$UserSID}
  
  If (($PolicyParts | Measure-Object).Count -eq 0)
   {
    $Record = [Ordered]@{"User logon name"= $User
                         "Fullname"       = $FullName
                         "Computer"       = $env:COMPUTERNAME
                         "Actionname"     = ""
                         "Scope"          = $Scope 
                         "Remark"         = ($RemarkText -Join " ").Trim()
                         "Error"          = $ErrorMessage}
    $ArrayWithActions += New-Object -TypeName PSObject -Property $Record 
   }
    else
   {
    ForEach ($PolicyPart in $PolicyParts)
     {
      $Namespace        = "root\ccm\Policy\$PolicyPart\ActualConfig"
      If ($PolicyPart -eq "Machine") {$Scope = $PolicyPart} else {$Scope = "User"}
      $Actions          = Get-CimInstance -ClassName CCM_Scheduler_ScheduledMessage -Namespace $Namespace -Filter "ScheduledMessageID like '{0000%'"  | Select-Object -Property ScheduledMessageID
      ForEach ($Action in $Actions)
       {
        $ErrorMessage = $Null
        If ($($Action.ScheduledMessageID) -in $ClientActions)
         {
          Try
           {
            $WMIAction = ([wmi]"root\ccm\Policy\$PolicyPart\ActualConfig:CCM_Scheduler_ScheduledMessage.ScheduledMessageID='$($Action.ScheduledMessageID)'")
            $WMIAction.Triggers=@('SimpleInterval;Minutes=1;MaxRandomDelayMinutes=0')
            $WMIAction.Put()
           }
            Catch
           {
            $ErrorMessage = "[$($_.Exception.Message)] Type [$($_.Exception.GetType().FullName)]"
           }
            Finally
           {
            $Record = [Ordered]@{"User logon name" = $User
                                 "Fullname"        = $FullName
                                 "Computer"        = $env:COMPUTERNAME
                                 "Actionname"      = Convert-SSCMGuidToActionName -GUIDForAction $($Action.ScheduledMessageID)
                                 "Scope"           = $Scope 
                                 "Remark"          = ($RemarkText -Join " ").Trim()
                                 "Error"           = $ErrorMessage}
            $ArrayWithActions += New-Object -TypeName PSObject -Property $Record
           }
         }
       }
     } 
   }

  Return  $ArrayWithActions 
} 

# =============================================================================================================================================
# End script block
# =============================================================================================================================================

# =============================================================================================================================================
# Function block
# =============================================================================================================================================

  Function Add-EntryToLogFile
   {

    <#
    .NOTES
    =============================================================================================================================================
    Created with:     Windows PowerShell ISE
    Created on:       17-May-2020 / Modified 09-May-2022: Includes the function name
    Created by:       Willem-Jan Vroom
    Organization:     
    Functionname:     Add-EntryToLogFile
    =============================================================================================================================================
    .SYNOPSIS

    This function adds a line to a log file

    #>

    Param
     (
      [Parameter(Mandatory=$True)]  [string] $Entry,
      [Parameter(Mandatory=$False)] [String] $FunctionName
     )
      
     Write-Verbose "[Function: $FunctionName] - $Entry"
     If ($Global:gblDetailedLogging -and $Global:gblLogFile)
      {
       $Timestamp = (Get-Date -format "yyyy-MM-dd HH-mm-ss").ToString()
       Add-Content $Global:gblLogFile -Value $($Timestamp + "[Function: $FunctionName] - $Entry") -Force -ErrorAction SilentlyContinue
      }
   }

  Function Add-RecordToLogFile
   {

    <#
    .NOTES
    =============================================================================================================================================
    Created with:     Windows PowerShell ISE
    Created on:       17-May-2020 / Modified 09-May-2022: Includes the function name
    Created by:       Willem-Jan Vroom
    Organization:     
    Functionname:     Add-RecordToLogFile
    =============================================================================================================================================
    .SYNOPSIS

    This function adds a record to the log file

    #>

    Param
     (
      [Parameter(Mandatory=$True)] $Record
     )      

    Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry ">>> The variable 'Record' has as type: $($Record.GetType().Name)."  

    If ($($Record.GetType().Name) -eq "Object[]" -or $($Record.GetType().Name) -eq "CimInstance")
     {$Headers              = ([array]$Record | Get-Member -MemberType Properties).Name}
     else
     {$Headers              = @([PSCustomObject]$Record.psobject.properties |Where-Object {$_.Name -eq 'Keys'}).Value}
    Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "The following information is gathered:"
    ForEach ($Item in $Record)
     {
      ForEach ($Header in $Headers)
       {
        Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "$Header --> $($Item.$Header)"
       }
      Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "- - - - - - - - - - - - - - - - - - - - - - - - - - -"
     }
   }

  Function Get-ADUserName
   {

    <#
    .NOTES
    =============================================================================================================================================
    Created with:     Windows PowerShell ISE
    Created on:       27-April 2024
    Created by:       Willem-Jan Vroom
    Organization:     
    Functionname:     Get-ADUserName
    =============================================================================================================================================
    .SYNOPSIS

    This function converts the 'domain\user' to the users' name.

    #>

    Param
     (
      [Parameter(Mandatory=$True)][String] $UsernameInDomainUserFormat
     )

    $ReturnValue = $Null

    If (($UsernameInDomainUserFormat).Contains("\"))
     {
      $Domain      = ($UsernameInDomainUserFormat.Split("\"))[0]
      $User        = ($UsernameInDomainUserFormat.Split("\"))[1]  
      $ReturnValue = ([adsi]"WinNT://$Domain/$User,user").fullname
     }
      else
     {
      $ReturnValue = $UsernameInDomainUserFormat
     }
    Return $ReturnValue
   }

  Function Get-ADUserSID
   {

    <#
    .NOTES
    =============================================================================================================================================
    Created with:     Windows PowerShell ISE
    Created on:       27-April 2024
    Created by:       Willem-Jan Vroom
    Organization:     
    Functionname:     Get-ADUserSID
    =============================================================================================================================================
    .SYNOPSIS

    This function converts the 'domain\user' to the users' SID.

    #>

    Param
     (
      [Parameter(Mandatory=$True)][String] $UsernameInDomainUserFormat
     )

    $ReturnValue = $Null

    If (($UsernameInDomainUserFormat).Contains("\"))
     {
     $Domain      = ($UsernameInDomainUserFormat.Split("\"))[0]
      $User        = ($UsernameInDomainUserFormat.Split("\"))[1] 
      
      $strSID      = (New-Object System.Security.Principal.NTAccount($Domain, $User)).Translate([System.Security.Principal.SecurityIdentifier])
      $ReturnValue = $strSID.Value 
     }
      else
     {
      $ReturnValue = $UsernameInDomainUserFormat
     }
    
    Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "The user [$UsernameInDomainUserFormat] has SID [$ReturnValue]." 
    Return $ReturnValue
   }

  Function Run-ClientSCCMActionsOnRemoteComputers
   {

    <#
    .NOTES
    =============================================================================================================================================
    Created with:     Windows PowerShell ISE
    Created on:       27-April 2024
    Created by:       Willem-Jan Vroom
    Organization:     
    Functionname:     Run-ClientSCCMActionsOnRemoteComputers
    =============================================================================================================================================
    .SYNOPSIS

    This function starts the SCCM on the remote computers.

    #>

    Param
     (
      [Parameter(Mandatory=$True)][String] $WMIClass,
      [Parameter(Mandatory=$True)][String] $WMIFilter,
      [Parameter(Mandatory=$True)]         $WMIParam
     )

    $ArrayWithTheResults = @()

    Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "Running query: [Select * From $WMIClass Where $WMIFilter]"
    $Computers = @(Get-CimInstance @WMIParam -Class $WMIClass -Filter $WMIFilter | Select-Object -Property Name,CurrentLogonUser) | Sort-Object -Property Name
    ForEach ($Computer in $Computers)
     {
     If($($Computer.CurrentLogonUser)) 
      {$User   = "$($Computer.CurrentLogonUser)";$FullName = Get-ADUserName -UsernameInDomainUserFormat $($Computer.CurrentLogonUser)}
      else
      {$User = $Null;$FullName = "-"}

     Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "Discovered userid on $($Computer.Name)   : $User"
     Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "Discovered fullname on $($Computer.Name) : $FullName"

     Try 
      {
       $Result              = $Null
       $ResetPolicy         = $Global:ResetPolicy
       If ($($Global:OnlyMachinePolicies.IsPresent)) {$OnlyMachinePolicies = "yes"} else {$OnlyMachinePolicies = $Null}
       If ($($Global:OnlyUserPolicies.IsPresent))    {$OnlyUserPolicies    = "yes"} else {$OnlyUserPolicies    = $null}
       If ($User)
        {$UserSID             = Get-ADUserSID -UsernameInDomainUserFormat $User}
         else
        {$UserSID = "-"}
       Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "Performing the actions on the computer [$($Computer.Name)]." 
       $Result = Invoke-Command -ComputerName $Computer.Name -ScriptBlock $ScriptBlock -ArgumentList $User,$FullName,$ResetPolicy,$OnlyMachinePolicies,$OnlyUserPolicies,$UserSID -ErrorAction Stop
      }
       Catch [System.Management.Automation.Remoting.PSRemotingTransportException]
      {
       $ErrorMessage = "The host [$($Computer.Name)] cannot be reached via PSRemoting (WinRM)"
       Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "Error [$ErrorMessage]"    
      }
       Catch
      {
       $ErrorMessage = "Host [$($Computer.Name)]: [$($_.Exception.Message)] Type [$($_.Exception.GetType().FullName)]"
       Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "Error [$ErrorMessage]"  
      }
       Finally
      {
       If (-not $Result)
        {
         $Record = [Ordered]@{"Userdetails"= $User
                              "Fullname"   = $FullName
                              "Computer"   = $Computer.Name
                              "Actionname" = ""
                              "Error"      = $ErrorMessage}
         $Result = New-Object -TypeName PSObject -Property $Record
        }
       $ArrayWithTheResults += $Result
       Add-RecordToLogFile -Record $Result
       }
     }
     Return $ArrayWithTheResults
   } 

# =============================================================================================================================================
# End function block
# =============================================================================================================================================

$WMIParam         = @{ComputerName = $SCCMServer
                      NameSpace    = "root\sms\site_$SCCMSiteCode"}

$strCurrentDir                  = Split-Path -parent $MyInvocation.MyCommand.Definition
$Global:gblLogPath              = $strCurrentDir
$Global:gblDetailedLogging      = $DetailedLogging
$DateTime                       = (Get-Date -format "yyyy-MM-dd_HH-mm-ss").ToString()
$Global:gblLogFile              = "$($Global:gblLogPath)\$($Env:USERNAME)_$($DateTime)_AllSCCMClientActionsOnRemoteComputers.log"
$Global:ResetPolicy             = $ResetPolicy
$Global:OnlyMachinePolicies     = $OnlyMachinePolicies
$Global:OnlyUserPolicies        = $OnlyUserPolicies

If ($Global:gblDetailedLogging)
 {
  New-Item $Global:gblLogFile -ItemType File -Force | Out-Null
 }

Clear-Host

If (-not $Groupname -and -not $ComputerNames -and -not $CollectionName)
 {
  $ErrorMessage = "One of the parameters Groupname, CollectionName or ComputerNameForClientActions should be mentioned."
  Write-Warning $ErrorMessage
  Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry $ErrorMessage
  Exit 999
 }

$ArrayWithAllTheActionResults = @()

If ($CollectionName)
 {
  Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "Collection details are mentioned..."
  $Query             = "SELECT CollectionType,CollectionID,Name,MemberClassName From SMS_Collection Where Name = '$CollectionName'"
  Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "Running query: [$Query]"
  $CollectionDetails = Get-CimInstance @WMIParam -Query $Query| Select-Object -Property CollectionType,CollectionID,Name,MemberClassName
  If ($CollectionDetails.CollectionType -eq 1)  # User collection
   {
    Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "User collection...."
    $WMIClass        = $CollectionDetails.MemberClassName
    $Query           = "SELECT SMSID From $WMIClass"
    Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "Running query: [$Query]"
    $UserOrGroupName = @(Get-CimInstance @WMIParam -Query $Query | Select-Object -Property SMSID)
    If ($UserOrGroupName.Count -eq 1)
     {
      $UserDomain      = $($UserOrGroupName.SMSID).Split("\")[0]
      $UserName        = $($UserOrGroupName.SMSID).Split("\")[1]
     }
      else
     {
      $UserDomain      = $($UserOrGroupName.SMSID[-1]).Split("\")[0]
      $UserName        = $($UserOrGroupName.SMSID[-1]).Split("\")[1]
     }
    If (([adsi]"WinNT://$UserDomain/$UserName").Properties.GroupType) # Check if it is a group or user. 
     {
      $Groupname = $UserOrGroupName.SMSID
     }
      else
     {
      $WMIClass           = "SMS_CombinedDeviceResources"
      [int32]$Counter     = 1
      [int32]$TotalItems  = $UserOrGroupName.Count
      ForEach ($Item in $UserOrGroupName)
       {
        Write-Progress -Id 1 -Activity "Going through the users of the collection [$CollectionName]." -Status "Processing user [$($Item.SMSID) - $(Get-ADUserName -UsernameInDomainUserFormat $($Item.SMSID))]" -PercentComplete ($Counter / $TotalItems * 100)
        $Counter ++
        $WMIFilter = "CurrentLogonUser = '$($($Item.SMSID).Replace('\','\\'))'"
        $ArrayWithAllTheActionResults += @(Run-ClientSCCMActionsOnRemoteComputers -WMIClass $WMIClass -WMIFilter $WMIFilter -WMIParam $WMIParam)
       }
     }
   }
  ElseIf ($CollectionDetails.CollectionType -eq 2) # DeviceCollection
   {
    Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "Device collection...."
    $WMIClass   = $CollectionDetails.MemberClassName
    $Query      = "SELECT Name From $WMIClass"
    Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "Running query: [$Query]"
    $ComputerNames = @(Get-CimInstance @WMIParam -Query $Query | Select-Object -Property Name).Name
   }
 }

If ($Groupname)
 {
  Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "Running the query....."
  $GroupName       = $GroupName.Replace("\","\\")
  $Query           = "select SMS_R_User.UniqueUserName from SMS_R_User where SMS_R_User.UserGroupName LIKE '%$GroupName'"
  Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry "Running query: [$Query]"
  $UniqueUserNames = @(Get-WMIObject @WMIParam -Query $Query | Select-Object -Property UniqueUserName)
  [Int32] $UniqueUserNamesCount = $UniqueUserNames.Count
  [Int32] $Counter = 1 
  $WMIClass        = "SMS_CombinedDeviceResources"
  ForEach ($UniqueUserName in $UniqueUserNames)
   {
    Write-Progress -Id 1 -Activity "Going through the users of group [$Groupname]." -Status "Processing user [$($UniqueUserName.UniqueUserName) - $(Get-ADUserName -UsernameInDomainUserFormat $($UniqueUserName.UniqueUserName))]" -PercentComplete ($Counter / $UniqueUserNamesCount * 100)
    $Counter++
    $WMIFilter = "CurrentLogonUser = '$($($UniqueUserName.UniqueUserName).Replace('\','\\'))'"
    $ArrayWithAllTheActionResults += @(Run-ClientSCCMActionsOnRemoteComputers -WMIClass $WMIClass -WMIFilter $WMIFilter -WMIParam $WMIParam)
   } 
 }
  else
 {
  [int32]$Counter     = 1    
  [int32]$TotalItems  = $ComputerNames.Count
  ForEach ($ComputerNameForClientActions in $ComputerNames)
   {
    Write-Progress -Id 1 -Activity "Going through the computers." -Status "Processing computers that match [$ComputerNameForClientActions]" -PercentComplete ($Counter / $TotalItems * 100)
    $Counter++
    If ($ComputerNameForClientActions.Contains('%')) {$Operator = 'like'} else {$Operator = '='}
    $WMIClass         = "SMS_CombinedDeviceResources"
    $WMIFilter        = "Name $Operator '$ComputerNameForClientActions'"
    $ArrayWithAllTheActionResults += @(Run-ClientSCCMActionsOnRemoteComputers -WMIClass $WMIClass -WMIFilter $WMIFilter -WMIParam $WMIParam)
   }
 }

 $ArrayWithAllTheActionResults = $ArrayWithAllTheActionResults | Where-Object {$_.Computer -ne $Null} | Select-Object -Property "User logon name",FullName,Computer,Actionname,Scope,Remark,Error | Sort-Object -Property Computer,Actionname
 If (($ArrayWithAllTheActionResults | Measure-Object).Count -gt 0)
  {
   $Title = "Results of performing SCCM Client Actions on computers."
   If ($ShowOnlyErrors)
    {
     If ($ShowSummary.IsPresent)
      { 
       $ArrayWithAllTheActionResults | Where-Object {$_.Error} | Sort-Object -Property Computer,Scope,ActionName | Out-GridView -Title "Results of performing SCCM Client Actions on computers."
      }
       else
      {
       $ArrayWithAllTheActionResults | Where-Object {$_.Error} | Sort-Object -Property Computer,Scope,ActionName | Format-Table -GroupBy Computer -Property FullName,ActionName,Scope,Remark,Error
      }
    }
     else
    {
     If ($ShowSummary.IsPresent)
      {
       $ArrayWithAllTheActionResults | Sort-Object -Property Computer,Scope,ActionName | Out-GridView -Title "Results of performing SCCM Client Actions on computers."
      }
       else
      {
       $ArrayWithAllTheActionResults | Sort-Object -Property Computer,Scope,ActionName | Format-Table -GroupBy Computer -Property FullName,ActionName,Scope,Remark,Error
      }
    }
  }
   else
  {
   $ErrorMessage = "There is nothing to report......"
   Add-EntryToLogFile -FunctionName $($MyInvocation.MyCommand.Name) -Entry $ErrorMessage
   Write-Host $ErrorMessage
  }



After extracting the ZIP file, please 'unblock' the Powershell script and/or the batch file. 

Unblock
Unblock