Udostępnij za pośrednictwem


Windows 7 Deployment Pre-scan check PowerShell script

I was recently tasked to upgrade roughly 5000 Windows XP clients to Windows 7.  The company uses both Microsoft System Center 2007 R3 OSD and MDT 2012 in their environment for building their base image(s) and deploying them to the enterprise.  My previous experiences with working with XP is that there were going to be client issues during this deployment project, so I built a PowerShell script to help identify problems machines as well as help rectify known problems.  I ran this script each day on the collection(s) that were assigned to be upgraded on that night.  Running this script helped us achieve a 90+ success rate for each nights deployment.  The script was constantly expanding as the project grew as more client issues presented themselves.

First, I need to mention that my client would not allow my domain network account to have admin rights on their workstations, but they did give me the local admin credentials to gain access to these machines.  I needed a way to store the local admin credentials and populate the pop-up request for each workstation I wanted to gain access to, rather than have to enter the credentials each time.  I used this one line script to create an encrypted text file that I could use in my main script when calling the credentials.

Read-Host -AsSecureString | ConvertFrom-SecureString | Out-File C:\Temp\Local-Admin-Password.txt

I used PowerShell ISE to run the script(s), so that I could see the results from the scan easily. 

Here is the full code I used:

#Purpose: Script looks at all workstations in AD Security Group, pings them to find which ones are online
#; Gets OS and Computer Model information
#; Checks to see if SCCM, BITs, WMI services are running
#; Checks to see if there is a Software Update stuck in WMI and clears it
#; Checks to see if there is enough disk space and removes CCM Cache contents if disk space is low
#; Checks to see if TEMP profile exists and adds Environment Variable for MDT to bypass error ScanState error
#; Checks WMI by running WMI Scan to repair if needed
#; Trys to wake machine up if offline

#-------------------------------
#   Clear Screen in ISE
#-------------------------------
CLS

#-----------------------
# Error Preference
#-----------------------
$ErrorActionPreference = "SilentlyContinue"

#------------------------
# Declare Variables
#------------------------
$i=1
$ServerName = 'SCCM_SERVER_NAME'
$OS = 'Microsoft Windows XP Professional'
$CollID = 'PRI00328' #SCCM CollectionID

#-------------------------------------------------------------------------------------------------
# Enter credentials from secure text file containing Local Admin password
#-------------------------------------------------------------------------------------------------
$Password = type C:\Temp\Local-Admin-Password.txt | ConvertTo-SecureString
$Creds = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList LocalHost\Cenadmin001, $Password
$NetworkCred = $Creds.GetNetworkCredential()

#--------------------------------------------------------------------------------------------------------------
# Prompt to run WMI Scan on Windows XP machines.  Used for the first run scan
#--------------------------------------------------------------------------------------------------------------
$Results = [System.Windows.Forms.MessageBox]::Show("Do you want to scan the machines for WMI corruption?", "WMI Scan","YesNo","Exclamation")

#-----------------------------------------------------------------
# Get List of computers from SCCM CollectionID
#------------------------------------------------------------------
$Group = Get-WmiObject -ComputerName $ServerName -Namespace "root\sms\site_PRI" -Query "Select * From SMS_FullCollectionMembership where collectionID = '$CollID'" | Select-Object Name

#-------------------
# Functions
#-------------------

#--------------------------------------------------------------------------------------
#  Map connection to each computer using stored credentials to gain access to C$ share
#--------------------------------------------------------------------------------------
Function Permissions {
 net use "\\$ComputerName\c$" $NetworkCred.password /USER:$($NetworkCred.domain)\$($NetworkCred.UserName)
}

#------------------------------------------------------------------------------
#  Gather information about SCCM client and advertisement for each workstation
#------------------------------------------------------------------------------
Function SCCMClientInfo {
 $SCCMClient = (Get-WmiObject -Namespace root\ccm -Class CCM_Client -ComputerName $ComputerName -Credential $Creds).ClientVersion
 Write-host "SCCM Client Version: $SCCMClient"
 $ResourceID = (Get-WmiObject -Namespace root\sms\site_PRI -ComputerName $ServerName -Query "Select * From SMS_R_SYSTEM Where Name='$ComputerName'").ResourceID
 $CS = Get-WmiObject –NameSpace Root\SMS\Site_PRI -ComputerName $ServerName -Query "Select * From SMS_ClientAdvertisementStatus Where ResourceID='$ResourceID'"
 ForEach($_ in $CS){
  $ADV = Get-WmiObject -Namespace root\sms\site_PRI -ComputerName $ServerName -Query "Select * From SMS_Advertisement"
  $PKG = Get-WmiObject -Namespace root\sms\site_PRI -ComputerName $ServerName -Query "Select * From SMS_Package"
  $ADSTAT = Get-WmiObject –NameSpace Root\SMS\Site_PRI  -ComputerName $ServerName -Query "Select * From SMS_ClientAdvertisementStatus"
  $Query = Get-WmiObject -Namespace root\sms\site_PRI -ComputerName $ServerName -Query "Select * From SMS_Advertisement ADV Inner Join SMS_Package PKG on ADV.PackageID = PKG.PackageID Inner Join SMS_ClientAdvertisementStatus CAS on ADV.AdvertisementID = CAS.AdvertismentID WHERE ADV.Advertisement ='$_.AdvertisementID'"
  Write-Host $Query.Name
 
 $DateTime = [System.Management.ManagementDateTimeConverter]::ToDateTime($_.LastStatusTime)
 Write-Host  $_.AdvertisementID   $DateTime   $_.LastStatusMessageIDName
 }
}

