Reassigning clients to Site | PowerShell | ConfigMgr | CB Feature Automation |SCCM


Hello All,

Hope everyone has plans for this week!😉 If you’re not one of them, then I would like to extend my cheers to you! 🍻 I’m also one of them among you who was/is working with some set of PowerShell script for automating stuffs.

If you’re one of the Org./ConfigMgr Admin who has multi-domain AD forest with Multiple Primaries (a.k.a) Complex Environments, you might be seeing clients are assigned to a different site code instead of which it should be assigned to in actual, also you might be fed up performing remediation using registry methods available from various bloggers!

Link to MSFT docs for more detail on Client Reassignment: Article! I don’t remember the Current Branch version exactly from when support for Reassigning Clients from ConfigMgr console has been introduced.

Pre-Requisites:

  1. You have to be in one of the ConfigMgr Current Branch versions. I use ConfigMgr CB 1802
  2. A computer where ConfigMgr console is installed.
  3. You must be connected to CAS site using PowerShell. How to do?

Pro Tip: If you want to use this script in a fully automated way, you can add few lines of code in this script to connect your CAS site automatically. So, that you can use Windows In-Built Task Schedulers for end to end automation of this script.

For building this script I took scenario of an environment with 1 AD Forest with 4 Domains and 3 ConfigMgr Sites.

Domain SCCM Site Code
D01 S01
D02 S02
D03 & D04 S03

What this script does?

This script checks if the device has client installed and has active reporting state. Post check if client is registered in the relevant Site based on the organization technical architecture, if found incorrect, it performs reassigning client to the relevant site.

How do I identify your script is working?

I tried this in our environment and found clients are getting reassigned, also I’ve added logging in script as well, if still required you can check SMSProv.log on your CAS site to verify whether this actions are being performed.

Manual Log File Location: Temp directory on your Windows installation drive.

File Name: ClientReassignment.log

PowerShell Script:

Clear-Host
Clear-Content $env:windir\Temp\ClientReassignment.log -Force -ErrorAction SilentlyContinue
$Devices = Get-CMDevice
foreach ($Device in $devices)
{
$Domain = $Device.Domain
$SiteCode = $Device.SiteCode
[Array]$ResourceID = @("$($Device.ResourceID)")
$ObjName = $Device.Name
"$(Get-Date -DisplayHint DateTime) | Checking for $ObjName"
"$(Get-Date -DisplayHint DateTime) | Checking for $ObjName" | Out-File $env:windir\Temp\ClientReassignment.log -Append
if ($Device.IsClient -eq "True" -and $Device.IsActive -eq "True")
{
if ($Domain -eq "D01" -and $SiteCode -ne "S01")
{
"$(Get-Date -DisplayHint DateTime) | Reassigning $ObjName to S01 site as per domain"
"$(Get-Date -DisplayHint DateTime) | Reassigning $ObjName to S01 site as per domain" | Out-File $env:windir\Temp\ClientReassignment.log -Append
Invoke-CMWmiMethod -ClassName "SMS_Collection" -MethodName "ReassignClientsToSite" -Parameter @{ResourceIDs = $ResourceID ; NewSiteCode = "S01"} | Out-Null
}
elseif($Domain -eq "D02" -and $SiteCode -ne "S02")
{
"$(Get-Date -DisplayHint DateTime) | Reassigning $ObjName to S02 site as per domain"
"$(Get-Date -DisplayHint DateTime) | Reassigning $ObjName to S02 site as per domain" | Out-File $env:windir\Temp\ClientReassignment.log -Append
Invoke-CMWmiMethod -ClassName "SMS_Collection" -MethodName "ReassignClientsToSite" -Parameter @{ResourceIDs = $ResourceID ; NewSiteCode = "S02"} | Out-Null
}
elseif($Domain -eq "D03" -and $SiteCode -ne "S03")
{
"$(Get-Date -DisplayHint DateTime) | Reassigning $ObjName to S03 site as per domain"
"$(Get-Date -DisplayHint DateTime) | Reassigning $ObjName to S03 site as per domain" | Out-File $env:windir\Temp\ClientReassignment.log -Append
Invoke-CMWmiMethod -ClassName "SMS_Collection" -MethodName "ReassignClientsToSite" -Parameter @{ResourceIDs = $ResourceID ; NewSiteCode = "S03"} | Out-Null
}
elseif($Domain -eq "D04" -and $SiteCode -ne "S03")
{
"$(Get-Date -DisplayHint DateTime) | Reassigning $ObjName to S03 site as per domain"
"$(Get-Date -DisplayHint DateTime) | Reassigning $ObjName to S03 site as per domain" | Out-File $env:windir\Temp\ClientReassignment.log -Append
Invoke-CMWmiMethod -ClassName "SMS_Collection" -MethodName "ReassignClientsToSite" -Parameter @{ResourceIDs = $ResourceID ; NewSiteCode = "S03"} | Out-Null
}
else
{
"$(Get-Date -DisplayHint DateTime) | Performing no actions on $ObjName since it is not matching reassignment criteria"
"$(Get-Date -DisplayHint DateTime) | Performing no actions on $ObjName since it is not matching reassignment criteria" | Out-File $env:windir\Temp\ClientReassignment.log -Append
}
}
else
{
"$(Get-Date -DisplayHint DateTime) | Performing no actions on $ObjName since it is not an active client"
"$(Get-Date -DisplayHint DateTime) | Performing no actions on $ObjName since it is not an active client" | Out-File $env:windir\Temp\ClientReassignment.log -Append
}
}
"$(Get-Date -DisplayHint DateTime) | End of Execution"
"$(Get-Date -DisplayHint DateTime) | End of Execution" | Out-File $env:windir\Temp\ClientReassignment.log -Append

