Serge Chegorian's System Center Blog

Serge Chegorian's System Center Blog

[PS]: Running parallel processes in PowerShell

October 15th, 2020

Many of you were in the situation when you have to run the same routine against multiple end points. If you run them in sequence, the process might take very long time depending on the routine complexity and the network performance. The wiser solution is to run all routines in parallel and than to collate data. The following cmdlets will help you:

Start-Job
Get-Job
Receive-Job
Remove-Job

So what each of them does?

Start-Job -ScriptBlock $scriptBlock -ArgumentList($ArgumentList)

Start-Job has two main parameters: ScriptBlock and ArgumentList. ScriptBlock is the main piece of code of your routine. This is what will be executed independently using ArgumentList as a parameter.

Get-Job gets the list of active jobs created by Start-Job and their status which could be ‘running’, ‘completed’ or ‘failed’.

Receive-Job uses Job ID as a parameter and gets all output from the $job created by Start-Job. This is important because Receive-Job simply returns the content of $job buffer. Whatever you put there using Write-Host, return or any other function will be returned as unindexed block of data.

Remove-Job uses $job handler or $job ID as a parameter and deletes the job. Remember that the job is not deleted automatically once it is completed and job results are sitting in the buffer until the job is deleted by Remove-Job cmdlet.

So the structure of your script should be as follows:

#Script Block
$scriptBlock = {
param ($MyParam)
function MyFunction
{
            return $Return
}
            MyFunction -MyParam $MyParam
}
#Starting jobs with variable parameter
for ($k=0;$k -lt $Something; $k++){
$job = Start-Job -ScriptBlock $scriptBlock -ArgumentList(MyParam)
}
# Checking jobs status. If at least one is running waiting for 10 seconds and checking again
While (Get-Job -State "Running") { 
$iScriptItterations++
$iRunning=0
$iCompleted=0
$iFailed=0
foreach($job in Get-Job) {
if ([string]$job.JobStateInfo -eq "Completed") {$iCompleted++}
if ([string]$job.JobStateInfo -eq "Running") {$iRunning++}
if ([string]$job.JobStateInfo -eq "Failed") {$iFailed++}
}
Write-Host "Total jobs: $iTotalJobs, $iRunning jobs are running... Completed:
$iCompleted, failed: $iFailed"
Start-Sleep 10 
}
#All jobs have fiinished. Adding job results to the array and deleting the job
$AllJobResults= @()
foreach($job in Get-Job){
$AllJobResults += Receive-Job -Job $job 
Remove-Job -Job $job 
}

One more bit. As I have mentioned Receive-Job returns unindexed results. What I have found practical is to return data in a form of generic PowerShell object. This will allow you later to sort your data and display them in a form of the table:

$objReturn = New-Object -Type PSObject -Property @{
  'DataFiled1' = $Data1
  'DataFiled2' = $Data2
  ...
  'DataFiledN' = $DataN
}
$AllJobResults | Sort-Object -Property Data1,Data2 |Format-Table -property Data1,Data2,DataN

[EUC]: Adding new input languages in Windows 10

January 2nd, 2019

Starting Windows 10 Build 1803 adding a new input language to Windows 10 is not an easy task. Adding it via settings may cause two issues: adding an additional en-US language, which is most likely not required in Australia, and arbitrary changing the application interface to the wrong language.

This PowerShell scripts adds new languages correctly without affecting the rest of OS (in my case I need 3 languages for my day to day business, English (AU), German and Russian).

Note that this code will remove all previous language settings.

$1=New-WinUserLanguageList en-AU
$1.Add("de-DE")
$1.Add("ru-RU")
Set-WinUserLanguageList $1

 

State Message Storm in SCCM 2012

December 9th, 2015

Sometimes (in my case after SCCM 2012 upgrade to R2) you may see a high number of error messages (ID 6105) produced by SMS_STATE_SYSTEM with the following description:

SMS State System message file processing processed one or more files that contained errors or invalid data. Review the statesys.log file for further details.

The STATESYS.LOG File shows the following:

SQL MESSAGE: spProcessStateReport – Error: The key for machine <AGENTNAME> (GUID:EE344EFE-A80C-483E-BAFB-E3XXXXXXXXXX) did not match the one in the database.

That means that the client system has changed SMS GUID but this change has not been reflected in SCCM DB. You have to manually reset the agent SMS GUID to fix the problem.

If you are dealing with thousand messages and hundreds of clients you can script up this operation as I did.

To get the names for affected clients you have to go to <SCCM Installation Folder>\Inboxes\auth\statesys.box\corrupt and copy all SMX files to the temporary location. Each SMS file is in fact a XML document. In order to extract system name from it I use this PowerShell script:

get-childitem *.smx | % {[xml]$statfile = get-content $_.Name

Out-File -filepath “list-of-computers.txt” -InputObject $statfile.Report.ReportHeader.Identification.Machine.NetBiosName -append

}