#----------------------------------------------------------
#  Make sure CCMEXEC service is running on the workstation
#----------------------------------------------------------
Function SCCMServiceStatus {
 $SCCM = Get-WmiObject -ComputerName $ComputerName -Credential $Creds -Query "Select * From Win32_Service Where Name= 'ccmexec'"
    If($SCCM.Status -ne 'OK'){Write-Host "SCCM Service: " $SCCM.Status -BackgroundColor Red}
 }
 
#----------------------------------------------------------
#  Make sure WMI service is running on the workstation
#----------------------------------------------------------
Function WMIServiceStatus {
 $WMI = Get-WmiObject -ComputerName $ComputerName -Credential $Creds -Query "Select * From Win32_Service Where Name= 'Winmgmt'"
 If($WMI.Status -ne 'OK'){Write-Host "WMI Service: " $WMI.Status -BackgroundColor Red}
 }
 
#----------------------------------------------------------
#  Make sure Bits service is running on the workstation
#----------------------------------------------------------
Function BITsServiceStatus {
 $Bits = Get-WmiObject -ComputerName $ComputerName -Credential $Creds -Query "Select * From Win32_Service Where Name= 'bits'"
 If($Bits.Status -ne 'OK'){Write-Host "Bits Service: " $Bits.Status -BackgroundColor Red}
 }
 
#-------------------------------------
#  Restart SCCM Service when needed
#-------------------------------------
Function SCCMService {   
 #Restart SMS Agent Service
    $SMSAgent = Get-WmiObject Win32_Service -ComputerName $Workstation.name -Credential $Creds -Filter "name= 'CCMExec'" | Restart-Service
    Write-Host "Restarted SMS Agent Service"
 }

#------------------------------
#  Get Computer information
#------------------------------
Function GetPCInfo {
 #Get Computer Information
  $OS = (Get-WmiObject -ComputerName $ComputerName -Credential $Creds -Query "Select * From Win32_OperatingSystem" -Namespace 'root\cimv2').Caption
  If($OS -eq "Microsoft Windows 7 Enterprise "){Write-Host "Operating System: " $OS -BackgroundColor Blue -ForegroundColor White} Else {Write-Host "Operating System: " $OS}
    
  $Model = (Get-WmiObject -ComputerName $ComputerName -Credential $Creds -Query "Select * From Win32_ComputerSystem" -Namespace 'root\cimv2').Model
     $FF = (Get-WmiObject -ComputerName $ComputerName -Credential $Creds -Query "Select * From Win32_SystemEnclosure" -Namespace 'root\cimv2').ChassisTypes 
     Write-Host "Model: " $Model
     If($Model -eq 'Optiplex 760' -and $FF -eq '6'){Write-Host "************* Dell Optiplex 760 is Large Chassis **************" -BackgroundColor Red}
 
  $NICs = Get-WmiObject -ComputerName $ComputerName -Credential $Creds -Query "Select * From Win32_NetworkAdapterConfiguration" -Namespace root\Cimv2
  ForEach ($Card in $NICs){
   If($Card.DHCPEnabled -eq 'TRUE'){
    If($Card.IPAddress -like '150*'){
    Write-Host "Network Card: " $Card.Description
    Write-Host "IP Address: " $Card.IPAddress -BackgroundColor Yellow
    }
   Else{
    Write-Host "Network Card: " $Card.Description
    Write-Host "IP Address: " $Card.IPAddress
    }
   }
  }
 }

#-------------------------------------------------------------------------------------------------------------
#  Run WMI Repair on clients using PSEXEC.  You must have PSEXEC Tools downloaded on your machine to be used.
#-------------------------------------------------------------------------------------------------------------
Function WMIScan {
 #Scan and Repair WMI for Windows XP
  If($Results -eq "Yes"){
   $OS = (Get-WmiObject -ComputerName $ComputerName -Credential $Creds -Query "Select * From Win32_OperatingSystem" -Namespace 'root\cimv2').Caption
   If ($OS -eq "Microsoft Windows XP Professional"){
   Write-Host "Scanning WMI for Corruption"  
   Set-Alias PSEXEC "C:\PSEXEC\PSEXEC.exe"
   & PSEXEC \\$ComputerName cmd /c rundll32 wbemupgd, UpgradeRepository}
  }
 }