CmdLet References:

CMDLET Reference
Get-CMDevice Article
Invoke-CMWmiMethod Article

 
Feel free to connect with me for queries/concerns.

-Praveen

Advertisements

PowerShell One-Liner to remove dead DP from ConfigMgr hierarchy | WMI | PowerShell


Hello All,

This week, I was working on one of the tasks for removing dead DP from a ConfigMgr hierarchy, I was bored of performing actions always from ConfigMgr console, came up with this PowerShell one-liner, it queries WMI class for the availability and deletes them if found.

You can connect to either your CAS/Primary Server remotely from WMI and perform this actions. You’ve to update the below given variables in order to perform this action in your environment.

SVR1 – CAS / Primary Server Name.

PRI – Relevant Site Code.

DP1 – Site Server name which you want to remove from hierarchy.

PowerShell Script:

Get-WmiObject -ComputerName "SVR1" -Namespace "root/sms/site_PRI" -query "select * from SMS_SCI_SYSRESUSE where nalpath like '%DP1%'" | Remove-WmiObject -Verbose

Pro Tip: Prefer running command from CAS site if you have CAS hierarchy in your environment!

Reference Article:

SMS_SCI_SYSRESUSE

Feel free to comment for questions/concerns.

-Praveen

 

SCCM Logs : File Rename Error | Maximum Log File Size Reached : Fix


Hello All,

I was working one of the incident at my workplace which was related to distribution, some packages are not getting distributed to Remote DP’s. I opened PkgXferMgr.log to trace what’s happening with regards to distribution.

I found something like the image given below. Actual threshold of the file was 2 MB, but this file has reached 48 MB and is not getting renamed.

pkgxfermgr

Tried restarting the component and SMS_EXECUTIVE with no success, I was scratching my head and surfing internet to see if someone has encountered similar problem. Didn’t find any relevant articles which directed towards fix.

Resolution:

  • Stop the component which is having issues. Either from Service Manager or from RegEdit
  • Try renaming the file manually.
  • If you get “This action cannot be performed because it is opened in System” error. Follow below.
  • Use fsmgmt.msc disconnect all sessions of the file if opened remotely.
  • Try renaming the file again.
  • Sample Powershell:

    Rename-Item PkgXferMgr.log -NewName "PkgXferMgr.lo_Old" -Force -Verbose 

The above given steps should fix this issue. Happy Troubleshooting!

-Praveen


 

Configuration Baseline for Profile CleanUp | ConfigMgr | WMI | PowerShell


Hello All,

With this post I would like to help my fellow teammates who face Customers/IT BRM’s directly. Who are they? They are the people who support Desktops/Laptops/Servers on a real-time basis. Popularly called as “System Administrators

At times, Administrators may see messages stating low disk space on OS drives which will eventually end up showing disk utilized by user profiles, to mitigate this some will clean old user profiles from file explorer and forget to delete the registry. Post this action some end users might receive a pop-up once they login stating “You have been logged in with a temporary profile!” End users those who don’t know what happened may raise incidents with Service Desk to get this fixed. For automating this I came up with an idea.

Some of the environments may already have startup scripts enabled to do this on daily/weekly/monthly basis. Again I have came up with a ConfigMgr Configuration Baseline to reduce GPO efforts.

