Noticed in our environment that after installing the KB2957689 (included in June patching session by Microsoft) users get an additional home tab (pointing to http://windows.microsoft.com/en-us/internet-explorer/ie-10-welcome-upgrade1) after they start IE for the first time after the upgrade. While the tab is gone after the first run of IE, in the Enterprise environment we kept receiving questions about what was going on.
It looked even more strange as we have a setting defined in the global GPO that should be preventing this type of behavior:
So after a bit of search we found out that this behavior could be blocked by applying the following registry change:
[HKCU\Software\Microsoft\Internet Explorer\Main]
"PrivacyPolicyShown"=dword:00000001
As this key is not mentioned in IE's ADMX and I couldn't even find it via Microsoft GPO search, decided to implement it via GPP and it fixed the issue:
Hope it will help you to avoid wasting time to search for the solution.
Monday, June 16, 2014
Friday, June 13, 2014
Script to inventory logon / logoff / startup / shutdown scripts in GPO environment
I was looking for a way on how to access programmatically GPO information about different type of GPO logon, etc. scripts.
Unfortunately standard GPO cmdlets don't cover this part of GPO configurations. So after a bit of looking around on MSDN GPO Documentation, I confirmed my assumptions that it's stored in scripts.ini files on Sysvol. So for any given GPO you need to look in these 2 places:
- User part: \\domain\SYSVOL\domain\Policies\GPO_GUID\User\Scripts\scripts.ini
- Computer part: \\domain\SYSVOL\domain\Policies\GPO_GUID\Machine\Scripts\scripts.ini
Following this I've created a script that goes through all GPOs in AD & inventories the information about all scripts referenced there.
InventoryLogonScriptsInGPOs.ps1
Unfortunately standard GPO cmdlets don't cover this part of GPO configurations. So after a bit of looking around on MSDN GPO Documentation, I confirmed my assumptions that it's stored in scripts.ini files on Sysvol. So for any given GPO you need to look in these 2 places:
- User part: \\domain\SYSVOL\domain\Policies\GPO_GUID\User\Scripts\scripts.ini
- Computer part: \\domain\SYSVOL\domain\Policies\GPO_GUID\Machine\Scripts\scripts.ini
Following this I've created a script that goes through all GPOs in AD & inventories the information about all scripts referenced there.
InventoryLogonScriptsInGPOs.ps1
# Inventory script to gather the information about logon / logoff / startup / shutdown scripts in your environment
# It's scanning all GPOs in your environment & checking Sysvol for scripts.ini files
# that contain references about these scripts
# support maximum 10 scripts per GPO part (user or machine)
# Author: Michael Sedenkov (PMI)
clear
#please define here your domain name
$DomainName = "domain"
#please define here the path to your Policies folder on Sysvol
$SysvolPath = "\\domain\SYSVOL\domain\Policies"
Function ParseScriptsINI ($FileToParse, $GPOpart) {
#Initialize variables
$LogonScriptsList = "no", "no", "no", "no", "no", "no", "no", "no", "no", "no"
$LogonScriptsParameters = "no", "no", "no", "no", "no", "no", "no", "no", "no", "no"
$LogoffScriptsList = "no", "no", "no", "no", "no", "no", "no", "no", "no", "no"
$LogoffScriptsParameters = "no", "no", "no", "no", "no", "no", "no", "no", "no", "no"
$StartupScriptsList = "no", "no", "no", "no", "no", "no", "no", "no", "no", "no"
$StartupScriptsParameters = "no", "no", "no", "no", "no", "no", "no", "no", "no", "no"
$ShutdownScriptsList = "no", "no", "no", "no", "no", "no", "no", "no", "no", "no"
$ShutdownScriptsParameters = "no", "no", "no", "no", "no", "no", "no", "no", "no", "no"
$LogonScriptCounter = 0
$LogoffScriptCounter = 0
$StartupScriptCounter = 0
$ShutdownScriptCounter = 0
$section = ""
#read file content
$AllLines = Get-Content -Path $FileToParse
#if the file is too short - escaping
if ($AllLines.Count -le 1) { Write-Debug "File is too short, doing nothing"; return }
#parse line by line
foreach ($line in $AllLines) {
switch ($line.Trim())
{
#defining in what part of the file we are
"" {Write-Debug "empty line"}
"[Logon]" { Write-debug "found logon scripts section"; $section = "logon"}
"[Logoff]" { Write-debug "found logoff scripts section"; $section = "logoff"}
"[Startup]" { Write-debug "found logoff scripts section"; $section = "startup"}
"[Shutdown]" { Write-debug "found logoff scripts section"; $section = "shutdown"}
"[ScriptConfig]" { Write-debug "found configuration section"; $section = "config"}
default {
#trying to split the script line
$data = $line.Split("=")
if ($data[0].Trim() -eq "") { exit }
#reading logon section
if ($section -eq "logon") {
#increasing the counter
$LogonScriptCounter = [int]($data[0].Chars(0)).tostring() + 1
#populating the scripts array
if ($data[0].Contains("CmdLine")) {$LogonScriptsList[[int]($data[0].Chars(0)).tostring()] = $data[1]}
#populating the parameters array
if ($data[0].Contains("Parameters")) {$LogonScriptsParameters[[int]($data[0].Chars(0)).tostring()] = $data[1]}
}
if ($section -eq "logoff") {
$LogoffScriptCounter = [int]($data[0].Chars(0)).tostring() + 1
if ($data[0].Contains("CmdLine")) {$LogoffScriptsList[[int]($data[0].Chars(0)).tostring()] = $data[1]}
if ($data[0].Contains("Parameters")) {$LogoffScriptsParameters[[int]($data[0].Chars(0)).tostring()] = $data[1]}
}
if ($section -eq "startup") {
$StartupScriptCounter = [int]($data[0].Chars(0)).tostring() + 1
if ($data[0].Contains("CmdLine")) {$StartupScriptsList[[int]($data[0].Chars(0)).tostring()] = $data[1]}
if ($data[0].Contains("Parameters")) {$StartupScriptsParameters[[int]($data[0].Chars(0)).tostring()] = $data[1]}
}
if ($section -eq "shutdown") {
$ShutdownScriptCounter = [int]($data[0].Chars(0)).tostring() + 1
if ($data[0].Contains("CmdLine")) {$ShutdownScriptsList[[int]($data[0].Chars(0)).tostring()] = $data[1]}
if ($data[0].Contains("Parameters")) {$ShutdownScriptsParameters[[int]($data[0].Chars(0)).tostring()] = $data[1]}
}
}
}
}
#in case something is found - printing it out
if ( ($GPOpart -eq "User") -and (($LogonScriptCounter -ne 0) -or ($LogoffScriptCounter -ne 0))) {
Write-Host "Parsing the GPO (User part):" $targetGPO.DisplayName "; GUID:{"$targetGPO.ID"}"
if ($LogonScriptCounter -ne 0) { Write-Host " Total number of logon scripts: " $LogonScriptCounter}
while ($LogonScriptCounter -ne 0) {
Write-Host " "$LogonScriptsList[$LogonScriptCounter - 1] "params:" $LogonScriptsParameters[$LogonScriptCounter - 1]
$LogonScriptCounter = $LogonScriptCounter - 1
}
if ($LogoffScriptCounter -ne 0) { Write-Host " Total number of logoff scripts: " $LogoffScriptCounter}
while ($LogoffScriptCounter -ne 0) {
Write-Host " "$LogoffScriptsList[$LogoffScriptCounter - 1] "params:" $LogoffScriptsParameters[$LogoffScriptCounter - 1]
$LogoffScriptCounter = $LogoffScriptCounter - 1
}
}
if ( ($GPOpart -eq "Machine") -and (($StartupScriptCounter -ne 0) -or ($ShutdownScriptCounter -ne 0))) {
Write-Host "Parsing the GPO (Machine part):" $targetGPO.DisplayName "; GUID:{"$targetGPO.ID"}"
if ($StartupScriptCounter -ne 0) { Write-Host " Total number of startup scripts: " $StartupScriptCounter}
while ($StartupScriptCounter -ne 0) {
Write-Host " "$StartupScriptsList[$StartupScriptCounter - 1] "params:" $StartupScriptsParameters[$StartupScriptCounter - 1]
$StartupScriptCounter = $StartupScriptCounter - 1
}
if ($ShutdownScriptCounter -ne 0) { Write-Host " Total number of shutdown scripts: " $ShutdownScriptCounter}
while ($ShutdownScriptCounter -ne 0) {
Write-Host " "$ShutdownScriptsList[$ShutdownScriptCounter - 1] "params:" $ShutdownScriptsParameters[$ShutdownScriptCounter - 1]
$ShutdownScriptCounter = $ShutdownScriptCounter - 1
}
}
}
#look at all GPOs in the domain environment
$AllDomainGPOs = (Get-GPO -All -Domain $DomainName | sort DisplayName)
#for each GPO we check if there are any logon / logoff / startup / shutdown scripts
foreach ($targetGPO in $AllDomainGPOs) {
#construct path to User part scripts
$InputFileUser = $SysvolPath + "\{$($targetGPO.ID)}\User\Scripts\scripts.ini"
#if scripts.ini file exists - then we try to parse it
if ((Test-Path $InputFileUser) -eq $true) { ParseScriptsINI $InputFileUser User }
#construct path to Machine part scripts
$InputFileMachine = $SysvolPath + "\{$($targetGPO.ID)}\Machine\Scripts\scripts.ini"
#if scripts.ini file exists - then we try to parse it
if ((Test-Path $InputFileMachine) -eq $true) { ParseScriptsINI $InputFileMachine Machine }
}
Tuesday, June 10, 2014
How to identify SCCM packages having DP replication issues & refresh them
In SCCM 2012 it's difficult to perform a mass re-sync of packages that failed DP replication. And re-synching them one by one could be tiresome. So below you could find a PowerShell script, that identifies such problematic packages & tries refreshing them:
#script to refresh the content of packages that failed to replicate in your SCCM 2012 infrastructure
clear
$SiteCode = "PutYourSCCMSiteCodeHere"
$SCCMserver = "PutYourSCCMSererNameHere"
$dps = Get-WmiObject -computer $SCCMserver -Namespace "root\SMS\Site_$($SiteCode)" -Query "Select * From SMS_DistributionDPStatus WHERE MessageState=4"
foreach ($dp in $dps)
{
write-host "DP:" $dp.NALPath.Substring(12,13) "; Package:" $dp.PackageID
$PackageDP = Get-WmiObject -computer $SCCMserver -Namespace "root\SMS\Site_$($SiteCode)" -Class SMS_DistributionPoint -Filter "PackageID='$($dp.PackageID)' and ServerNALPath like '%$($dp.NALPath.Substring(12,13))%'"
$PackageDP.RefreshNow = $true
$PackageDP.Put()
}
In case you'd like to have an overview of the packages having replication issues with corresponding replication status messages, you could run the following SQL query on your SCCM database:
SELECT SMS_DistributionDPStatus.PackageID, dbo.v_Package.Manufacturer + ' ' + dbo.v_Package.Name + ' ' + dbo.v_Package.[Version] AS [Content Name], REPLACE(dbo.RBAC_SecuredObjectTypes.ObjectTypeName, 'SMS_', '') AS [Content Type], REPLACE(SMS_DistributionDPStatus.Name, '\', '') AS [Distribution Point], SMS_DistributionDPStatus.SiteCode, CASE SMS_DistributionDPStatus.MessageState WHEN 1 THEN 'Success' WHEN 2 THEN 'In Progress' WHEN 4 THEN 'Failed' ELSE 'Unknown' END AS [Status], CASE SMS_DistributionDPStatus.MessageID WHEN 2303 THEN 'Content was successfully refreshed' WHEN 2324 THEN 'Failed to access or create the content share' WHEN 2330 THEN 'Content was distributed to distribution point' WHEN 2384 THEN 'Content hash has been successfully verified' WHEN 2323 THEN 'Failed to initialize NAL' WHEN 2354 THEN 'Failed to validate content status file' WHEN 2357 THEN 'Content transfer manager was instructed to send content to the distribution point' WHEN 2360 THEN 'Status message 2360 unknown' WHEN 2370 THEN 'Failed to install distribution point' WHEN 2371 THEN 'Waiting for prestaged content' WHEN 2372 THEN 'Waiting for content' WHEN 2380 THEN 'Content evaluation has started' WHEN 2381 THEN 'An evaluation task is running. Content was added to the queue' WHEN 2382 THEN 'Content hash is invalid' WHEN 2383 THEN 'Failed to validate content hash' WHEN 2391 THEN 'Failed to connect to remote distribution point' WHEN 2398 THEN 'Content Status not found' WHEN 8203 THEN 'Failed to update package' WHEN 8204 THEN 'Content is being distributed to the distribution point' WHEN 8211 THEN 'Failed to update package' ELSE 'Status message ' + CAST(SMS_DistributionDPStatus.MessageID AS VARCHAR) + ' unknown' END AS [Message], SMS_DistributionDPStatus.GroupCount AS [Group Count] FROM dbo.vSMS_DistributionDPStatus AS SMS_DistributionDPStatus INNER JOIN dbo.RBAC_SecuredObjectTypes ON SMS_DistributionDPStatus.ObjectTypeID = dbo.RBAC_SecuredObjectTypes.ObjectTypeID LEFT OUTER JOIN dbo.v_Package ON SMS_DistributionDPStatus.PackageID = dbo.v_Package.PackageID WHERE (SMS_DistributionDPStatus.MessageState = 4) ORDER BY [Distribution Point] ASC
Monday, June 9, 2014
PowerShell code to query SCCM client utility if a reboot is pending
Inspired by this blog post.
Invoke-WmiMethod -Namespace "ROOT\ccm\ClientSDK" -Class CCM_ClientUtilities -Name DetermineIfRebootPending
Sunday, June 8, 2014
PowerShell script to enable Content Validation on all SCCM DPs in your infrastructure
By default DP content validation is disabled in SCCM 2012.
If you got numerous Distribution Points, it might be tiresome to go and enable it one by one. The script below would do it for your whole infrastructure, it's using SCCM default settings and schedule. You might uncomment schedule definition part, to use your own.
#Script is to enable the enable Content Validation on all SCCM DPs in your infrastructure
#could only be run from "Windows PowerShell (x86)" as it doesn't work with 64 bit PS
cls
Import-Module ($Env:SMS_ADMIN_UI_PATH.Substring(0,$Env:SMS_ADMIN_UI_PATH.Length-5) + '\ConfigurationManager.psd1')
$PSD = Get-PSDrive -PSProvider CMSite
#in case there are more that 1 SCCM sites, we are connecting to the first one
CD "$($PSD[0]):"
#SCCM 2012 site code
$SiteCode = $PSD[0].name
#SCCM 2012 site server
$SCCMserver = $PSD[0].root
$AllDPs = Get-WmiObject -computer $SCCMserver -Namespace root\sms\site_$($SiteCode) -Class SMS_DPStatusInfo -Property NALPath
foreach ($DP in $AllDPs)
{
$ServerFQDN = $DP.NALPath.Substring(12).Split('\')[0]
if ($ServerFQDN)
{
#optionally set the validation schedule (weekly)
#$ValidationSchedule = New-CMSchedule -Start $(Get-Date -Year 2014 -Month 05 -Day 31 -Hour 1 -Minute 0 -Second 0) -DayOfWeek Saturday
#use default SCCM validation schedule (uncomment the trailing part to use the previously defined schedule)
Set-CMDistributionPoint -SiteCode $SiteCode -SiteSystemServerName $ServerFQDN -EnableValidateContent $true #-ValidateContentSchedule $ValidationSchedule
Write-Host "Validation Scheduled on $($ServerFQDN)"
#Write-Host "for $(Get-Date -Year 2014 -Month 05 -Day 31 -Hour 1 -Minute 0 -Second 0)"
}
}
Monday, January 13, 2014
PowerShell script to list SCCM 2012 collections that take long time to evaluate
Needed to list collections that were having evaluation delay issues. To do so, I put together a small script parsing Colleval log files & listing problematic collections.
You might want to have a look at their queries & try to optimize them.
# created by Michael Sedenkov January 13, 2014
# Script listing SCCM 2012 Collections that are taking too long to be evaluated
$inputLogFiles = @("\\SCCM2012Server\logs$\colleval.lo_", "\\SCCM2012Server\logs$\colleval.log")
$evaluationTimeLimit = 60
$outputFile = "c:\temp\CollectionsRefreshDelay.csv"
$SCCMconnection = "\\SCCM2012Server\root\sms\site_SCCM2012Site"
write-Output "CollectionID;CollName;EvalDelaySeconds;Time" | Out-File $OutPutFile
write-host "CollectionID;CollName;EvalDelaySeconds;Time";
foreach ($file in $inputLogFiles) {
cat $file |
Select-String "PF: [Primary Evaluator] successfully evaluated collection" -SimpleMatch |
select -expand line |
foreach {
#write-host $_
$_ -match "PF: \[Primary Evaluator\] successfully evaluated collection \[(\w+)\] and used (\d+\.\d+) seconds \$\$<(.{19})" | out-null
if ([int]$matches[2] -ge $evaluationTimeLimit) {
$ConnectionString = $SCCMconnection + ":SMS_Collection.CollectionID='" + $matches[1] +"'"
$Coll = [wmi]$ConnectionString
write-host $matches[1]"; " $Coll.Name"; " $matches[2]"; " $matches[3]
write-Output "$($matches[1]);$($Coll.Name);$($matches[2]);$($matches[3])" | Out-File $OutPutFile -append
}
}
}
Sunday, January 12, 2014
PowerShell script to inventory SCCM 2012 collections
I was looking for a simple way to inventory hundreds of SCCM 2012 collections we are having & compile all interesting information, like
SCCMCollectionsInformationExport.ps1
- Collection ID (WMI: CollectionID)
- Name (Name)
- Refresh Type (RefreshType)
- Refresh schedule (RefreshSchedule.DaySpan, RefreshSchedule.HourSpan)
- Number of members (MemberCount)
- Limiting Collection Name (LimitToCollectionName)
- Limiting Collection ID (LimitToCollectionID)
- Number of Membership Rules (CollectionRules.Count)
- Exact Membership Rules (CollectionRules.RuleName, CollectionRules.IncludeCollectionID, CollectionRules.ExcludeCollectionID, CollectionRules.ResourceID, CollectionRules.QueryExpression)
SCCMCollectionsInformationExport.ps1
# created by Michael Sedenkov January 10, 2014
# Script listing SCCM 2012 Collections and their properties
# CSV file, where are we are going to export
$OutPutFile = "c:\temp\Collections.csv"
# name of SCCM 2012 server where are we connecting
$SCCMServerName = "CM01"
# name of SCCM 2012 namespace, containing SCCM Site Name
$SCCMNameSpace = "root\sms\site_XXX"
write-Output "CollectionID;CollName;RefreshType;Days;Hours;MemberCount;LimitingCollection;LimitingCollectionID;NumberOfCollectionRules;RuleName1;Rule1;RuleName2;Rule2;RuleName3;Rule3;RuleName4;Rule4" | Out-File $OutPutFile
gwmi sms_collection -computer $SCCMServerName -namespace $SCCMNameSpace | foreach {
$Coll = [wmi] $_.__Path
$CollRulesString = ""
if ($Coll.CollectionRules.Count) {
# in case some collection rules are really defined
$CollRuleCount = $Coll.CollectionRules.Count
foreach ($CollRule in $Coll.CollectionRules) {
#write-host "Collection Rule Name: " $CollRule.RuleName
$CollRulesString = $CollRulesString + $CollRule.RuleName + "; "
if ($CollRule.IncludeCollectionID) {
#write-host "Included collection: " $CollRule.IncludeCollectionID
$CollRulesString = $CollRulesString + "IncludedCollID " + $CollRule.IncludeCollectionID + "; "
}
if ($CollRule.ExcludeCollectionID) {
#write-host "Excluded collection: " $CollRule.ExcludeCollectionID
$CollRulesString = $CollRulesString + "ExcludedCollID " + $CollRule.ExcludeCollectionID + "; "
}
if ($CollRule.ResourceID) {
#write-host "Direct membership: " $CollRule.ResourceID
$CollRulesString = $CollRulesString + "MachineID " + $CollRule.ResourceID + "; "
}
if ($CollRule.QueryExpression) {
#write-host "Collection query: " $CollRule.QueryExpression
$CollRulesString = $CollRulesString + "Query " + $CollRule.QueryExpression + "; "
}
}
}
# if no rules are present
else { $CollRuleCount = 0 }
# convert RefreshType values into descriptions
$CollRefreshT = ""
switch ($Coll.RefreshType) {
1 {$CollRefreshT = "Manual"}
2 {$CollRefreshT = "Schedule"}
4 {$CollRefreshT = "Incremental"}
6 {$CollRefreshT = "Schedule+Incremental"}
}
write-host $Coll.CollectionID "; " $Coll.Name "; " $CollRefreshT "; " $Coll.RefreshSchedule.DaySpan "; " $Coll.RefreshSchedule.HourSpan "; " $Coll.MemberCount "; " $Coll.LimitToCollectionName "; " $Coll.LimitToCollectionID "; " $CollRuleCount "; " $CollRulesString #$Coll.CollectionRules[0].RuleName
Write-Output "$($Coll.CollectionID);$($Coll.Name);$($CollRefreshT);$($Coll.RefreshSchedule.DaySpan);$($Coll.RefreshSchedule.HourSpan);$($Coll.MemberCount);$($Coll.LimitToCollectionName);$($Coll.LimitToCollectionID);$($CollRuleCount);$($CollRulesString)" | Out-File $OutPutFile -Append
}
Subscribe to:
Comments (Atom)