#------------------------------------------------------------------------------
#  Determine Free Disk Space on computer to see if there is enough room on it.
#  If there is not enough room for the Windows 7 image to cache to the machine
#  Delete the cache folder contents to see if it will free up enough space
#-------------------------------------------------------------------------------
Function FreeDiskSpace {
 $Disk = Get-WmiObject Win32_LogicalDisk -ComputerName $ComputerName -Credential $Creds -Filter "DeviceID='C:'"
 $FreeSpace = [Math]::Truncate($Disk.Freespace/1GB)
 $DiskSpace = [Math]::Truncate($Disk.Size/1GB)
 $Percentage = [Math]::Truncate(($FreeSpace / $DiskSpace)*100)
 Write-Host "Free Space: "  $FreeSpace GB
 Write-Host "Disk Size: " $DiskSpace GB
 Write-Host "Percentage: " $Percentage %
 
 If ($FreeSpace -lt 10){
  Write-Host "*** Low Disk Space | Deleteing CCM Cache Folder Contents ***" -BackgroundColor Yellow
  $Target = "\\$ComputerName\C$\WINDOWS\SYSTEM32\CCM\CACHE"
  $Folders = Get-ChildItem $Target -Recurse
   ForEach ($File in $Folders){
    $File.Delete()
   }
  }
 }

#--------------------------------------------------------
#  Find a file name that ends with the "." at the end
#--------------------------------------------------------
Function BadFileName {
 #Check for Bad File or Folder Name
 # Write-Host "Checking for Bad File or Folder Name"
 # $Target = "\\$ComputerName\C$\Documents and Settings"
 # $BadFolder = Get-ChildItem $Target -Recurse
 # ForEach($File in $BadFolder){If ($File.name -like '*.'){Write-Host $File.name}}
 }

#-----------------------------------------------------------------------------------------------
#  Find TEMP account on workstation and add environmental variable to bypass the error for USMT
#-----------------------------------------------------------------------------------------------
Function FindTempUser {
 $Target = "\\$ComputerName\C$\Documents and Settings"
 $DocsAndSettings = Get-ChildItem $Target
 ForEach($Profile in $DocsAndSettings){
  If($Profile.name -eq 'Temp'){
  $TempUser = $Profile.Name
  Write-Host "Temp Account Found: " $TempUser -BackgroundColor Red
  Write-Host "Creating Environment Variable for USMT"
  Set-WmiInstance -ComputerName $ComputerName -Credential $Creds -Class Win32_Environment -Argument @{Name="MIG_FAIL_ON_PROFILE_ERROR";VariableValue="0";UserName="<SYSTEM>"}
  }
 }
 
#--------------------------------------------------------
#  Find .BAK registry profile which causes USMT to fail
#--------------------------------------------------------
 #List Registry Profile List where Account has BAK
 $HKLM = 2147483650
 $PCRegistry = Get-WmiObject -List "StdRegProv" -Namespace root\default -ComputerName $ComputerName -Credential $Creds
 $ProfileList = "SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList"
 $Keys = $PCRegistry.EnumKey($HKLM, $ProfileList)
 ForEach ($Profile in $Keys){
  If($Profile.sNames -like '*.bak'){Write-Host "Registry Value: " $Profile.sNames -BackgroundColor Red}
  }
}