What is being done in this script?

  • Log Off all disconnected users.
  • Checking registry path [HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList] to see if any keys present with names ending .bak
  • Cleanup those registry keys.
  • Check for domain profiles with last login greater than 90 days.
  • Delete those profiles which matches the criteria.

P.S: Actions performed by this script is being logged via log file in Windows Temp directory. a.k.a %windir%\temp

Discovery Script [PowerShell]:

Clear-Host
$oldprof = $null
quser | Select-String "Disc" |ForEach {logoff ($_.tostring() -split ' +')[2]}
Clear-Content $env:windir\temp\profilecleanup.log -Force -ErrorAction SilentlyContinue
$regpath = "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList"
#"$(Get-Date -DisplayHint DateTime) | Performing cleanup of bad registry entries on $regpath" | Out-File $env:windir\temp\profilecleanup.log -Append
foreach ($regkey in (Get-ChildItem "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\" -Recurse))
{
if ($regkey.Name -like "*.bak")
{
$cleanup = $regkey.Name
#"$(Get-Date -DisplayHint DateTime) | Removing $($cleanup)" | Out-File $env:windir\temp\profilecleanup.log -Append
#reg delete "$cleanup" /f
}
}
"$(Get-Date -DisplayHint DateTime) | Performing old user cleanup where last login time > 90 days" | Out-File $env:windir\temp\profilecleanup.log -Append
$users = Get-WmiObject win32_userprofile
$days = "90"
foreach ($user in $users)
{
$SID =$($user.sid)
$objSID = New-Object System.Security.Principal.SecurityIdentifier($SID)
$objUser = $objSID.Translate([System.Security.Principal.NTAccount])
if (($user.LastUseTime -lt $(Get-Date).Date.AddDays(-$days))-and $user.sid -like "S-1-5-21-*")
{
$oldprof++
#"$(Get-Date -DisplayHint DateTime) | Removing $($objUser.value)" | Out-File $env:windir\temp\profilecleanup.log -Append
#$user.Delete()
}
else
{
#"$(Get-Date -DisplayHint DateTime) | Skipping $($objUser.value)" | Out-File $env:windir\temp\profilecleanup.log -Append
}
}
if ($oldprof -ge "1")
{
return $false
}
else
{
return $true
}

Remediation Script [PowerShell]:

Clear-Host
quser | Select-String "Disc" |ForEach {logoff ($_.tostring() -split ' +')[2]}
Clear-Content $env:windir\temp\profilecleanup.log -Force -ErrorAction SilentlyContinue
$regpath = "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList"
"$(Get-Date -DisplayHint DateTime) | Performing cleanup of bad registry entries on $regpath" | Out-File $env:windir\temp\profilecleanup.log -Append
foreach ($regkey in (Get-ChildItem "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\" -Recurse))
{
if ($regkey.Name -like "*.bak")
{
$cleanup = $regkey.Name
"$(Get-Date -DisplayHint DateTime) | Removing $($cleanup)" | Out-File $env:windir\temp\profilecleanup.log -Append
reg delete "$cleanup" /f
}
}
"$(Get-Date -DisplayHint DateTime) | Performing old user cleanup where last login time > 90 days" | Out-File $env:windir\temp\profilecleanup.log -Append
$users = Get-WmiObject win32_userprofile
$days = "90"
foreach ($user in $users)
{
$SID =$($user.sid)
$objSID = New-Object System.Security.Principal.SecurityIdentifier($SID)
$objUser = $objSID.Translate([System.Security.Principal.NTAccount])
if (($user.LastUseTime -lt $(Get-Date).Date.AddDays(-$days))-and $user.sid -like "S-1-5-21-*")
{
"$(Get-Date -DisplayHint DateTime) | Removing $($objUser.value)" | Out-File $env:windir\temp\profilecleanup.log -Append
$user.Delete()
}
else
{
#"$(Get-Date -DisplayHint DateTime) | Skipping $($objUser.value)" | Out-File $env:windir\temp\profilecleanup.log -Append
}
}

We don’t have access to ConfigMgr, How to implement this via GPO?

Same script can be used with some modifications.

GPO Script [PowerShell]:

Clear-Host
quser | Select-String "Disc" |ForEach {logoff ($_.tostring() -split ' +')[2]}
Clear-Content $env:windir\temp\profilecleanup.log -Force -ErrorAction SilentlyContinue
$regpath = "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList"
"$(Get-Date -DisplayHint DateTime) | Performing cleanup of bad registry entries on $regpath" | Out-File $env:windir\temp\profilecleanup.log -Append
foreach ($regkey in (Get-ChildItem "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\" -Recurse))
{
if ($regkey.Name -like "*.bak")
{
$cleanup = $regkey.Name
"$(Get-Date -DisplayHint DateTime) | Removing $($cleanup)" | Out-File $env:windir\temp\profilecleanup.log -Append
reg delete "$cleanup" /f
}
}
"$(Get-Date -DisplayHint DateTime) | Performing old user cleanup where last login time > 90 days" | Out-File $env:windir\temp\profilecleanup.log -Append
$users = Get-WmiObject win32_userprofile
$days = "90"
foreach ($user in $users)
{
$SID =$($user.sid)
$objSID = New-Object System.Security.Principal.SecurityIdentifier($SID)
$objUser = $objSID.Translate([System.Security.Principal.NTAccount])
if (($user.LastUseTime -lt $(Get-Date).Date.AddDays(-$days))-and $user.sid -like "S-1-5-21-*")
{
"$(Get-Date -DisplayHint DateTime) | Removing $($objUser.value)" | Out-File $env:windir\temp\profilecleanup.log -Append
$user.Delete()
}
else
{
#"$(Get-Date -DisplayHint DateTime) | Skipping $($objUser.value)" | Out-File $env:windir\temp\profilecleanup.log -Append
}
}

Kindly drop your queries/concerns in comments section.

Happy to Help!!

-Praveen

 

Update SCEP AV definitions using SCCM Task Sequence

Hello All,

Happy New Year 2019!

Hope everyone’s 2019 New Year celebrations went well! From past few days I was working on one of the assignments of SCEP AV definition update related issues, wherein the endpoint fails to update using it’s usual update mechanism, to overcome this I was thinking of writing some set of PowerShell commands to automate / use SCCM inbuilt task sequence to perform this actions.

Here we’re going to utilize SCCM inbuilt Task Sequence to perform SCEP Update on a specified collection.

Pre-Requisites:

  • SCEP AV Definitions downloaded from Microsoft ( Download Link: 32 Bit | 64 Bit )
  • Package with 2 programs for installing downloaded executable’s based on the OS Architecture.
  • Distribute it to relevant Distribution Points.

P.S: Packages has to be updated on daily basis with the latest definition updates.

SCCM Task Sequence Steps (Flow Chart and Explanation):

scep_update_ts

As explained in the above flow-chart, just re-iterating those in words. Just to avoid issues for those folks who view blogs from their workplace, there were times where images placed in the blogs were not loaded properly.

  1. Start
  2. Check age of the AV Signature present in the machine is greater than or equal to 1, as per Microsoft recommendation, we’ve to keep our Anti-Virus definitions up to date to protect organization from the latest threats.
  3. If signature age condition matches, TS will download Package into Configuration Manager Cache.
  4. Post downloading, TS will check OS Architecture and run the executable based on the OS Bit.
  5. If condition on step two fails, then TS will exit execution on the deployed endpoint.

P.S: For utilizing pre download content feature you must be using ConfigMgr CB 1511 or above. You can disable pre download content step if required

References:

WQL Query:

  • For checking AV Signature Age
    • WMI NameSpace: ROOT\Microsoft\SecurityClient
    • WQL Query: select * from antimalwarehealthstatus where AntispywareSignatureAge != 0
  • For Checking OS Architecture
    • WMI NameSpace: root\cimv2
    • 64-Bit WQL Query: select * from win32_operatingsystem where OSArchitecture like "64-bit"
    • 32-Bit WQL Query: select * from win32_operatingsystem where OSArchitecture like "32-bit"

Microsoft Articles:

WMI Class: Win32_OperatingSystem

To ease the tasks explained above, uploaded exported TS to GitHub repository Download!

P.S: You can automate deployment of this TS using ConfigMgr PowerShell cmdlets.

Feel free to reach out in comments section for queries.

-Praveen

ConfigMgr SQL query to find patch installation status of a specific update against a collection/machine


Hello All,

I’ve came up with a small piece of SQL query, which’ll help you in getting patch installation status for a specific article against a machine/collection.

I was posting things in MSFT Gallery earlier but due to some policy changes they’ve restricted gallery contribution to few audience. So I ended up writing things in this blog to share my experience to fellow colleagues.

SQL:

select a.Name0[Computer Name],
a.Full_Domain_Name0[Computer Domain],
a.User_Name0[User Name],
a.User_Domain0[User Domain],
b.CollectionID,
d.ArticleID,
d.Title,
case c.status
when '0' then 'Detection Unknown'
when '1' then 'Not Required'
when '2' then 'Not Installed'
when '3' then 'Installed'
end [Update Installation Status]
from v_r_system a
join v_fullcollectionmembership b on a.resourceid = b.resourceid
join v_update_compliancestatusall c on a.ResourceID = c.ResourceID
join v_UpdateInfo d on c.CI_ID = d.CI_ID
where b.collectionid like '' and d.articleid like '%3191564%' -- Input your Collection ID where you want to fetch patch installation status.
where d.articleid like '%3191564%' and a.name0 like '' -- Input your Computer/Server Name where you want to fetch patch installation status.

--Any one set of where condition should be commented before executing on larger sets.

 

WMI Reference of v_Update_compliancestatusall MSFT Docs

Feel free to comment for queries/concerns.

-Praveen

Configuration Baseline for Detecting Patches CVE-2018-8653 : ConfigMgr : Powershell : WMI

Hello All,

To add additional workload to ConfigMgr admins 😉, MSFT has released Out-Of-Band patches for Internet Explorer. MSFT Article . Now most of the people like me would have started deploying those updates via emergency change process some people would wait for next Patch Tuesday to apply CU’s via normal update roll-out process.

I quickly thought of helping people who has started deploying this updates to Workstations/Servers.

Wrote a piece of code which will help in creating configuration baseline and deploy based on the requirement.

PowerShell Script for Creating Baseline:

$Build = (gwmi win32_operatingsystem).BuildNumber
switch ($Build)
{
('10240')
{
if (gwmi -Namespace "root/cimv2" -Query "select * from win32_quickfixengineering where hotfixid like 'KB4483228'")
{
return "$true"
}
else
{
return "$false"
}
}
('10586')
{
return "$false"
}
('14393')
{
if (gwmi -Namespace "root/cimv2" -Query "select * from win32_quickfixengineering where hotfixid like 'KB4483229'")
{
return "$true"
}
else
{
return "$false"
}
}
('15063')
{
if (gwmi -Namespace "root/cimv2" -Query "select * from win32_quickfixengineering where hotfixid like 'KB4483230'")
{
return "$true"
}
else
{
return "$false"
}
}
('16299')
{
if (gwmi -Namespace "root/cimv2" -Query "select * from win32_quickfixengineering where hotfixid like 'KB4483232'")
{
return "$true"
}
else
{
return "$false"
}
}
('17134')
{
if (gwmi -Namespace "root/cimv2" -Query "select * from win32_quickfixengineering where hotfixid like 'KB4483234'")
{
return "$true"
}
else
{
return "$false"
}
}
('17723')
{
if (gwmi -Namespace "root/cimv2" -Query "select * from win32_quickfixengineering where hotfixid like 'KB4483235'")
{
return "$true"
}
else
{
return "$false"
}
}
('17763')
{
return "$true"
}
('2600')
{
return "$true"
}
('3790')
{
return "$true"
}
('6001')
{
if (gwmi -Namespace "root/cimv2" -Query "select * from win32_quickfixengineering where hotfixid like 'KB4483187'")
{
return "$true"
}
else
{
return "$false"
}
}
('6002')
{
if (gwmi -Namespace "root/cimv2" -Query "select * from win32_quickfixengineering where hotfixid like 'KB4483187'")
{
return "$true"
}
else
{
return "$false"
}
}
('7600')
{
if (gwmi -Namespace "root/cimv2" -Query "select * from win32_quickfixengineering where hotfixid like 'KB4483187'")
{
return "$true"
}
else
{
return "$false"
}
}
('7601')
{
if (gwmi -Namespace "root/cimv2" -Query "select * from win32_quickfixengineering where hotfixid like 'KB4483187'")
{
return "$true"
}
else
{
return "$false"
}
}
('9200')
{
if (gwmi -Namespace "root/cimv2" -Query "select * from win32_quickfixengineering where hotfixid like 'KB4483187'")
{
return "$true"
}
else
{
return "$false"
}
}
('9600')
{
if (gwmi -Namespace "root/cimv2" -Query "select * from win32_quickfixengineering where hotfixid like 'KB4483187'")
{
return "$true"
}
else
{
return "$false"
}
}
}

Import Baseline directly to ConfigMgr, Cab File has been uploaded to GitHub. Download Link!

Please let me know in comments for queries/concerns.

-Praveen