Monday, June 16, 2014

Additional home tab in IE after applying KB2957689

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.

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
# 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)"
 }
}