#--------------------------------------------------------------------------------------------------------------------
#  Software Updates Component would not stop for the TSManager run the Windows 7 Task Sequence.
#  It was determinded that there were WMI settings there were "stuck", so we used this to remove those settings
#  to continue the deployment process
#--------------------------------------------------------------------------------------------------------------------
Function SoftwareUpdateFix {
 $Key = Get-WmiObject -ComputerName $Workstation.name -Credential $Creds -Query "Select * From CCM_DeploymentTaskEx1" -Namespace 'root\ccm\softwareupdates\deploymentagent'
    If($Key -ne $null){
  If($Key.AssignmentId -eq "{1B4035C9-1667-4EAC-B4A5-E754CE69AE89}"){
  Write-host "WMI AdvertisementID: " $Key.AssignmentId " Found" -BackgroundColor Magenta
  #Delete WMI Keys
  $Key.Delete()
 
  $TSManager = Get-WmiObject Win32_Process -ComputerName $ComputerName -Credential $Creds -Filter "name= 'TSManager.exe'"
  If($TSManager -eq "TSManager.exe"){
  Write-Host "TSManager.exe Running"
  $TSManager.Terminate()
  Write-Host "TSManager.exe Stopped"}
 
  #Delete _SMSTaskSequence Folder
  $TSFolder = Get-WmiObject Win32_Directory -ComputerName $ComputerName -Credential $Creds -Filter "name= 'C:\_SMSTaskSequence'"
  If($TSFolder.name -eq 'C:\_SMSTaskSequence'){
  Write-Host "_SMSTaskSequence Folder Present"
  $TSFolder.Delete()
  Write-Host "_SMSTaskSequence Folder Deleted"}
   }
  }
 
    $Key = Get-WmiObject -ComputerName $Workstation.name -Credential $Creds -Query "Select * From CCM_AtomicUpdateEx1" -Namespace 'root\ccm\softwareupdates\handler'
    If($Key -ne $null){
  If($Key.UpdateID -eq "16c6d7ff-82b6-4fd1-a3ef-d26465967646"){
  Write-host "WMI CCM_AtomicUpdateEx1.UpdateID: " $Key.UpdateID " Found" -BackgroundColor Magenta
  #Delete WMI Keys
  $Key.Delete()}
  }
 
    $Key = Get-WmiObject -ComputerName $Workstation.name -Credential $Creds -Query "Select * From CCM_BundledUpdateEx1" -Namespace 'root\ccm\softwareupdates\handler'
    If($Key -ne $null){
  If($Key.UpdateID -eq "94379166-f4d7-425f-90e0-53c9dc20d9cb"){
  Write-host "WMI CCM_BundledUpdateEx1.UpdateID: " $Key.UpdateID " Found" -BackgroundColor Magenta
  #Delete WMI Keys
  $Key.Delete()}
  }
 
    $Key = Get-WmiObject -ComputerName $Workstation.name -Credential $Creds -Query "Select * From CCM_UpdatesDeploymentJobEx1" -Namespace 'root\ccm\softwareupdates\handler'
    If($Key -ne $null){
  If($Key.UpdateID -eq "{2F3AD737-27F7-49BC-9150-35BD587F6B5D}" -or $Key.UpdateID -eq "{CB0E1256-23B7-44A1-9BF0-89B3D8FD46E4}"){
  Write-Host "WMI CCM_UpdatesDeploymentJobEx1.JobID: " $Key.JobID " Found" -BackgroundColor Magenta
  #Delete WMI Keys
  $Key.Delete()}
  }
 }

#-------------------------------------------------------------------
#  Wake On Lan command for machines that were asleep. 
#  Run a second scan 10 minutes later to connect to these machines
#-------------------------------------------------------------------
Function WOL {
  $MacAddresses = (Get-WmiObject -Namespace "Root\SMS\Site_PRI" -ComputerName $ServerName -Query "Select MACAddresses FROM SMS_R_SYSTEM Where NAME = '$ComputerName'").MACAddresses
 
  ForEach($Mac in $Macaddresses){

   $Mac = ([String]$Mac).split(":")|%{[Byte]"0x$_"}
   $Packet = [Byte[]](,0xFF*6)+($Mac*16)
  
   Write-Host "MacAddress in Binary: $Mac"  
   Write-Host "Broadcast Packet: $([BitConverter]::ToString($Packet))"
   
   #Send WOL Command
   $UDPClient = New-Object System.Net.Sockets.UdpClient
   $UDPClient.Connect(([System.Net.IPAddress]::Broadcast),4000)
   $UDPClient.Send($Packet, $Packet.Length) | Out-Null
   $UDPClient.Close()
  
   Write-Host ""
  }
}

#------------------------------
# Start Script for each machine
#------------------------------
Foreach($PC in $Group){

#Get Computer Name
$ComputerName = $PC.Name

#Ping Each Computer and continue when machine is online
$Ping = New-Object System.Net.NetworkInformation.Ping
$Reply = $Ping.Send($ComputerName)

 If($Reply.status -eq "Success"){
 Write-Host $i"." $ComputerName ": Online"
 $i++
 
 #Submit Permissions to Access Computer(s)
 Permissions
 
 #Call WMIScan Function
 WMIScan

 #Call GetPCInfo Function
 GetPCInfo
 
 #Call Function SCCMClient
 SCCMClient
 
 #Call Function SCCMServiceStatus
 SCCMServiceStatus

 #Call Function BITsServieStatus
 BITsServiceStatus
 
 #Call Function WMIServiceStatus
 WMIServiceStatus

 #Call Function FreeDiskSpace
 FreeDiskSpace

 #Call Function FindTempUser
 FindTempUser
 
 #Call Function SoftwareUpdateFix
 SoftwareUpdateFix

 Write-Host ""

 }  #End If Ping.Status -eq Success
 
 If($Reply.Status -eq "TimedOut"){
        Write-host $i"." $ComputerName ": Offline"
  $i++
  WOL
 }  #End If Ping.Status -eq TimedOut

 Write-Host ""
 
} #End Script