Mick's IT Blogs

Mick's IT Blogs

Latest Updates

01 July 2015

Laptop Mandatory Reboot Management

Posted By: Mick Pletcher - 5:16 PM
Managing laptops in certain environments can be daunting. Reboots are a must every now and then, especially for monthly windows updates. With the sleep and hibernate features being enabled, the chances of a user rebooting a laptop become far less. A laptop can go weeks and even months without a reboot. Working in the legal industry, as I do, adds to the complexity of forcing reboots as you have the issue of not causing a reboot during a hearing or during a client meeting for instance. You want to be as unobtrusive as possible. You might say that this is not needed as SCCM could be setup to automatically perform this same function on a regular schedule. That is true. Where this becomes valuable is when you don't want to force a reboot on users that regularly reboot their machines. If they are already doing this, which we have a fair number of users that do, then there is no need to reboot an additional time that will inconvenience them.

To make this process as unobtrusive as possible, I have written the following two PowerShell scripts that work in conjunction with SCCM 2012 to give users the leisure of a full business day to reboot the machine. One script is the custom detection method and the other is the installer. 

The custom detection method works by reading the last event viewer 1074. It looks at the date of the ID and then sees if it is 14 days or older. This can be set to any number of days other than the 14 my firm has chosen. If it is 14 days old, the script then sets the registry key Rebooted to a DWORD value of 1 and fails the detection method. When the method fails, SCCM will then run the second PowerShell script. 

The second script operates by running the same detection method. Once it detects the same values, it resets the Rebooted Key to 0 and then returns the error code 3010 back to SCCM. SCCM then reruns the detection method. The detection method sees there has been 14 days or more and the Rebooted key is set to 0. It returns and error code 0 back to SCCM with a write-host of "Success". This tells SCCM the application ran successfully and to now process with the soft reboot, which was required by the 3010 return code. 

The final part is to configure the Computer Restart under the SCCM client settings. I configured ours to be 480 minutes, which is 8 hours, with a mandatory dialog window that cannot be closed the final 60 minutes. 

When the system reboots, the custom detection method runs again and sees there is a new 1074 entry in the event viewer, along with the registry key Rebooted being a 0, therefor it shows successfully installed. As the days progress and SCCM reruns the custom detection method, it will rerun the application script to reboot the machine again if the machine is not rebooted in the 14 allotted days. If the user reboots the machine every week, the SCCM application will never reboot the machine. 