This script may produce several errors which could be ignored.

The list will contain a lot of duplicates. I get rid of them using Excel filter.

Once you have your list you may proceed with SMS GUID reset. To reset SCCM client SMS GUID you have to simply delete SMSCFG.INI file from %windir% and restart SMS Agent Host service. I use this script to reset SMS GUID on multiple servers:

$FullServerList = Get-Content “list-of-computers.txt”

foreach ($servername in $FullServerList ) {
If (Test-Connection -computername $servername -quiet) {
$RemoteFile = “\\”+$servername+”\Admin$\SMSCFG.INI”
If (Test-Path $RemoteFile) {
Remove-Item $RemoteFile
Write-Host “Deleting” $RemoteFile
(gwmi Win32_Service -filter “name=’ccmexec’” -computername $ServerName).StopService()
(gwmi Win32_Service -filter “name=’ccmexec’” -computername $ServerName).StopService()
} Else {Write-Host “Cannot connect to” $RemoteFile
}
} Else {Write-Host $ServerName “cannot be contacted”}
}

SCCM: How to delete all packages from the DP scheduled for decommissioning

September 4th, 2015

Prior to the decommissioning of the Distribution Point it is recommended to delete all packages assigned to this DP. If you do not do that the ‘orphaned’ packages may sometimes appear on your software distribution reports producing unnecessary noise.

This simple PowerShell one liner removes all packages from the selected DP. Note that this script does not physically remove the package from DP, it simply deletes the relevant record in the database.

Let’s assume CEN is your Central or standalone SCCM site. This is the script:

gwmi -Namespace root\sms\Site_CEN -query "select * from SMS_DistributionPoint where ServerNALPath like '%MYDP001%'" | % ($_.Delete())

Sometimes this script may give you an error. This may happen because CEN is your central site and the package is published on one of the primary sites. To identify package’s source site simply modify the script:

gwmi -Namespace root\sms\Site_CEN -query "select * from SMS_DistributionPoint where ServerNALPath like '%MYDP001%'" | % ($_.SourceSite)

And then change CEN site code in the namespace root\sms\Site_CEN to the site code you have found.

Issue with editing SCOM 2012 Subscription criteria via GUI

August 7th, 2015

When editing SCOM 2012 Subscription criteria via GUI you may have the following error message:

“The criteria associated with this notification subscription are of a form not supported by the Operations Manager Console. You may continue through this wizard, and the criteria will remain unchanged.”

That typically means that your criteria are referencing to the non-existing rule or monitor. This is how to fix it:

Go to SCOM PowerShell. Get your criteria using the following command:

Get-SCOMNotificationSubscription | Where-Object {$_.DisplayName -eq “<Your faulty subscription name>”} | Format-List DisplayName,@{Label=”Criteria”,Expression={$_.Configuration.criteria}}

This will produce XML file with the following format:

<Expression>
<SimpleExpression>
 <ValueExpression>
  <Property>ProblemID</Property>
 </ValueExpression>
 <Operator>Equal</Operator>
 <ValueExpression>
  <Value>aaa0000-0000-0000-0000-000000000000</Value>
 </ValueExpression>
 </SimpleExpression>
</Expression>

Every value represents a rule or monitor ID. Extract all these values. For each ID run the following command:

Get-SCOMMonitor -id aaa0000-0000-0000-0000-000000000000 | Select-Object Name

This will give you the rule or monitor name. The ones returning no result are your non-existing ones. Record names for the existing rules or monitors.

Delete the faulty subscription and recreate it from scratch. Now you have all your criteria.

How to create a new SCCM system role with a script

August 8th, 2013

Both SCCM 2007 and 2012 keep system role information in the site control file. So the two main operation at the begining and at the end of the script will be getting the file handle, refreshing the file, commit changes and release the handle.

$ComputerName = "MySiteServer"
$MyNewRoleServer = "MyNewRoleServer"
$sitecode = "ABC"
$nameSpacePath = "\\$ComputerName\root\sms\site_$sitecode"
$domainname=".mydomain.local"

# Get a session handle for the site control file
$scf = Invoke-WmiMethod -Namespace $NameSpace -class SMS_SiteControlFile -name GetSessionHandle -computername $ComputerName

# Refresh the WMI copy of the site control file
$refresh = Invoke-WmiMethod -Namespace $NameSpace -class SMS_SiteControlFile -name RefreshSCF -ArgumentList $sitecode -computername $ComputerName
<#
your main script will be here
#>
# Commit site control file from WMI to the actual file
$commit = Invoke-WmiMethod -Namespace $NameSpace -class SMS_SiteControlFile -name CommitSCF $sitecode -computername $ComputerName

# Release session handle
$scf = Invoke-WmiMethod -Namespace $NameSpace -class SMS_SiteControlFile -name ReleaseSessionHandle -ArgumentList $scf.SessionHandle -computername $ComputerName

Serge Chegorian's System Center Blog

Serge Chegorian's System Center Blog