Here are the links to the two scripts:

 <#  
 .SYNOPSIS  
   SCCM Reboot Detection Method  
 .DESCRIPTION  
   This script will read the last time a system rebooted from the event  
   viewer logs. It then calculates the number of days since that time. If  
   the number of days equals or exceeds the RebootThreshold variable, the  
   script will exit with a return code 0 and no data output. No data output   
   is read by SCCM as a failure. If the number of days is less than the  
   RebootThreshold, then a message is written saying the system is within  
   the threshold and the script exits with a return code of 0. SCCM reads  
   an error code 0 with data output as a success.   
 .Author  
   Mick Pletcher  
 .Date  
   30 June 2015  
 #>  
   
 $RebootThreshold = 14  
 $Today = Get-Date  
 $Architecture = Get-WmiObject -Class Win32_OperatingSystem | Select-Object OSArchitecture  
 $Architecture = $Architecture.OSArchitecture  
 $LastReboot = get-winevent -filterhashtable @{logname='system';ID=1074} -maxevents 1 -ErrorAction SilentlyContinue  
 if ($Architecture -eq "32-bit") {  
      if ((Test-Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Reboot") -eq $false) {  
           New-Item "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Reboot" | New-ItemProperty -Name Rebooted -Value 0 -Force | Out-Null  
      }  
      $Rebooted = Get-ItemProperty -Name Rebooted -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Reboot"  
      $Rebooted = $Rebooted.Rebooted  
 } else {  
      if ((Test-Path "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Reboot") -eq $false) {  
           New-Item "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Reboot" | New-ItemProperty -Name Rebooted -Value 0 -Force | Out-Null  
      }  
      $Rebooted = Get-ItemProperty -Name Rebooted -Path "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Reboot"  
      $Rebooted = $Rebooted.Rebooted  
 }  
 if ($LastReboot -eq $null) {  
      $Difference = $RebootThreshold  
 } else {  
      $Difference = New-TimeSpan -Start $Today -End $LastReboot.TimeCreated  
      $Difference = [math]::Abs($Difference.Days)  
 }  
 #Write-Host "Reboot Threshold:"$RebootThreshold  
 #Write-Host "Difference:"$Difference  
 #Write-Host "Rebooted:"$Rebooted  
 if (($Difference -lt $RebootThreshold) -and ($Rebooted -eq 0)) {  
      Write-Host "Success"  
      exit 0  
 }  
 if (($Difference -ge $RebootThreshold) -and ($Rebooted -eq 1)) {  
      Write-Host "Success"  
      exit 0  
 }  
 if (($Difference -ge $RebootThreshold) -and ($Rebooted -eq 0)) {  
      exit 0  
 }  
 if (($Difference -lt $RebootThreshold) -and ($Rebooted -eq 1)) {  
      exit 0  
 }  
   



 <#  
 .SYNOPSIS  
   SCCM Mandatory Reboot  
 .DESCRIPTION  
   This script will read the last time a system rebooted from the event  
   viewer logs. It then calculates the number of days since that time. If  
   the number of days equals or exceeds the RebootThreshold variable, the  
   script will change the registry key Rebooted to a 0. It then exits with   
   an error code 3010, which tells SCCM 2012 to perform a soft reboot.  
 .Author  
   Mick Pletcher  
 .Date  
   30 June 2015  
 #>  
   
 #Declare Global Variables  
 Set-Variable -Name Architecture -Scope local -Force  
 Set-Variable -Name Difference -Scope Local -Force  
 Set-Variable -Name LastReboot -Scope Local -Force  
 Set-Variable -Name Rebooted -Scope Local -Force  
 Set-Variable -Name RebootThreshold -Scope Local -Force  
 Set-Variable -Name Today -Scope Local -Force  
   
 $Architecture = Get-WmiObject -Class Win32_OperatingSystem | Select-Object OSArchitecture  
 $Architecture = $Architecture.OSArchitecture  
 if ($Architecture -eq "32-bit") {  
      if ((Test-Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Reboot") -eq $false) {  
           New-Item "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Reboot" | New-ItemProperty -Name Rebooted -Value 0 -Force | Out-Null  
      }  
      $Rebooted = Get-ItemProperty -Name Rebooted -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Reboot"  
      $Rebooted = $Rebooted.Rebooted  
 } else {  
      if ((Test-Path "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Reboot") -eq $false) {  
           New-Item "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Reboot" | New-ItemProperty -Name Rebooted -Value 0 -Force | Out-Null  
      }  
      $Rebooted = Get-ItemProperty -Name Rebooted -Path "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Reboot"  
      $Rebooted = $Rebooted.Rebooted  
 }  
 $RebootThreshold = 14  
 $Today = Get-Date  
 $LastReboot = get-winevent -filterhashtable @{logname='system';ID=1074} -maxevents 1 -ErrorAction SilentlyContinue  
 if ($LastReboot -eq $null) {  
      $Difference = $RebootThreshold 
 } else {  
      $Difference = New-TimeSpan -Start $Today -End $LastReboot.TimeCreated  
      $Difference = [math]::Abs($Difference.Days)  
 }  
 if (($Difference -ge $RebootThreshold) -and ($Rebooted -eq 0)) {  
      if ((Test-Path "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Reboot") -eq $true) {  
           Set-ItemProperty -Path "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Reboot" -Name Rebooted -Value 1 -Type DWORD -Force  
           $Rebooted = Get-ItemProperty -Name Rebooted -Path "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Reboot"  
           $Rebooted = $Rebooted.Rebooted  
      } else {  
           Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Reboot" -Name Rebooted -Value 1 -Type DWORD -Force  
           $Rebooted = Get-ItemProperty -Name Rebooted -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Reboot"  
           $Rebooted = $Rebooted.Rebooted  
      }  
 }  
 if (($Difference -lt $RebootThreshold) -and ($Rebooted -eq 1)) {  
      if ((Test-Path "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Reboot") -eq $true) {  
           Set-ItemProperty -Name Rebooted -Value 0 -Path "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Reboot" -Type DWORD -Force  
           $Rebooted = Get-ItemProperty -Name Rebooted -Path "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Reboot"  
           $Rebooted = $Rebooted.Rebooted  
      } else {  
           Set-ItemProperty -Name Rebooted -Value 0 -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Reboot" -Type DWORD -Force  
           $Rebooted = Get-ItemProperty -Name Rebooted -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Reboot"  
           $Rebooted = $Rebooted.Rebooted  
      }  
      Write-Host "System is within"$RebootThreshold" Day Reboot Threshold"  
 }  
 Write-Host "Reboot Threshold:"$RebootThreshold  
 Write-Host "Difference:"$Difference  
 Write-Host "Rebooted:"$Rebooted  
   

16 June 2015

Deploying Workshare Professional

Posted By: Mick Pletcher - 10:00 AM





Deloying Workshare Professsional is by no means an easy task for configuring the installation if you try and edit the MSI with ORCA. When configuring the MSI to customize the installation of Workshare for office and document management integration, I found the easiest way is to set specific registry keys before the install takes place. If the keys are set first, the MSI will read those keys and configure the app during the installation. Once these keys are installed, you can then run the WorkshareProfessional.msi with the switches /qb- or /qn and nothing else. Of course I installed the prerequisites first before executing the Worshare msi.

32-Bit System:
[HKEY_LOCAL_MACHINE\SOFTWARE\Workshare\Install]
"ProfInterwovenModule"=dword:00000000
"ProfCompareModule"=dword:00000001
"ProfHummingbirdModule"=dword:00000000
"OfficeExcelIntegration"=dword:00000000
"OfficeOutlookIntegration"=dword:00000000
"OfficePowerPointIntegration"=dword:00000000
"OfficeWordIntegration"=dword:00000000
"ProfNetDocumentsModule"=dword:00000001
"ProfWorldoxModule"=dword:00000000
"ProfProtectModule"=dword:00000000
"ProfReviewModule"=dword:00000000
"ProfSecureFileTransferModule"=dword:00000000
"ProfSharepointModule"=dword:00000000

64-Bit System:
[HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Workshare\Install]
"ProfInterwovenModule"=dword:00000000
"ProfCompareModule"=dword:00000001
"ProfHummingbirdModule"=dword:00000000
"OfficeExcelIntegration"=dword:00000000
"OfficeOutlookIntegration"=dword:00000000
"OfficePowerPointIntegration"=dword:00000000
"OfficeWordIntegration"=dword:00000000
"ProfNetDocumentsModule"=dword:00000001
"ProfWorldoxModule"=dword:00000000
"ProfProtectModule"=dword:00000000
"ProfReviewModule"=dword:00000000
"ProfSecureFileTransferModule"=dword:00000000
"ProfSharepointModule"=dword:00000000


14 April 2015

Move PowerShell console during script execution

Posted By: Mick Pletcher - 10:32 PM














I heavily use PowerShell during the SCCM imaging process for installing each application. There are often times that I will watch the build to make sure everything is working correctly. When it calls a task sequence which calls a PowerShell script, the script will often open up in the top left of the screen and is covered up by the build status window. This function and the AutoIT script will allow for the console window to be moved while the script is executing. This does require executing the AutoIT script from the powershell script. There have been other methods shown for doing this, but from what I have seen, they are far more cumbersome.


AutoIT Script
 Sleep(500)  
 $WindowName = $CmdLine[1]  
 If WinExists($WindowName) Then  
      $aWinGetPos = WinGetPos($WindowName)  
      $Y = (@DesktopHeight / 2) - ($aWinGetPos[2] / 3)  
      $X = (@DesktopWidth / 2) - ($aWinGetPos[3] / 1)  
      WinMove($WindowName, "", $X, $Y)  
 EndIf  
   


PowerShell Function
 function MoveConsoleWindow {  
      $WindowName = $Host.ui.RawUI.WindowTitle  
      start-process -filepath "\\FileShare\MovePowerShellWindow\CenterPowerShellWindow.exe" -argumentlist $WindowName  
 }  
   

10 April 2015

Application List

Posted By: Mick Pletcher - 10:53 AM





This script will generate a list of installed applications minus those in the exclusion list text file. I created this script so the help desk could have a concise list of applications that need to be installed post-build. The script excludes all apps that are in the exclusion list, which includes everything in our build. I wrote the script so that it can be executed from SCCM on a per system basis. It could be modified to allow it to execute on remote machines.

The first thing to do is to create the ExclusionList.txt file and place it in the same directory as the script. Next is create a package in SCCM. That is all that is to it.

You can download the script from here.


 <#  
 .SYNOPSIS  
   Installed Applications  
 .DESCRIPTION  
   This will retrieve the list of installed applications from   
   add/remove programs  
 .Author  
   Mick Pletcher  
 .Date  
   09 April 2015  
 .EXAMPLE  
   powershell.exe -executionpolicy bypass -file InstalledApplications.ps1  
 #>  
   
 #Declare Global Variables  
 Set-Variable -Name Architecture -Scope Global -Force  
 Set-Variable -Name Applications -Scope Global -Force  
 Set-Variable -Name LogFile -Value "c:\Applications.csv" -Scope Global -Force  
 Set-Variable -Name RelativePath -Scope Global -Force  
   
 function Get-RelativePath {  
      $Global:RelativePath = (split-path $SCRIPT:MyInvocation.MyCommand.Path -parent)+"\"  
 }  
   
 function Get-Architecture {  
      $Global:Architecture = Get-WmiObject -Class Win32_OperatingSystem | Select-Object OSArchitecture  
      $Global:Architecture = $Global:Architecture.OSArchitecture  
      #Returns 32-bit or 64-bit  
 }  
   
 function CreateLogFile {  
      #Define Local Variables  
      Set-Variable -Name Output -Scope Local -Force  
      Set-Variable -Name Temp -Scope Local -Force  
        
      if ((Test-Path $Global:LogFile) -eq $true) {  
           Remove-Item -Path $Global:LogFile -Force  
      }  
      if ((Test-Path $Global:LogFile) -eq $false) {  
           $Temp = New-Item -Path $Global:LogFile -ItemType file -Force  
      }  
      $Output = "Application Name"  
      Out-File -FilePath $Global:LogFile -InputObject $Output -Append -Force -Encoding UTF8  
                       
        
      #Cleanup Local Variables  
      Remove-Variable -Name Output -Scope Local -Force  
      Remove-Variable -Name Temp -Scope Local -Force  
 }  
   
 function GetAddRemovePrograms {  
      #Define Local Variables  
      Set-Variable -Name Applicationsx86 -Scope Local -Force  
      Set-Variable -Name Applicationsx64 -Scope Local -Force  
      Set-Variable -Name ARPx86 -Scope Local -Force  
      Set-Variable -Name ARPx64 -Scope Local -Force  
        
      $ARPx86 = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\"  
      $ARPx64 = "HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\"  
      if ($Global:Architecture -eq "32-bit") {  
           $Applicationsx86 = Get-ChildItem -Path $ARPx86 | ForEach-Object -Process {$_.GetValue("DisplayName")}  
      } else {  
           $Applicationsx86 = Get-ChildItem -Path $ARPx64 | ForEach-Object -Process {$_.GetValue("DisplayName")}  
           $Applicationsx64 = Get-ChildItem -Path $ARPx86 | ForEach-Object -Process {$_.GetValue("DisplayName")}  
      }  
      $Global:Applications = $Applicationsx86 + $Applicationsx64  
      $Global:Applications = $Global:Applications | Sort-Object  
      $Global:Applications = $Global:Applications | select -Unique  
        
      #Cleanup Local Memory  
      Remove-Variable -Name Applicationsx86 -Force  
      Remove-Variable -Name Applicationsx64 -Force  
      Remove-Variable -Name ARPx86 -Force  
      Remove-Variable -Name ARPx64 -Force  
 }  
   
 function GenerateReport {  
      #Define Local Variables  
      Set-Variable -Name Application -Scope Local -Force  
      Set-Variable -Name Exclusions -Scope Local -Force  
      Set-Variable -Name LogFile -Scope Local -Force  
      Set-Variable -Name Print -Value $true -Scope Local -Force  
        
      $Exclusions = Get-Content -Path $RelativePath"ExclusionList.txt"  
      foreach ($Application in $Global:Applications) {  
           if ($Application -ne $null) {  
                $Application = $Application.Trim()  
           }  
           if (($Application -ne "") -and ($Application -ne $null)) {  
                foreach ($Exclusion in $Exclusions) {  
                     $Exclusion = $Exclusion.Trim()  
                     if ($Application -like $Exclusion) {  
                          $Print = $false  
                     }  
                }  
                if ($Print -eq $true) {  
                     Write-Host $Application  
                     Out-File -FilePath $Global:LogFile -InputObject $Application -Append -Force -Encoding UTF8  
                }  
           }  
           $Print = $true  
      }  
   
      #Cleanup Local Variables  
      Remove-Variable -Name Application -Scope Local -Force  
      Remove-Variable -Name Exclusions -Scope Local -Force  
      Remove-Variable -Name LogFile -Scope Local -Force  
      Remove-Variable -Name Print -Scope Local -Force  
 }  
   
 cls  
 Get-RelativePath  
 Get-Architecture  
 CreateLogFile  
 GetAddRemovePrograms  
 GenerateReport  
   
 #Cleanup Global Variables  
 Remove-Variable -Name Architecture -Force  
 Remove-Variable -Name Applications -Force  
 Remove-Variable -Name LogFile -Force  
 Remove-Variable -Name RelativePath -Force  
   

08 April 2015

SCCM PowerShell SCUP Alternative

Posted By: Mick Pletcher - 11:10 AM












I have wanted to be able to update an application on the fly without having to create new application packages in SCCM. This is especially useful for applications that are updated quite frequently, such as Java Runtime Environment. This can be done by using the custom script detection method. The following script I wrote will go out and extract the product code from the MSI installer located in the source installation directory. It then looks at the HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{GUID} to make sure the application is installed. If that registry key is missing, SCCM will begin installation of the new software.

To make this work correctly, you will need to use the Uninstall-MSIbyName function included in my Deployment Module and listed below. Some installers uninstall the old version, but for best results, I always make sure I perform the uninstall before the install takes place. This will allow for the install script to uninstall the old version and then the installer can come back and install the new version. This function allows for you to input just a portion of the application name listed in Add/Remove programs. An example is Java. In my Java installer, I use the line below which uninstalls all Java version 8 packages:

Uninstall-MSIByName -ApplicationName "Java 8" -Switches "/qb- /norestart"

Next, you will need to rename the MSI installer to a generic name. I called mine JREx86.msi and JREx64.msi. When a new version comes out, I delete these and rename the new version to the same name. The powershell installation package will always look for the same named installer. Once you replace the MSI files, all that is required is to go into the SCCM console, select the deployment, and click Update Content. Once the content is updated on the distribution points and systems begin running the Application Deployment Evaluation Cycle, the package will begin installing the new version of the software.

This script only covers installations involving MSI files. I am also going to write a detection method to cover EXE files too. 

 Function Uninstall-MSIByName {  
      <#  
      .SYNOPSIS  
           Uninstall-MSIByName  
      .DESCRIPTION  
           Uninstalls an MSI application using the MSI file  
      .EXAMPLE  
           Uninstall-MSIByName -ApplicationName "Adobe Reader" -Switches "/qb- /norestart"  
      #>  
   
      Param([String]$ApplicationName, [String]$Switches)  
      $Uninstall = Get-ChildItem HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall -Recurse -ea SilentlyContinue  
      $Uninstall += Get-ChildItem HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall -Recurse -ea SilentlyContinue  
      $SearchName = "*"+$ApplicationName+"*"  
      $Executable = $Env:windir+"\system32\msiexec.exe"  
      Foreach ($Key in $Uninstall) {  
           $TempKey = $Key.Name -split "\\"  
           If ($TempKey[002] -eq "Microsoft") {  
                $Key = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\"+$Key.PSChildName  
           } else {  
                $Key = "HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\"+$Key.PSChildName  
           }  
           If ((Test-Path $Key) -eq $true) {  
                $KeyName = Get-ItemProperty -Path $Key  
                If ($KeyName.DisplayName -like $SearchName) {  
                     $TempKey = $KeyName.UninstallString -split " "  
                     If ($TempKey[0] -eq "MsiExec.exe") {  
                          $Output = "Uninstall "+$KeyName.DisplayName+"....."  
                          Write-Host "Uninstall"$KeyName.DisplayName"....." -NoNewline  
                          $Parameters = "/x "+$KeyName.PSChildName+[char]32+$Switches  
                          $ErrCode = (Start-Process -FilePath $Executable -ArgumentList $Parameters -Wait -Passthru).ExitCode  
                          If (($ErrCode -eq 0) -or ($ErrCode -eq 3010) -or ($ErrCode -eq 1605)) {  
                               $Output = $Output+"Success"  
                               Write-Host "Success" -ForegroundColor Yellow  
                          } else {  
                               $Output = $Output+"Failed with error code "+$ErrCode  
                               Write-Host "Failed with error code "$ErrCode -ForegroundColor Red  
                               $Global:Errors++  
                          }  
                          Out-File -FilePath $Global:LogFile -InputObject $Output -Append -Force  
                     }  
                }  
           }  
      }  
 }  
   


 <#  
 .SYNOPSIS  
   SCCM Application Detection for Java Runtime Environment  
 .DESCRIPTION  
   This script will detect if the latest version of Java is installed by performing a query  
   on the MSI for the product key. It then searches the uninstall registry keys for a matching  
   GUID. If it does match, then the script returns a 0 back to SCCM. If it does not match,  
   then the script does not return any code back to SCCM.  
 .Author  
   Mick Pletcher  
 .Date  
   07 April 2015  
 #>  
   
 $Architecture = Get-WmiObject -Class Win32_OperatingSystem | Select-Object OSArchitecture  
 $Architecture = $Architecture.OSArchitecture  
 If ($Architecture -eq "32-bit") {  
      [string]$MSIPath = "\\FileShare\Oracle\JavaTest\JREx86.msi"  
 } else {  
      [string]$MSIPath = "\\FileShare\Oracle\JavaTest\JREx64.msi"  
 }  
 #"ProductCode","ProductVersion","ProductName"  
 [string]$Property = "ProductCode"  
 $WindowsInstaller = New-Object -ComObject WindowsInstaller.Installer  
 $MSIDatabase = $WindowsInstaller.GetType().InvokeMember("OpenDatabase","InvokeMethod",$Null,$WindowsInstaller,@($MSIPath,0))  
 $Query = "SELECT Value FROM Property WHERE Property = '$($Property)'"  
 $View = $MSIDatabase.GetType().InvokeMember("OpenView","InvokeMethod",$null,$MSIDatabase,($Query))  
 $View.GetType().InvokeMember("Execute", "InvokeMethod", $null, $View, $null)  
 $Record = $View.GetType().InvokeMember("Fetch","InvokeMethod",$null,$View,$null)  
 $Value = $Record.GetType().InvokeMember("StringData","GetProperty",$null,$Record,1)  
 If ($Architecture -eq "32-bit") {  
      $RegPath = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\"+$Value  
 } else {  
      $RegPath = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\"+$Value  
 }  
 If ((Test-Path $RegPath) -eq $true) {  
      Return 0  
 } else {}  
   

06 April 2015

Get Default Printer

Posted By: Mick Pletcher - 12:55 PM





This script will find the default printer of a logged on user and write it to a text file named DefaultPrinter.txt in the %APPDATA% folder. I have this script run as a package in SCCM on a reoccurring weekly basis. This keeps the file up to date in the event a user needs a new machine or a new profile.

You can download the file from here: GetDefaultPrinter.ps1



 <#  
 .SYNOPSIS  
   Get Default Printer  
 .DESCRIPTION  
   Gets the default printer and writes the printer to a text file in the %APPDATA% folder. If  
   this is executed through SCCM, it must be run as the user.  
 .Author  
   Mick Pletcher  
 .Date  
   06 April 2015  
 .EXAMPLE  
   powershell.exe -executionpolicy bypass -file GetDefaultPrinter.ps1  
 #>  
   
 #Declare Global Variables  
 Set-Variable -Name DefaultPrinter -Scope Global -Force  
   
 cls  
 If ((Test-Path $env:APPDATA"\DefaultPrinter.txt") -eq $true) {  
      Remove-Item -Path $env:APPDATA"\DefaultPrinter.txt" -Force  
 }  
 $DefaultPrinter = Get-WmiObject -Class win32_printer -ComputerName "localhost" -Filter "Default='true'" | Select-Object ShareName  
 Write-Host "Default Printer: " -NoNewline  
 If ($DefaultPrinter.ShareName -ne $null) {  
      $DefaultPrinter.ShareName | Out-File -FilePath $env:APPDATA"\DefaultPrinter.txt" -Force -Encoding "ASCII"  
      Write-Host $DefaultPrinter.ShareName  
 } else {  
      $DefaultPrinter = "No Default Printer"  
      $DefaultPrinter | Out-File -FilePath $env:APPDATA"\DefaultPrinter.txt" -Force -Encoding "ASCII"  
      Write-Host $DefaultPrinter  
 }  
   
 #Cleanup Global Variables  
 Remove-Variable -Name DefaultPrinter -Scope Global -Force  
   

27 March 2015

OS Detection

Posted By: Mick Pletcher - 11:37 AM





Recently, we added the Windows 8.1 operating system to the company domain. I ran into the problem of deploying Adobe Flash Player to these systems because the ActiveX does not get installed. The issue came up in SCCM where I needed a detection method for the OS using file, registry, or product ID. I needed a way to detect the plugin and windows 8.1 to be able to validate a successful deployment. This gave me the idea to write a powershell script to place a file in the windows directory that is named for the operating system that is running. It makes for easy OS detection in SCCM for future deployments.



 <#  
 .SYNOPSIS  
   Operating System  
 .DESCRIPTION  
   This script will detect what operating system is installed  
   and write a file named for that OS to the windows directory.  
 .EXAMPLE  
   powershell.exe -executionpolicy bypass -file OperatingSytem.ps1  
 #>  
   
 cls  
 $OS = Get-WmiObject -Class Win32_OperatingSystem -ComputerName .  
 Switch ($OS.Version) {  
      5.0.2195 {New-Item -Name "Windows 2000" -Path $env:windir -ItemType File}  
      5.1.2600 {New-Item -Name "Windows XP" -Path $env:windir -ItemType File}  
      5.2.3790 {New-Item -Name "Windows XP 64-Bit" -Path $env:windir -ItemType File}  
      6.0.6000 {New-Item -Name "Windows Vista" -Path $env:windir -ItemType File}  
      6.0.6001 {New-Item -Name "Windows Vista SP1" -Path $env:windir -ItemType File}  
      6.0.6002 {New-Item -Name "Windows Vista SP2" -Path $env:windir -ItemType File}  
      6.1.7600 {New-Item -Name "Windows 7" -Path $env:windir -ItemType File}  
      6.1.7601 {New-Item -Name "Windows 7 SP1" -Path $env:windir -ItemType File}  
      6.2.9200 {New-Item -Name "Windows 8" -Path $env:windir -ItemType File}  
      6.3.9600 {New-Item -Name "Windows 8.1" -Path $env:windir -ItemType File}  
 }  
   

27 February 2015

Cleaning up old systems in Active Directory, SCCM, and Antivirus

Posted By: Mick Pletcher - 1:19 PM
Every place I have worked, there has been the issue of systems being in SCCM, AD, and antivirus that no longer existed. The is often caused by systems being overlooked when a user departs the company, a laptop that gets put in a desk and not turned on forever, and a lot of other similar scenarios.

While I was in the process of upgrading SCCM 2007 to 2012, I finally became fed up over it and wrote this script. The script uses All Systems from SCCM as the master list. Here is why. The way I have the SCCM configured is that All Systems is populated by AD. Logically, that means that even if a system has been deleted in AD, it might still be present in SCCM. We use a third party antivirus, so I integrated it by importing a list of all systems it showed with the antivirus client.

The script will first read a system from the SCCM list named SCCMSystems.txt. It reads the antivirus systems from a text file called AntivirusSystems.txt. The text files should be in UTF-8 format. It then runs through three tests. The first test is to query AD. The second test is to check if the system name is included in the antivirus list. The third test is to run a network connectivity test. If the network connectivity test fails, it runs a secondary ping test and reads the output of ping.exe to see if it returns host not found. The Network Connectivity test fails only if a host not found is returned. It passes for any other message because it could be a laptop that is offline at the time.

There is a csv file that is written to for all failures. The csv file contains two primary columns, deletions and additions. Under the deletions, it has AD, antivirus, and SCCM. Under the additions if antivirus. An X is placed in each box indicating the recommended action.

If you do not want/need the antivirus, you can delete that portion out of the script. This script is only a primer for instituting in your organization. It will need to be adapted to your network.

You can download the script from here.




 <#  
 .SYNOPSIS  
   ValidateSystems  
 .DESCRIPTION  
   Validate if a system still exists  
 .Author  
   Mick Pletcher  
 .Date  
   26 February 2015  
 .EXAMPLE  
   powershell.exe -executionpolicy bypass -file ValidateSystems.ps1  
 #>  
   
 Function InitializeGlobalMemory {  
      Set-Variable -Name Computers -Scope Global -Force  
      Set-Variable -Name Logfile -Scope Global -Force  
      Set-Variable -Name RelativePath -Scope Global -Force  
      Set-Variable -Name Webroot -Scope Global -Force  
      $Global:Failures = @()  
      $Global:RelativePath = (split-path $SCRIPT:MyInvocation.MyCommand.Path -parent)+"\"   
      $Global:LogFile = $Global:RelativePath + "Output.csv"  
      $Global:Computers = Get-Content -Path $Global:RelativePath"SCCMSystems.txt" -Force  
      $Global:Webroot = Get-Content -Path $Global:RelativePath"AntivirusSystems.txt" -Force  
   
 }  
   
 Function ProcessLogFile {  
      If ((Test-Path $Global:LogFile) -eq $true) {  
           Remove-Item $Global:LogFile -Force  
      }  
      If ((Test-Path $Global:LogFile) -eq $false) {  
           $temp = New-Item $Global:LogFile -ItemType File -Force  
           $Output = ","+"Deletions"+","+","+","+"Additions"  
           Out-File -FilePath $Global:LogFile -InputObject $Output -Append -Force -Encoding UTF8  
           $Output = "Computer Name"+","+"Active Directory"+","+"SCCM"+","+"Antivirus"+","+"Antivirus"  
           Out-File -FilePath $Global:LogFile -InputObject $Output -Append -Force -Encoding UTF8  
      }  
 }  
   
 Function ProcessComputers {  
      $obj = New-Object PSObject  
      $Count = 0  
      Foreach ($Computer in $Global:Computers) {  
           cls  
           $Antivirus = $false  
           $Count += 1  
           Write-Host "Processing "$Count" of " -NoNewline  
           Write-Host $Global:Computers.Count  
           Write-Host  
           Write-Host "Computer Name: "$Computer  
           $ADAccount = $null  
           #Active Directory  
           Write-Host "Testing AD Presence....." -NoNewline  
           $ErrorActionPreference = 'SilentlyContinue'  
           $ADAccount = Get-ADComputer $Computer  
           If ($ADAccount -eq $null) {  
                $ADAccount = $false  
                Write-Host "Does not Exist" -ForegroundColor Red  
           } else {  
                $ADAccount = $true  
                Write-Host "Exists" -ForegroundColor Yellow  
           }  
           #Antivirus  
           Write-Host "Testing Antivirus....." -NoNewline  
           Foreach ($system in $Global:Webroot) {  
                If ($system -eq $Computer) {  
                     $Antivirus = $true  
                }  
           }  
           If ($Antivirus -eq $true) {  
                Write-Host "Exists" -ForegroundColor Yellow  
           } else {  
                Write-Host "Does not exist" -ForegroundColor Red  
           }  
           #Network Connectivity  
           Write-Host "Testing Network Connectivity....." -NoNewline  
           If ((Test-Connection -ComputerName $Computer -Quiet) -eq $false) {  
                $NetworkTest = ping $Computer  
                If ($NetworkTest -like '*Ping request could not find host*') {  
                     $NetworkTest = $false  
                     Write-Host "Does not exist" -ForegroundColor Red  
                } else {  
                     $NetworkTest = $true  
                     Write-Host "Exists" -ForegroundColor Yellow  
                }  
           } else {  
                $NetworkTest = $true  
                Write-Host "Exists" -ForegroundColor Yellow  
           }  
           If (($ADAccount -eq $true) -and ($NetworkTest -eq $false)) {  
                #Write-Host $Computer -NoNewline  
                #Write-Host " - Delete from AD and SCCM" -BackgroundColor Yellow -ForegroundColor Black  
                $obj | Add-Member -MemberType NoteProperty -Name ComputerName -Value $Computer  
                If ($Antivirus -eq $true) {  
                     $obj | Add-Member -MemberType NoteProperty -Name Action -Value "AD_SCCM_Antivirus"  
                } else {  
                     $obj | Add-Member -MemberType NoteProperty -Name Action -Value "AD_SCCM"  
                }  
           }  
           If (($ADAccount -eq $false) -and ($NetworkTest -eq $false)) {  
                #Write-Host $Computer -NoNewline  
                #Write-Host " - Delete from SCCM" -BackgroundColor Green -ForegroundColor Black  
                $obj | Add-Member -MemberType NoteProperty -Name ComputerName -Value $Computer  
                If ($Antivirus -eq $true) {  
                     $obj | Add-Member -MemberType NoteProperty -Name Action -Value "SCCM_Antivirus"  
                } else {  
                     $obj | Add-Member -MemberType NoteProperty -Name Action -Value "SCCM"  
                }  
           }  
           If (($ADAccount -eq $true) -and ($NetworkTest -eq $true) -and ($Antivirus -eq $false)) {  
                $obj | Add-Member -MemberType NoteProperty -Name ComputerName -Value $Computer  
                $obj | Add-Member -MemberType NoteProperty -Name Action -Value "AddAntivirus"  
           }  
           If ($obj -ne $null) {  
                $Global:Failures += $obj  
                $Output = $obj.ComputerName  
                If ($obj.Action -eq "AD_SCCM") {  
                     $Output = $Output + ","+"X"+","+"X"   
                }  
                If ($obj.Action -eq "AD_SCCM_Antivirus") {  
                     $Output = $Output + ","+"X"+","+"X"+","+"X"  
                }  
                If ($obj.Action -eq "SCCM") {  
                     $Output = $Output + ","+","+"X"  
                }  
                If ($obj.Action -eq "SCCM_Antivirus") {  
                     $Output = $Output + ","+","+"X"+","+"X"  
                }  
                If ($obj.Action -eq "AddAntivirus") {  
                     $Output = $Output + ","+","+","+","+"X"  
                }  
                Out-File -FilePath $Global:LogFile -InputObject $Output -Append -Force -Encoding UTF8  
                Remove-Variable -Name obj  
                $obj = New-Object PSObject  
           }  
           Start-Sleep -Seconds 1  
      }  
 }  
   
 Function WriteToScreen {  
      cls  
      Foreach ($Failure in $Global:Failures) {  
           If ($Failure -ne $null) {  
                Write-Output $Failure  
           }  
      }  
      $ErrorActionPreference = 'Continue'  
 }  
   
 cls  
 Import-Module -Name ActiveDirectory  
 InitializeGlobalMemory  
 ProcessLogFile  
 ProcessComputers  
 WriteToScreen  
   

23 February 2015

Import and Apply Local GPOs

Posted By: Mick Pletcher - 10:23 PM





This script will import and apply a local GPO using the local GPO utility, ImportRegPol.exe, located here. The script is a wrapper that makes implementing this utility a snap. All that has to be done is to use the Microsoft Security Compliance Manager to export the desired local GPO. I wrote this script for use mainly in the MDT build. I realize there is the GPO Pack built into MDT, but what happens when you want to deploy a local GPO to machines already built or multiple local GPOs at different times in a build? This script makes that easy.

The syntax for the function is as follows:

Syntax:
Import-LGPO -LGOPName "User Friendly Name" -LGPOLocation "<Path_To_GPO_GUID>" -GPOType "Machine"

Example:
Import-LGPO -LGOPName "Disable Network Wait" -LGPOLocation "\\Fileshare\LGPO\{57D203F7-B8CE-47BC-920F-CECF34F6A6BA}" -GPOType "Machine"

You can download the script from here.



 <#  
 .SYNOPSIS  
   Apply Local Group Policy  
 .Author  
   Mick Pletcher  
 .Date  
   23 February 2015  
 .EXAMPLE  
   powershell.exe -executionpolicy bypass -file LGPO.ps1  
 #>  
   
   
 Function Import-LGPO {  
   
      Param([String]$LGPOName, [String]$LGPOLocation, [String]$GPOType)  
        
      $Executable = $Global:RelativePath+"ImportRegPol.exe"  
      If ($GPOType -eq "Machine") {  
           $GPOType = "\DomainSysvol\GPO\Machine\registry.pol"  
      } else {  
           $GPOType = "\DomainSysvol\GPO\User\registry.pol"  
      }  
      $Parameters = "-m "+[char]34+$LGPOLocation+$GPOType+[char]34  
      Write-Host "Apply Local"$LGPOName" Policy....." -NoNewline  
      $ErrCode = (Start-Process -FilePath $Executable -ArgumentList $Parameters -Wait -Passthru).ExitCode  
      If (($ErrCode -eq 0) -or ($ErrCode -eq 3010)) {  
           Write-Host "Success" -ForegroundColor Yellow  
      } else {  
           Write-Host "Failed with error code "$ErrCode -ForegroundColor Red  
      }  
   
 }  
   
 cls  
 $Global:RelativePath = (split-path $SCRIPT:MyInvocation.MyCommand.Path -parent)+"\"  
 Import-LGPO -LGPOName "User Friendly Name" -LGPOLocation "<Path_To_GPO_GUID>" -GPOType "Machine"  
 Start-Sleep -Seconds 5  
   

18 February 2015

SCCM 2012 R2 Client Uninstalls after Reboot

Posted By: Mick Pletcher - 10:37 PM














I encountered an issue with SCCM 2012 R2 when deploying the client to systems that were being upgraded from SCCM 2007. What would happen was that I pushed the client to the system through the console using the Install Client option. The client cleanly uninstalled the 2007 client and installed the 2012 client with no issues. Upon the first reboot of the system, the client would be completely uninstalled. The logs showed little information. The next thing I did was to manually install the client from a command line thinking there might be something going on when the server was trying to administer the installation. The same thing happened. The only option I could think of was to delete the computer from sccm and then manually reinstall the client. This worked and the client remained installed on the system.

14 February 2015

List all Local Administrators on a Computer

Posted By: Mick Pletcher - 1:19 PM














I wrote this script to generate a list of local administrators on a PC. It saves the output to a text file at a central repository. The text file is named the computer name and contains a listing of all the local administrators. It can be pushed out to run as a package in SCCM. The second script will read all of the text files and create a csv file with all of the combined data.

You can download the script below:



 <#  
 .SYNOPSIS  
   Get a list of local administrators on a machine  
 .DESCRIPTION  
   Get a list of local administrators on a machine and write the list to  
   a csv file on a remote share.  
 .Author  
   Mick Pletcher  
 .Date  
   14 February 2015  
 .EXAMPLE  
   powershell.exe -executionpolicy bypass -file LocalAdministrators.ps1  
 #>  
   
 cls  
 $LocalAdmins = @()  
 $Members = net localgroup administrators | where {$_ -AND $_ -notmatch "command completed successfully"} | select -skip 4  
 $Profiles = Get-ChildItem -Path $env:SystemDrive"\users" -Force  
 $LogFile = "\\NetworkLocation\"+$env:COMPUTERNAME+".log"  
   
 Foreach ($Member in $Members) {  
      $Member = $Member.Split("\")  
      If ($Member.Count -gt 1) {  
           [string]$Member = $Member[1]  
           If (($Member -ne "Domain Admins") -and ($Member -ne "Workstation Admins")) {  
                Foreach ($Prof in $Profiles) {  
                     If ($Member -eq $Prof) {  
                          $LocalAdmins += $Member  
                     }  
                }  
           }  
      }  
      Remove-Variable -Name Member  
 }  
 If ((Test-Path $LogFile) -eq $true) {  
      Remove-Item -Path $LogFile -Force  
 }  
 If ((Test-Path $LogFile) -eq $false) {  
      New-Item -Path $LogFile -ItemType File -Force  
 }  
 If ($LocalAdmins.Count -gt 0) {  
      Foreach ($LocalAdmin in $LocalAdmins) {  
           Add-Content -Path $LogFile -Value $LocalAdmin -Force  
      }  
 }  
   


 <#  
 .SYNOPSIS  
   Create Local Administrators Report  
 .DESCRIPTION  
   This will read all .log files and consolidate them into a master   
   .csv file with the computer name and list of local admins for  
   each computer  
 .Author  
   Mick Pletcher  
 .Date  
   14 February 2015  
 .EXAMPLE  
   powershell.exe -executionpolicy bypass -file LocalAdministratorsReport.ps1  
 #>  
   
 $MasterLog = "\\NetworkLocation\LocalAdministrators.csv"  
 $Files = Get-ChildItem -Path \\NetworkLocation -Force  
 If ((Test-Path $MasterLog) -eq $true) {  
      Remove-Item -Path $MasterLog -Force  
 }  
 If ((Test-Path $MasterLog) -eq $false) {  
      $TitleBar = "ComputerName,UserName"+[char]13  
      New-Item -Path $MasterLog -ItemType File -Value $TitleBar -Force  
 }  
 Foreach ($File in $Files) {  
      If ($File.Extension -eq ".log") {  
           $Usernames = Get-Content -Path $File.FullName  
           Foreach ($Username in $Usernames) {  
                $Entry = $File.BaseName+","+$Username  
                Add-Content -Path $MasterLog -Value $Entry -Force  
           }  
      }  
 }  
   

12 February 2015

List of Installed Updates

Posted By: Mick Pletcher - 12:07 PM





This script will generate a clean list of Microsoft updates that were installed during a system build, making it drastically easier than having to parse through the log file. You open the powershell script up and update the $File variable to point to the location of the BDD.log file.

You can download the script from here.


 <#  
 .SYNOPSIS  
   Produce list of newly installed windows updates  
 .DESCRIPTION  
   Reads BDD.log file from a build and extracts the list of new updates  
   that were applied.  
 .Author  
   Mick Pletcher  
 .Date  
   12 February 2015  
 .EXAMPLE  
   powershell.exe -executionpolicy bypass -file UpdateList.ps1  
 #>  
   
 cls  
 $File = Get-Content -Path "C:\Users\Mick\Desktop\BDD.log" -Force  
 Foreach ($Entry in $File) {  
      If (($Entry -like '*INSTALL - *') -and ($Entry -like '*ZTIWindowsUpdate*')) {  
           #Write-Host $Entry  
           $SplitLine = $Entry.Split('KB')  
           $Update = $SplitLine[2]  
           $Update = $Update.Split(')')  
           $Update = $Update.Split('(')  
           Write-Host "KB"$Update[0]  
      }  
 }  
   
 Remove-Variable -Name Entry -Force  
 Remove-Variable -Name File -Force  
 Remove-Variable -Name Update -Force  

09 February 2015

Deploying Windows Management Framework 4.0

Posted By: Mick Pletcher - 9:47 AM
This PowerShell script will install WMF 4.0. It will return an error if the installation fails.

You can download the script from here.


 <#  
 .Author  
      Mick Pletcher  
 .Date  
      29 July 2014  
 .SYNOPSIS  
   Windows Management Framework 4.0  
 .EXAMPLE  
      powershell.exe -executionpolicy bypass -file install_WMF4.ps1  
 #>  
   
 #Declare Global Memory  
 Set-Variable -Name Errors -Value $null -Scope Global -Force  
 Set-Variable -Name RelativePath -Scope Global -Force  
 Set-Variable -Name Title -Scope Global -Force  
   
 Function ConsoleTitle ($Title){  
      $host.ui.RawUI.WindowTitle = $Title  
 }  
   
 Function DeclareGlobalVariables {  
      $Global:Title = "Windows Management Framework 4.0"  
 }  
   
 Function GetRelativePath {   
      $Global:RelativePath = (split-path $SCRIPT:MyInvocation.MyCommand.Path -parent)+"\"   
 }  
   
 Function InstallMSU ($ProgName,$MSU,$Switches) {  
      $EXE = $Env:windir+"\system32\wusa.exe"  
      $Parameters = [char]34+$MSU+[char]34+[char]32+$Switches  
      Write-Host "Install "$ProgName"....." -NoNewline  
      $ErrCode = (Start-Process -FilePath $EXE -ArgumentList $Parameters -Wait -Passthru).ExitCode  
      If (($ErrCode -eq 0) -or ($ErrCode -eq 3010)) {  
           Write-Host "Success" -ForegroundColor Yellow  
      } elseif ($ErrCode -eq 2359302) {  
           Write-Host "Already Installed" -ForegroundColor Green  
      } else {  
           Write-Host "Failed with error code "$ErrCode -ForegroundColor Red  
           $Global:Errors++  
      }  
 }  
   
 Function ExitPowerShell {  
      If (($Global:Errors -ne $null) -and ($Global:Errors -ne 0)) {  
           Exit 1  
      }  
 }  
   
 cls  
 DeclareGlobalVariables  
 GetRelativePath  
 ConsoleTitle $Global:Title  
 InstallMSU "Windows Management Framework 4.0" $Global:RelativePath"Windows6.1-KB2819745-x86-MultiPkg.msu" "/quiet /norestart"  
 Start-Sleep -Seconds 5  
 ExitPowerShell  
   

06 February 2015

Deploying CMTrace

Posted By: Mick Pletcher - 3:20 PM







CMTrace makes reading .log files much easier. Deploying it though can be somewhat tricky. I have written this PowerShell script that will install CMTrace and associates .log with the application. The association portion of this only works if the user has not manually associated a .log file. I have also written this to be compatible with both 32-bit and 64-bit systems.

All that needs to be done here is to make sure the CMTrace.exe is in the same directory as the PowerShell script. 

You can download the script from here.


 <#  
 .SYNOPSIS  
   Install CMTrace  
 .DESCRIPTION  
   Install CMTrace.exe and associate .log files  
 .Author  
   Mick Pletcher  
 .Date  
   06 February 2015  
 .EXAMPLE  
   powershell.exe -executionpolicy bypass -file install.ps1  
 #>  
   
 $RelativePath = (split-path $SCRIPT:MyInvocation.MyCommand.Path -parent)+"\"   
 $Architecture = Get-WmiObject -Class Win32_OperatingSystem | Select-Object OSArchitecture  
 $Architecture = $Global:Architecture.OSArchitecture  
 If ($Architecture -eq "32-bit") {  
      Copy-Item -Path $RelativePath"CMTrace.exe" -Destination $env:ProgramFiles"\Windows NT\Accessories" -Force  
      $Parameters = "Ftype logfile="+[char]34+$env:ProgramFiles+"\Windows NT\Accessories\CMTrace.exe"+[char]34+" %1"  
      cmd.exe /c $Parameters  
 } else {  
      Copy-Item -Path $RelativePath"CMTrace.exe" -Destination ${env:ProgramFiles(x86)}"\Windows NT\Accessories" -Force  
      $Parameters = "FType logfile="+[char]34+${env:ProgramFiles(x86)}+"\Windows NT\Accessories\CMTrace.exe"+[char]34+" %1"  
      cmd.exe /c $Parameters  
 }  
 $Parameters = "assoc .log=logfile"  
 cmd.exe /c $Parameters  
   

12 December 2014

Replicate Permissioning

Posted By: Mick Pletcher - 3:26 PM





Here is a script I have written that will replicate the permissions between two folders. Execute the script and you will be prompted for the source and destination folders. It will first parse through and set folder permissions and then parse through file permissions.

You can download the script here.


 <#  
 .Author  
   Mick Pletcher  
 .SYNOPSIS  
   Copy Permissions  
 .DESCRIPTION  
   This script will replication permissions for both files and folders using  
   robocopy. Change the $SourceDrive and $DestinationDrive variables to match what  
   you need to replicate.  
 #>  
   
 cls  
 $Errors = 0  
   
   
 $SourceDrive = Read-Host 'Enter source folder'  
 $DestinationDrive = Read-Host 'Enter destination folder'  
   
 #Match capitalization with directories  
 $TempDrive = Get-Item -Path $SourceDrive  
 $SourceDrive = $TempDrive.FullName  
 $TempDrive = Get-Item -Path $DestinationDrive  
 $DestinationDrive = $TempDrive.FullName  
   
 $SourceFolders = Get-ChildItem $SourceDrive -Recurse | ?{ $_.PSIsContainer }  
 $SourceFiles = Get-ChildItem $SourceDrive -Recurse -Force | where { ! $_.PSIsContainer }  
 $DestinationFolders = Get-ChildItem $DestinationDrive -Recurse | ?{ $_.PSIsContainer }  
 $DestinationFiles = Get-ChildItem $DestinationDrive -Recurse -Force | where { ! $_.PSIsContainer }  
   
 #Copy permissions for folders  
 $Output = robocopy $SourceDrive $DestinationDrive /ZB /E /LEV:0 /COPY:SOU /XF *.* /R:5 /W:5  
   
 #Verify Folder Permissions Match  
 Write-Host "Folders:"  
 Write-Host "========"  
 For ($Count=0; $Count -le $SourceFolders.Count; $Count++) {  
      If ($SourceFolders[$Count].FullName -ne $null) {  
           $SourceFolder = $SourceFolders[$Count].FullName  
           $DestinationFolder = $DestinationFolders[$Count].FullName  
           Write-Host $SourceFolder"....." -NoNewline  
           $SourceFolderACL = Get-Acl -Path $SourceFolder  
           $DestinationFolderACL = Get-Acl -Path $DestinationFolder  
           For ($Count1=0; $Count1 -le $SourceFolderACL.Access.Count; $Count1++) {  
                If ($SourceFolderACL.Access[$Count1].FileSystemRights -ne $DestinationFolderACL.Access[$Count1].FileSystemRights) {  
                     $Errors++  
                }  
           }  
           If ($Errors -eq 0) {  
                Write-Host "Success" -ForegroundColor Yellow  
           } else {  
                Write-Host "Failed" -ForegroundColor Red  
           }  
      }  
      $Errors = 0  
 }  
   
 $Output = robocopy $SourceDrive $DestinationDrive /ZB /E /LEV:0 /COPY:SOU /XD *.* /R:5 /W:5  
 #Copy permissions for files  
 Write-Host "Files:"  
 Write-Host "======"  
 For ($Count=0; $Count -le $SourceFiles.Count; $Count++) {  
      If ($SourceFiles[$Count].FullName -ne $null) {  
           $SourceFile = $SourceFiles[$Count].FullName  
           $DestinationFile = $DestinationFiles[$Count].FullName  
           Write-Host $SourceFile"....." -NoNewline  
           $SourceFileACL = Get-Acl -Path $SourceFile  
           $DestinationFileACL = Get-Acl -Path $DestinationFile  
           For ($Count1=0; $Count1 -le $SourceFileACL.Access.Count; $Count1++) {  
                If ($SourceFileACL.Access[$Count1].FileSystemRights -ne $DestinationFileACL.Access[$Count1].FileSystemRights) {  
                     $Errors++  
                }  
           }  
           If ($Errors -eq 0) {  
                Write-Host "Success" -ForegroundColor Yellow  
           } else {  
                Write-Host "Failed" -ForegroundColor Red  
           }  
      }  
      $Errors = 0  
 }  
   

Copyright © 2013 Mick's IT Blogs™ is a registered trademark.