Mick's IT Blogs

Mick's IT Blogs

Latest Updates

05 January 2017

PowerShell: Cached Exchange Mode Status Reporting

Posted By: Mick Pletcher - 11:05 AM















Recently, the firm I work at is going to cached exchange mode. Due to the sensitive nature of the industry I work in, we are turning on cached mode in blocks of users instead of all at once. We wanted to be able to track what machines have cached exchange mode turned on and what machines have it turned off. PowerShell and SCCM are the best method I devised.

I decided to write this PowerShell script in which it will report back to SCCM via WMI the status of cached exchange mode. It will iterate through all all HKEY_USER profiles to get the status of the exchange mailboxes and reports each to SCCM. The script will create a new WMI entry to write the status of cached exchange mode. In order for this to report to SCCM, you will need to import the WMI entry into the SCCM hardware inventory under Default Client Settings.

While writing this script, I decided to make it an option to either write the output to SCCM or to a text file for those admins that do not have SCCM. You can select a centralized location to write the text files to, which are named <ComputerName>.log with the status written inside the log.

The firm I am at skipped Office 2013, so I was not able to get the location and values of those keys. If you are wanting to use this with Office 2013, you will need to edit the Get-OfficeVersion and Find-RegistryKey functions to add those keys.

I have included examples on how to setup the script to execute at commandline within the script's .Example area. One more thing, SAPIEN's PowerShell Studio made writing this script a breeze! If it was not for PowerShell Studio, the script would not have the depth documentation it does.

This is what the report looked like in the SCCM Queries:


You will likely see some profiles saying unknown. This is caused by profiles that might have 2013 or Office 365 installed.

You can download the script from my GitHub site here.

CachedMode.ps1


1:  <#  
2:       .SYNOPSIS  
3:            Cached Exchange Mode Report  
4:         
5:       .DESCRIPTION  
6:            This script is to be executed on a machine to report if Microsoft Outlook is in cached exchange mode or not. It will report a status of on/off/unknown. The status was tracked down to registry key 00036601. The script queries under HKEY_USERS\%SID%\SOFTWARE\Microsoft\Office\16.0\Outlook\Profiles to find which key contains 00036601. The key under the above listed key is the name of the outlook profile and the key that contains 00036601 is a GUID key. Neither of those are standard across different systems, so the script has to find the actual key path. The script can then write the data to either a log file located at a centralized network path, or it can write it to the WMI so that it can be reported back to SCCM. The script was written so that it can be used in an environment that either has SCCM or does not. This script has not been written for Office 365 or Office 2013 as the firm I work at never used that version. If you need include Office 2013, you will need to add Office 15 to the Find-Registry Function  
7:         
8:       .PARAMETER SCCM  
9:            Write output to WMI for reporting to SCCM  
10:         
11:       .PARAMETER TextFile  
12:            Write output to a text file stored at a centralized repository  
13:         
14:       .PARAMETER TextFileLocation  
15:            Location to write the text file to  
16:         
17:       .EXAMPLE  
18:            Write output to WMI entry for reporting to SCCM  
19:                 powershell.exe -file CachedMode.ps1 -SCCM  
20:    
21:            Write output to a text file at a centralized location NOTE: -TextFileLocation can be prepopulated  
22:                 powershell.exe -file CachedMode.ps1 -TextFile -TextFileLocation '\\mick\Systems'  
23:    
24:       .NOTES  
25:            ===========================================================================  
26:            Created with:     SAPIEN Technologies, Inc., PowerShell Studio 2016 v5.3.131  
27:            Created on:       12/29/2016 12:45 PM  
28:            Created by:       Mick Pletcher  
29:            Organization:  
30:            Filename:         CachedModeReporting.ps1  
31:            ===========================================================================  
32:  #>  
33:  [CmdletBinding()]  
34:  param  
35:  (  
36:       [Switch]$SCCM,  
37:       [switch]$TextFile,  
38:       [string]$TextFileLocation  
39:  )  
40:    
41:  function Find-RegistryKey {  
42:  <#  
43:       .SYNOPSIS  
44:            Find Registry Key Value  
45:         
46:       .DESCRIPTION  
47:            Find the registry key that contains the specified value entry  
48:         
49:       .PARAMETER Value  
50:            Value to search registry key for  
51:         
52:       .PARAMETER SID  
53:            HKEY_USERS SID  
54:         
55:       .EXAMPLE  
56:            PS C:\> Find-RegistryKey  
57:         
58:       .NOTES  
59:            Additional information about the function.  
60:  #>  
61:         
62:       [CmdletBinding()][OutputType([string])]  
63:       param  
64:       (  
65:            [ValidateNotNullOrEmpty()][string]$Value,  
66:            [ValidateNotNullOrEmpty()][string]$SID  
67:       )  
68:         
69:       $Version = Get-OfficeVersion  
70:       switch ($Version) {  
71:            "Office 14" { $Key = "HKEY_USERS\" + $SID + "\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows Messaging Subsystem\Profiles" }  
72:            "Office 16" { $Key = "HKEY_USERS\" + $SID + "\SOFTWARE\Microsoft\Office\16.0\Outlook\Profiles" }  
73:       }  
74:       If ((Test-Path REGISTRY::$Key) -eq $true) {  
75:            [string]$CachedMode = get-childitem REGISTRY::$Key -recurse -ErrorAction SilentlyContinue | where-object { $_.property -eq "00036601" }  
76:            If ($CachedMode -ne $null) {  
77:                 [string]$CachedModeValue = (Get-ItemProperty REGISTRY::$CachedMode).'00036601'  
78:                 switch ($Version) {  
79:                      "Office 14" {  
80:                           switch ($CachedModeValue) {  
81:                                #Values below are converted to decimal from the registry hex value commented to the right  
82:                                '128 25 0 0' { Return "Enabled" } #'80 19 0 0'  
83:                                '0 16 0 0' { Return "Disabled" } #'0 10 0 0'  
84:                                default { Return "Unknown" }  
85:                           }  
86:                      }  
87:                      "Office 16" {  
88:                           switch ($CachedModeValue) {  
89:                                #Values below are converted to decimal from the registry hex value commented to the right  
90:                                '132 25 0 0' { Return "Enabled" } #'84 19 0 0'  
91:                                '4 16 0 0' { Return "Disabled" } #'4 10 0 0'  
92:                                default { Return "Unknown" }  
93:                           }  
94:                      }  
95:                 }  
96:                 Return $CachedModeValue  
97:            } else {  
98:                 Return $null  
99:            }  
100:       } else {  
101:            Return $null  
102:       }  
103:  }  
104:    
105:  function Get-HKEY_USERS_List {  
106:  <#  
107:       .SYNOPSIS  
108:            Retrieve list of HKEY_Users  
109:         
110:       .DESCRIPTION  
111:            Retrieve list of HKEY_Users while excluding the built-in and administrator accounts  
112:         
113:       .EXAMPLE  
114:                      PS C:\> Get-HKEY_USERS_List  
115:         
116:       .NOTES  
117:            Additional information about the function.  
118:  #>  
119:         
120:       [CmdletBinding()][OutputType([array])]  
121:       param ()  
122:         
123:       #Get list of HKEY_USERS registry keys filtering out built-in users  
124:       $HKEY_USERS = Get-ChildItem REGISTRY::HKEY_USERS | where-object { ($_.Name -like "*S-1-5-21*") -and ($_.Name -notlike "*_Classes") }  
125:       $Users = @()  
126:       foreach ($User in $HKEY_USERS) {  
127:            #Get the SID of the first profile  
128:            $PROFILESID = Get-ChildItem REGISTRY::"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList" | Where-Object { $_.name -like "*" + $USER.PSChildName + "*" }  
129:            $SID = $PROFILESID.PSChildName  
130:            #Determine if cached mode is on or off  
131:            $CachedMode = Find-RegistryKey -Value "00036601" -SID $SID  
132:            If ($CachedMode -ne $null) {  
133:                 #Get the username associated with the SID  
134:                 $ProfileName = ((Get-ItemProperty REGISTRY::$PROFILESID).ProfileImagePath).Split("\")[2]  
135:                 #Write username and sid to object  
136:                 $SystemInfo = New-Object -TypeName System.Management.Automation.PSObject  
137:                 Add-Member -InputObject $SystemInfo -MemberType NoteProperty -Name Profile -Value $ProfileName  
138:                 Add-Member -InputObject $SystemInfo -MemberType NoteProperty -Name Status -Value $CachedMode  
139:                 $Users += $SystemInfo  
140:            }  
141:       }  
142:       Return $Users  
143:  }  
144:    
145:  function New-WMIClass {  
146:       [CmdletBinding()]  
147:       param  
148:       (  
149:            [ValidateNotNullOrEmpty()][string]$Class  
150:       )  
151:         
152:       $WMITest = Get-WmiObject $Class -ErrorAction SilentlyContinue  
153:       If ($WMITest -ne "") {  
154:            $Output = "Deleting " + $Class + " WMI class....."  
155:            Remove-WmiObject $Class  
156:            $WMITest = Get-WmiObject $Class -ErrorAction SilentlyContinue  
157:            If ($WMITest -eq $null) {  
158:                 $Output += "Success"  
159:            } else {  
160:                 $Output += "Failed"  
161:                 Exit 1  
162:            }  
163:            Write-Output $Output  
164:       }  
165:       $Output = "Creating " + $Class + " WMI class....."  
166:       $newClass = New-Object System.Management.ManagementClass("root\cimv2", [String]::Empty, $null);  
167:       $newClass["__CLASS"] = $Class;  
168:       $newClass.Qualifiers.Add("Static", $true)  
169:       $newClass.Properties.Add("Profile", [System.Management.CimType]::String, $false)  
170:       $newClass.Properties["Profile"].Qualifiers.Add("key", $true)  
171:       $newClass.Properties["Profile"].Qualifiers.Add("read", $true)  
172:       $newClass.Properties.Add("Status", [System.Management.CimType]::String, $false)  
173:       $newClass.Properties["Status"].Qualifiers.Add("key", $true)  
174:       $newClass.Properties["Status"].Qualifiers.Add("read", $true)  
175:       $newClass.Put() | Out-Null  
176:       $WMITest = Get-WmiObject $Class -ErrorAction SilentlyContinue  
177:       If ($WMITest -eq $null) {  
178:            $Output += "Success"  
179:       } else {  
180:            $Output += "Failed"  
181:            Exit 1  
182:       }  
183:       Write-Output $Output  
184:  }  
185:    
186:  function New-WMIInstance {  
187:  <#  
188:       .SYNOPSIS  
189:            Write new instance  
190:         
191:       .DESCRIPTION  
192:            Write a new instance for each profile along with its cached mode status  
193:         
194:       .PARAMETER Username  
195:            Username  
196:         
197:       .PARAMETER CachedModeStatus  
198:            Status of exchange cached mode  
199:         
200:       .PARAMETER Class  
201:            WMI Class to write information to  
202:         
203:       .PARAMETER MappedDrives  
204:            List of mapped drives  
205:         
206:       .EXAMPLE  
207:            PS C:\> New-WMIInstance  
208:         
209:       .NOTES  
210:            Additional information about the function.  
211:  #>  
212:         
213:       [CmdletBinding()]  
214:       param  
215:       (  
216:            [ValidateNotNullOrEmpty()][string]$Username,  
217:            [ValidateNotNullOrEmpty()][string]$CachedModeStatus,  
218:            [ValidateNotNullOrEmpty()][string]$Class  
219:       )  
220:         
221:       $Output = "Writing Cached Exchange information instance to" + [char]32 + $Class + [char]32 + "class....."  
222:       $Return = Set-WmiInstance -Class $Class -Arguments @{ Profile = $Username; Status = $CachedModeStatus }  
223:       If ($Return -like "*" + $Username + "*") {  
224:            $Output += "Success"  
225:       } else {  
226:            $Output += "Failed"  
227:       }  
228:       Write-Output $Output  
229:  }  
230:    
231:  function Get-OfficeVersion {  
232:  <#  
233:       .SYNOPSIS  
234:            Get Microsoft Office Version  
235:         
236:       .DESCRIPTION  
237:            Execute the OSPP.vbs to display the license information, which also contains the current version of Microsoft Office.  
238:         
239:       .EXAMPLE  
240:                      PS C:\> Get-OfficeVersion  
241:         
242:       .NOTES  
243:            Additional information about the function.  
244:  #>  
245:         
246:       [CmdletBinding()][OutputType([string])]  
247:       param ()  
248:         
249:       If ((Test-Path $env:ProgramFiles"\Microsoft Office") -eq $true) {  
250:            $File = get-childitem -path $env:ProgramFiles"\Microsoft Office" -filter ospp.vbs -recurse  
251:       }  
252:       If ((Test-Path ${env:ProgramFiles(x86)}"\Microsoft Office") -eq $true) {  
253:            $File = get-childitem -path ${env:ProgramFiles(x86)}"\Microsoft Office" -filter ospp.vbs -recurse  
254:       }  
255:       #Get current version of office  
256:       $Version = (cscript.exe $File.Fullname /dstatus | where-object { $_ -like "LICENSE NAME:*" }).split(":")[1].Trim().Split(",")[0]  
257:       Return $Version  
258:  }  
259:    
260:  function Initialize-HardwareInventory {  
261:  <#  
262:       .SYNOPSIS  
263:            Perform Hardware Inventory  
264:         
265:       .DESCRIPTION  
266:            Perform a hardware inventory via the SCCM client to report the WMI entry.  
267:         
268:       .EXAMPLE  
269:                      PS C:\> Initialize-HardwareInventory  
270:         
271:       .NOTES  
272:            Additional information about the function.  
273:  #>  
274:         
275:       [CmdletBinding()]  
276:       param ()  
277:         
278:       $Output = "Initiate SCCM Hardware Inventory....."  
279:       $SMSCli = [wmiclass] "\\localhost\root\ccm:SMS_Client"  
280:       $ErrCode = ($SMSCli.TriggerSchedule("{00000000-0000-0000-0000-000000000001}")).ReturnValue  
281:       If ($ErrCode -eq $null) {  
282:            $Output += "Success"  
283:       } else {  
284:            $Output += "Failed"  
285:       }  
286:       Write-Output $Output  
287:  }  
288:    
289:  Clear-Host  
290:  #Get list of users and report if they are running Outlook in cached exchange mode  
291:  $Users = Get-HKEY_USERS_List  
292:  If ($SCCM.IsPresent) {  
293:       #Create new WMI Class to report status to  
294:       New-WMIClass -Class "Cached_Exchange_Mode"  
295:       #Write Cached Mode Status reports to WMI instances  
296:       foreach ($User in $Users) {  
297:            New-WMIInstance -Username $User.Profile -CachedModeStatus $User.Status -Class "Cached_Exchange_Mode"  
298:       }  
299:       Initialize-HardwareInventory  
300:  }  
301:  If ($TextFile.IsPresent) {  
302:       #Check if $TextFileLocation is populated  
303:       If (($TextFileLocation -ne "") -and ($TextFileLocation -ne $null)) {  
304:            #Check if $TextFileLocation exists  
305:            If ((Test-Path $TextFileLocation) -eq $true) {  
306:                 #Insert backslash at the end of the $TextFileLocation and define the name of the text file  
307:                 If ($TextFileLocation.Length - 1 -ne '\') {  
308:                      $File = $TextFileLocation + '\' + $env:COMPUTERNAME + ".log"  
309:                 } else {  
310:                      $File = $TextFileLocation + $env:COMPUTERNAME + ".log"  
311:                 }  
312:                 #Delete the old log file if it exists  
313:                 If ((Test-Path $File) -eq $true) {  
314:                      Remove-Item $File -Force  
315:                 }  
316:                 #Write the results to the log file  
317:                 $Users | Out-File $File -Encoding UTF8 -Force  
318:            } else {  
319:                 Write-Host "Text file location does not exist"  
320:            }  
321:       } else {  
322:            Write-Host "No text file location was defined."  
323:       }  
324:  }  
325:  #Display Results to Screen  
326:  $Users  

11 November 2016

SCCM: Local Administrators Reporting

Posted By: Mick Pletcher - 3:51 PM










Here is a script that will gather a list of local administrators on a machine. The script can report the list to SCCM by writing the list to a WMI entry. It can also write the list to a text file at a specified location for admins that do not have SCCM. The text file is named <%COMPUTERNAME%>.txt. You can do both SCCM reporting and text file reporting if desired.

To implement this into SCCM, run the script once on a machine with the following command line:

powershell.exe -file LocalAdmins.ps1 -SCCMReporting

Once this has executed, do the following: 
  1. Open the SCCM console
  2. Click on Administration
  3. Click Client Settings
  4. Right-click Default Client Settings
  5. Left-click Properties
  6. Click Hardware Inventory
  7. Click Set Classes
  8. Click Add
  9. Click Connect
  10. Enter the computer name of the system you ran the script on
  11. Check Recursive
  12. Check Credentials required
  13. Enter the domain\username for user name
  14. Enter the associated password
  15. Click Connect
  16. Once the list of classes appears, click on Class Name to sort the classes
  17. Scroll down to find Local_Administrators and check the box to the left
  18. Click OK
  19. Click OK
  20. Click OK
  21. Now go back to the machine you ran the script on and run a hardware inventory to send the data up to SCCM. It will take a few minutes until the data appears in SCCM

The next step is to setup the script to execute through SCCM as a package. The script will need to be executed on a routine basis if you want it to be reported regularly to SCCM. As a package, the following pictures show how I have it configured in SCCM.



Finally, you will want to be able to look at the results. You can create a query to show the systems that have reported users in the local administrators group. Here is the WQL I use:


 select distinct SMS_R_System.Name, SMS_G_System_LOCAL_ADMINISTRATORS.Domain, SMS_G_System_LOCAL_ADMINISTRATORS.User from SMS_R_System inner join SMS_G_System_LOCAL_ADMINISTRATORS on SMS_G_System_LOCAL_ADMINISTRATORS.ResourceID = SMS_R_System.ResourceId order by SMS_R_System.Name, SMS_G_System_LOCAL_ADMINISTRATORS.Domain, SMS_G_System_LOCAL_ADMINISTRATORS.User  

You can download the SCCM WQL Query from here.

You can download the script from my GitHub repository located here


LocalAdmins.ps1

 <#  
      .SYNOPSIS  
           Report Local administrators  
        
      .DESCRIPTION  
           Report a list of local administrators on machines to a designated text file, screen, and/or to SCCM via a WMI entry.  
        
      .PARAMETER MemberExclusionsFile  
           Text file containing a list of users to exclude  
        
      .PARAMETER OutputFile  
           Specifies if the output is to be written to a text file. The OutputFileLocation parameter also needs to be populated with the location to write the text file to.  
        
      .PARAMETER OutputFileLocation  
           Location where to write the output text files  
        
      .PARAMETER SCCMReporting  
           Report results to SCCM  
        
      .PARAMETER SystemExclusionsFile  
           Text file containing a list of systems to not generate a report on  
        
      .EXAMPLE  
           Get a list of local admins without reporting to SCCM or writing output to text file  
           powershell.exe -file LocalAdmins.ps1  
             
           Get a list of local admins and report to SCCM  
           powershell.exe -file LocalAdmins.ps1 -SCCMReporting  
             
           Get a list of local admins and write report to a text file at a specified location  
           powershell.exe -file LocalAdmins.ps1 -OutputFile  
        
      .NOTES  
           ===========================================================================  
           Created with:      SAPIEN Technologies, Inc., PowerShell Studio 2016 v5.2.129  
           Created on:       11/9/2016 12:47 PM  
           Created by:       Mick Pletcher  
           Organization:  
           Filename:        LocalAdministrators.ps1  
           ===========================================================================  
 #>  
 [CmdletBinding()]  
 param  
 (  
      [string]  
      $MemberExclusionsFile = 'MemberExclusions.txt',  
      [switch]  
      $OutputFile,  
      [string]  
      $OutputFileLocation = '',  
      [switch]  
      $SCCMReporting,  
      [string]  
      $SystemExclusionsFile = 'SystemExclusions.txt'  
 )  
   
 function Get-RelativePath {  
 <#  
      .SYNOPSIS  
           Get the relative path  
        
      .DESCRIPTION  
           Returns the location of the currently running PowerShell script  
        
      .NOTES  
           Additional information about the function.  
 #>  
        
      [CmdletBinding()][OutputType([string])]  
      param ()  
        
      $Path = (split-path $SCRIPT:MyInvocation.MyCommand.Path -parent) + "\"  
      Return $Path  
 }  
   
 function Invoke-SCCMHardwareInventory {  
 <#  
      .SYNOPSIS  
           Initiate a Hardware Inventory  
        
      .DESCRIPTION  
           This will initiate a hardware inventory that does not include a full hardware inventory. This is enought to collect the WMI data.  
        
      .EXAMPLE  
                     PS C:\> Invoke-SCCMHardwareInventory  
        
      .NOTES  
           Additional information about the function.  
 #>  
        
      [CmdletBinding()]  
      param ()  
        
      $ComputerName = $env:COMPUTERNAME  
      $SMSCli = [wmiclass] "\\$ComputerName\root\ccm:SMS_Client"  
      $SMSCli.TriggerSchedule("{00000000-0000-0000-0000-000000000001}") | Out-Null  
 }  
   
 function New-WMIClass {  
      [CmdletBinding()]  
      param  
      (  
           [ValidateNotNullOrEmpty()][string]  
           $Class  
      )  
        
      $WMITest = Get-WmiObject $Class -ErrorAction SilentlyContinue  
      If ($WMITest -ne $null) {  
           $Output = "Deleting " + $Class + " WMI class....."  
           Remove-WmiObject $Class  
           $WMITest = Get-WmiObject $Class -ErrorAction SilentlyContinue  
           If ($WMITest -eq $null) {  
                $Output += "success"  
           } else {  
                $Output += "Failed"  
                Exit 1  
           }  
           Write-Output $Output  
      }  
      $Output = "Creating " + $Class + " WMI class....."  
      $newClass = New-Object System.Management.ManagementClass("root\cimv2", [String]::Empty, $null);  
      $newClass["__CLASS"] = $Class;  
      $newClass.Qualifiers.Add("Static", $true)  
      $newClass.Properties.Add("Domain", [System.Management.CimType]::String, $false)  
      $newClass.Properties["Domain"].Qualifiers.Add("key", $true)  
      $newClass.Properties["Domain"].Qualifiers.Add("read", $true)  
      $newClass.Properties.Add("User", [System.Management.CimType]::String, $false)  
      $newClass.Properties["User"].Qualifiers.Add("key", $false)  
      $newClass.Properties["User"].Qualifiers.Add("read", $true)  
      $newClass.Put() | Out-Null  
      $WMITest = Get-WmiObject $Class -ErrorAction SilentlyContinue  
      If ($WMITest -eq $null) {  
           $Output += "success"  
      } else {  
           $Output += "Failed"  
           Exit 1  
      }  
      Write-Output $Output  
 }  
   
 function New-WMIInstance {  
 <#  
      .SYNOPSIS  
           Write new instance  
        
      .DESCRIPTION  
           A detailed description of the New-WMIInstance function.  
        
      .PARAMETER MappedDrives  
           List of mapped drives  
        
      .PARAMETER Class  
           A description of the Class parameter.  
        
      .EXAMPLE  
           PS C:\> New-WMIInstance  
        
      .NOTES  
           Additional information about the function.  
 #>  
        
      [CmdletBinding()]  
      param  
      (  
           [ValidateNotNullOrEmpty()][array]  
           $LocalAdministrators,  
           [string]  
           $Class  
      )  
        
      foreach ($LocalAdministrator in $LocalAdministrators) {  
           $Output = "Writing" + [char]32 +$LocalAdministrator.User + [char]32 + "instance to" + [char]32 + $Class + [char]32 + "class....."  
           $Return = Set-WmiInstance -Class $Class -Arguments @{ Domain = $LocalAdministrator.Domain; User = $LocalAdministrator.User }  
           If ($Return -like "*" + $LocalAdministrator.User + "*") {  
                $Output += "Success"  
           } else {  
                $Output += "Failed"  
           }  
           Write-Output $Output  
      }  
 }  
   
 cls  
 #Get the path this script is being executed from  
 $RelativePath = Get-RelativePath  
 #Name of the computer this script is being executed on  
 $ComputerName = $Env:COMPUTERNAME  
 #Read the list of systems to exclude from reporting  
 $File = $RelativePath + $SystemExclusionsFile  
 $SystemExclusions = Get-Content $File  
 If ($SystemExclusions -notcontains $Env:COMPUTERNAME) {  
      #Get list of users to exclude from reporting  
      $File = $RelativePath + $MemberExclusionsFile  
      $MemberExclusions = Get-Content $File  
      #Get list of local administrators while excluding specified members  
      $Members = net localgroup administrators | Where-Object { $_ -AND $_ -notmatch "command completed successfully" } | select -skip 4 | Where-Object { $MemberExclusions -notcontains $_ }  
      $LocalAdmins = @()  
      foreach ($Member in $Members) {  
           #Create new object  
           $Admin = New-Object -TypeName System.Management.Automation.PSObject  
           $Member = $Member.Split("\")  
           If ($Member.length -gt 1) {  
                Add-Member -InputObject $Admin -MemberType NoteProperty -Name Domain -Value $Member[0].Trim()  
                Add-Member -InputObject $Admin -MemberType NoteProperty -Name User -Value $Member[1].Trim()  
           } else {  
                Add-Member -InputObject $Admin -MemberType NoteProperty -Name Domain -Value ""  
                Add-Member -InputObject $Admin -MemberType NoteProperty -Name User -Value $Member.Trim()  
           }  
           $LocalAdmins += $Admin  
      }  
 }  
 #Report output to WMI which will report up to SCCM  
 If ($SCCMReporting.IsPresent) {  
      New-WMIClass -Class "Local_Administrators"  
      New-WMIInstance -Class "Local_Administrators" -LocalAdministrators $LocalAdmins  
      #Report WMI entry to SCCM  
      Invoke-SCCMHardwareInventory  
 }  
 If ($OutputFile.IsPresent) {  
      If ($OutputFileLocation[$OutputFileLocation.Length - 1] -ne "\") {  
           $File = $OutputFileLocation + "\" + $ComputerName + ".log"  
      } else {  
           $File = $OutputFileLocation + $ComputerName + ".log"  
      }  
      #Delete old log file if it exists  
      $Output = "Deleting $ComputerName.log....."  
      If ((Test-Path $File) -eq $true) {  
           Remove-Item -Path $File -Force  
      }  
      If ((Test-Path $File) -eq $false) {  
           $Output += "Success"  
      } else {  
           $Output += "Failed"  
      }  
      Write-Output $Output  
      $Output = "Writing local admins to $ComputerName.log....."  
      $LocalAdmins | Out-File $File  
      If ((Test-Path $File) -eq $true) {  
           $Output += "Success"  
      } else {  
           $Output += "Failed"  
      }  
      Write-Output $Output  
 }  
 #Display list of local administrators to screen  
 $LocalAdmins  
   

26 October 2016

SCCM Mapped Drives Report

Posted By: Mick Pletcher - 3:10 PM









Recently, we wanted to start keeping track of users with mapped drives due to cryptolocker vulnerabilities. There are a few applications we have that require mapped drives, so we have certain users with them. Once again, I must say thank you to Sapien Technology for PowerShell Studio! That tool makes writing these PowerShell scripts a breeze.

This script will scan all user profiles on a machine and report users with mapped drives. This is done by parsing through the HKU registries. It has been written so that you can either have the script write the report to a text file if you do not have SCCM and/or it can write it to WMI so that SCCM can read the results. I have also included a UNCPathExclusionsFile parameter that allows you to create a text file that resides in the same directory as the script. It contains a list of UNC paths that you do not want the script to report. I recommend pre-populating the values of the $TextFileLocation and $UNCPathExclusionsFile parameters within the script. That just leaves the $OutputFile and $SCCMReporting left to specify at the command line.

If you are wanting this to write the results to SCCM, here is what you need to do. First, SCCM needs to know what to look for in order to report on it. This script will use WMI to report that data to SCCM. The first thing is to execute the script locally on any PC. Run it using the following command line: powershell.exe -file MappedDriveReport.ps1 -SCCMReporting



That command line will execute the script to scan for mapped drives write the results to WMI and then initiate a hardware inventory. Because the new WMI entry has not been added to SCCM, it will not be reported yet. Now that you have executed the script on the local machine, do the following:

  1. Go into SCCM--->Administration Tab--->Client Settings---> Default Client Settings--->Hardware Inventory--->Set Classes.
  2. Click Add--->Connect.
  3. Enter the computer name of the system you ran the script on, check recursive, check Credentials required (Computer is not local)---> <domain>\<username> in the username field, and finally the password for the associated username. 
  4. Click Connect
  5. Click on the Class Name tab to sort by class name
  6. Scroll down to find MappedDrives and check the box
  7. Click OK

You have now added the WMI class to SCCM for it to grab the data from the PCs and report it back to SCCM. 


To get the systems to report the data back to SCCM, you will need to setup a package, not an application, in SCCM to deploy out to the systems. I have the package setup to re-run once a week at 12:00 pm on Wednesdays so that I can get the most users to report back. More users are online at that time here than any of the other days.

If you read the .Example in the documentation portion of the script, you will see two examples on how to execute the script.

I have also included a hardware inventory within the script so the data will be reported back to SCCM right after the script is executed.

In order to view the data in SCCM, you can do the following using the Resource Explorer:

  1. Right-click on a machine in the Assets and Compliance--->Devices
  2. Click Start--->Resource Explorer
  3. Click the plus beside Hardware
  4. If a system had mapped drives, then there will be a mapped drives field, otherwise it does not exist.
You can also use the queries to report systems with mapped drives. Here is the query I use:
select distinct SMS_G_System_MAPPEDDRIVES.user, SMS_G_System_MAPPEDDRIVES.Letter, SMS_G_System_MAPPEDDRIVES.Path from  SMS_R_System inner join SMS_G_System_MAPPEDDRIVES on SMS_G_System_MAPPEDDRIVES.ResourceID = SMS_R_System.ResourceId order by SMS_G_System_MAPPEDDRIVES.user

If you do not have SCCM and need a report, you can use the -OutputFile to have it write the results to a text file at the specified location defined in the $TextFileLocation parameter. 


The script is available on my GitHub site located here.

MappedDriveReport.ps1
1:  <#  
2:       .SYNOPSIS  
3:            Get List of Mapped Drives  
4:         
5:       .DESCRIPTION  
6:            Scans each profile for a list of mapped drives. It will generate a screen report and can also write the output to the WMI for reporting to SCCM.  
7:         
8:       .PARAMETER OutputFile  
9:            Specifies if the output is to be written to a text file. The TextFileLocation parameter also needs to be populated with the location to write the text file to.  
10:         
11:       .PARAMETER TextFileLocation  
12:            Location where to write the text file to  
13:         
14:       .PARAMETER UNCPathExclusionsFile  
15:            Text file containing a list of UNC paths to exclude from reporting.  
16:         
17:       .PARAMETER SCCMReporting  
18:            Specifies to write the data to WMI so that SCCM can pickup the data.  
19:         
20:       .PARAMETER TextFileName  
21:            Write output to a text file  
22:    
23:       .EXAMPLE  
24:            Execute and write output to the reporting file location and also write output to WMI for reporting to SCCM. -TextFileLocation parameter is prepopulated below.  
25:                 powershell.exe -file MappedDriveReport.ps1 -OutputFile -SCCMReporting  
26:    
27:            Execute and write output to WMI to report to SCCM.  
28:                 powershell.exe -file MappedDriveReport.ps1 -SCCMReporting  
29:         
30:       .NOTES  
31:            ===========================================================================  
32:            Created with:      SAPIEN Technologies, Inc., PowerShell Studio 2016 v5.2.128  
33:            Created on:       10/7/2016 10:57 AM  
34:            Created by:       Mick Pletcher  
35:            Organization:  
36:            Filename:          MappedDriveReport.ps1  
37:            ===========================================================================  
38:  #>  
39:  [CmdletBinding()]  
40:  param  
41:  (  
42:       [switch]  
43:       $OutputFile,  
44:       [string]  
45:       $TextFileLocation = '\\drfs1\DesktopApplications\ProductionApplications\Waller\MappedDrivesReport\Reports',  
46:       [string]  
47:       $UNCPathExclusionsFile = "\\drfs1\DesktopApplications\ProductionApplications\Waller\MappedDrivesReport\UNCPathExclusions.txt",  
48:       [switch]  
49:       $SCCMReporting  
50:  )  
51:    
52:  function Get-CurrentDate {  
53:  <#  
54:       .SYNOPSIS   
55:            Get the current date and return formatted value   
56:    
57:       .DESCRIPTION   
58:            Return the current date in the following format: mm-dd-yyyy   
59:    
60:       .NOTES   
61:            Additional information about the function.   
62:  #>  
63:         
64:       [CmdletBinding()][OutputType([string])]  
65:       param ()  
66:    
67:       $CurrentDate = Get-Date  
68:       $CurrentDate = $CurrentDate.ToShortDateString()  
69:       $CurrentDate = $CurrentDate -replace "/", "-"  
70:       If ($CurrentDate[2] -ne "-") {  
71:            $CurrentDate = $CurrentDate.Insert(0, "0")  
72:       }  
73:       If ($CurrentDate[5] -ne "-") {  
74:            $CurrentDate = $CurrentDate.Insert(3, "0")  
75:       }  
76:       Return $CurrentDate  
77:  }  
78:    
79:  function Get-MappedDrives {  
80:  <#  
81:       .SYNOPSIS  
82:            Get list of Mapped Drives  
83:         
84:       .DESCRIPTION  
85:            Retrieve a list of mapped drives for each user that has logged onto the machine.  
86:         
87:       .EXAMPLE  
88:            PS C:\> Get-MappedDrives  
89:         
90:       .NOTES  
91:            Additional information about the function.  
92:  #>  
93:         
94:       [CmdletBinding()][OutputType([array])]  
95:         
96:       #Get UNC Exclusions from UNCPathExclusions.txt file  
97:       $UNCExclusions = Get-Content $UNCPathExclusionsFile -Force  
98:       #Get HKEY_Users Registry Keys  
99:       [array]$UserSIDS = (Get-ChildItem -Path REGISTRY::HKEY_Users | Where-Object { ($_ -notlike "*Classes*") -and ($_ -like "*S-1-5-21*") }).Name  
100:       #Get Profiles from HKLM  
101:       [array]$ProfileList = (Get-ChildItem -Path REGISTRY::"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList" | Where-Object { $_ -like "*S-1-5-21*" }).Name  
102:       $UserMappedDrives = @()  
103:       #Iterate through each HKEY_USERS profile  
104:       foreach ($UserSID in $UserSIDS) {  
105:            #GET SID only  
106:            [string]$UserSID = $UserSID.Split("\")[1].Trim()  
107:            #Find the userprofile that matches the HKEY_USERS  
108:            [string]$UserPROFILE = $ProfileList | Where-Object { $_ -like "*" + $UserSID + "*" }  
109:            #Get the username associated with the SID  
110:            $Username = ((Get-ItemProperty -Path REGISTRY::$UserPROFILE).ProfileImagePath).Split("\")[2].trim()  
111:            #Define registry path to mapped drives  
112:            [string]$MappedDrives = "HKEY_USERS\" + $UserSID + "\Network"  
113:            #Get list of mapped drives  
114:            [array]$MappedDrives = (Get-ChildItem REGISTRY::$MappedDrives | Select-Object name).name  
115:            foreach ($MappedDrive in $MappedDrives) {  
116:                 $DriveLetter = (Get-ItemProperty -Path REGISTRY::$MappedDrive | select PSChildName).PSChildName  
117:                 $DrivePath = (Get-ItemProperty -Path REGISTRY::$MappedDrive | select RemotePath).RemotePath  
118:                 If ($DrivePath -notin $UNCExclusions) {  
119:                      $Drives = New-Object System.Management.Automation.PSObject  
120:                      $Drives | Add-Member -MemberType NoteProperty -Name ComputerName -Value $env:COMPUTERNAME  
121:                      $Drives | Add-Member -MemberType NoteProperty -Name Username -Value $Username  
122:                      $Drives | Add-Member -MemberType NoteProperty -Name DriveLetter -Value $DriveLetter  
123:                      $Drives | Add-Member -MemberType NoteProperty -Name DrivePath -Value $DrivePath  
124:                      $UserMappedDrives += $Drives  
125:                 }  
126:            }  
127:       }  
128:       Return $UserMappedDrives  
129:  }  
130:    
131:  function Get-RelativePath {  
132:  <#  
133:       .SYNOPSIS  
134:            Get the relative path  
135:         
136:       .DESCRIPTION  
137:            Returns the location of the currently running PowerShell script  
138:         
139:       .NOTES  
140:            Additional information about the function.  
141:  #>  
142:         
143:       [CmdletBinding()][OutputType([string])]  
144:       param ()  
145:         
146:       $Path = (split-path $SCRIPT:MyInvocation.MyCommand.Path -parent) + "\"  
147:       Return $Path  
148:  }  
149:    
150:  function Invoke-SCCMHardwareInventory {  
151:  <#  
152:       .SYNOPSIS  
153:            Initiate a Hardware Inventory  
154:         
155:       .DESCRIPTION  
156:            This will initiate a hardware inventory that does not include a full hardware inventory. This is enought to collect the WMI data.  
157:         
158:       .EXAMPLE  
159:                      PS C:\> Invoke-SCCMHardwareInventory  
160:         
161:       .NOTES  
162:            Additional information about the function.  
163:  #>  
164:         
165:       [CmdletBinding()]  
166:       param ()  
167:         
168:       $ComputerName = $env:COMPUTERNAME  
169:       $SMSCli = [wmiclass] "\\$ComputerName\root\ccm:SMS_Client"  
170:       $SMSCli.TriggerSchedule("{00000000-0000-0000-0000-000000000001}") | Out-Null  
171:  }  
172:    
173:  function New-WMIClass {  
174:       [CmdletBinding()]  
175:       param  
176:       (  
177:            [ValidateNotNullOrEmpty()][string]  
178:            $Class  
179:       )  
180:         
181:       $WMITest = Get-WmiObject $Class -ErrorAction SilentlyContinue  
182:       If ($WMITest -ne $null) {  
183:            $Output = "Deleting " + $WMITest.__CLASS[0] + " WMI class....."  
184:            Remove-WmiObject $Class  
185:            $WMITest = Get-WmiObject $Class -ErrorAction SilentlyContinue  
186:            If ($WMITest -eq $null) {  
187:                 $Output += "success"  
188:            } else {  
189:                 $Output += "Failed"  
190:                 Exit 1  
191:            }  
192:            Write-Output $Output  
193:       }  
194:       $Output = "Creating " + $Class + " WMI class....."  
195:       $newClass = New-Object System.Management.ManagementClass("root\cimv2", [String]::Empty, $null);  
196:       $newClass["__CLASS"] = $Class;  
197:       $newClass.Qualifiers.Add("Static", $true)  
198:       $newClass.Properties.Add("ComputerName", [System.Management.CimType]::String, $false)  
199:       $newClass.Properties["ComputerName"].Qualifiers.Add("key", $true)  
200:       $newClass.Properties["ComputerName"].Qualifiers.Add("read", $true)  
201:       $newClass.Properties.Add("DriveLetter", [System.Management.CimType]::String, $false)  
202:       $newClass.Properties["DriveLetter"].Qualifiers.Add("key", $false)  
203:       $newClass.Properties["DriveLetter"].Qualifiers.Add("read", $true)  
204:       $newClass.Properties.Add("DrivePath", [System.Management.CimType]::String, $false)  
205:       $newClass.Properties["DrivePath"].Qualifiers.Add("key", $false)  
206:       $newClass.Properties["DrivePath"].Qualifiers.Add("read", $true)  
207:       $newClass.Properties.Add("Username", [System.Management.CimType]::String, $false)  
208:       $newClass.Properties["Username"].Qualifiers.Add("key", $false)  
209:       $newClass.Properties["Username"].Qualifiers.Add("read", $true)  
210:       $newClass.Put() | Out-Null  
211:       $WMITest = Get-WmiObject $Class -ErrorAction SilentlyContinue  
212:       If ($WMITest -eq $null) {  
213:            $Output += "success"  
214:       } else {  
215:            $Output += "Failed"  
216:            Exit 1  
217:       }  
218:       Write-Output $Output  
219:  }  
220:    
221:  function New-WMIInstance {  
222:  <#  
223:       .SYNOPSIS  
224:            Write new instance  
225:         
226:       .DESCRIPTION  
227:            A detailed description of the New-WMIInstance function.  
228:         
229:       .PARAMETER MappedDrives  
230:            List of mapped drives  
231:         
232:       .PARAMETER Class  
233:            A description of the Class parameter.  
234:         
235:       .EXAMPLE  
236:            PS C:\> New-WMIInstance  
237:         
238:       .NOTES  
239:            Additional information about the function.  
240:  #>  
241:         
242:       [CmdletBinding()]  
243:       param  
244:       (  
245:            [ValidateNotNullOrEmpty()][array]  
246:            $MappedDrives,  
247:            [string]  
248:            $Class  
249:       )  
250:         
251:       foreach ($MappedDrive in $MappedDrives) {  
252:            Set-WmiInstance -Class $Class -Arguments @{ ComputerName = $MappedDrive.ComputerName; DriveLetter = $MappedDrive.DriveLetter; DrivePath = $MappedDrive.DrivePath; Username = $MappedDrive.Username } | Out-Null  
253:       }  
254:  }  
255:    
256:  function Start-ConfigurationManagerClientScan {  
257:  <#   
258:       .SYNOPSIS   
259:            Initiate Configuration Manager Client Scan   
260:    
261:       .DESCRIPTION   
262:            This will initiate an SCCM action   
263:    
264:       .PARAMETER ScheduleID   
265:            GUID ID of the SCCM action   
266:    
267:       .NOTES   
268:            Additional information about the function.   
269:  #>  
270:    
271:       [CmdletBinding()]  
272:       param  
273:       (  
274:            [ValidateSet('00000000-0000-0000-0000-000000000121', '00000000-0000-0000-0000-000000000003', '00000000-0000-0000-0000-000000000010', '00000000-0000-0000-0000-000000000001', '00000000-0000-0000-0000-000000000021', '00000000-0000-0000-0000-000000000022', '00000000-0000-0000-0000-000000000002', '00000000-0000-0000-0000-000000000031', '00000000-0000-0000-0000-000000000108', '00000000-0000-0000-0000-000000000113', '00000000-0000-0000-0000-000000000111', '00000000-0000-0000-0000-000000000026', '00000000-0000-0000-0000-000000000027', '00000000-0000-0000-0000-000000000032')]$ScheduleID  
275:       )  
276:    
277:       $WMIPath = "\\" + $env:COMPUTERNAME + "\root\ccm:SMS_Client"  
278:       $SMSwmi = [wmiclass]$WMIPath  
279:       $Action = [char]123 + $ScheduleID + [char]125  
280:       [Void]$SMSwmi.TriggerSchedule($Action)  
281:  }  
282:    
283:  cls  
284:  #Get list of mapped drives for each user  
285:  $UserMappedDrives = Get-MappedDrives  
286:  #Write output to a text file if -OutputFile is specified  
287:  If ($OutputFile.IsPresent) {  
288:       If (($TextFileLocation -ne $null) -and ($TextFileLocation -ne "")) {  
289:            #Add backslash (\) to the end of the TextFileLocation if it is not present  
290:            If ($TextFileLocation[$TextFileLocation.Length - 1] -ne "\") {  
291:                 $TextFileLocation += "\"  
292:            }  
293:            #Write list of mapped drives to the specified text file.  
294:            [string]$OutputFile = [string]$TextFileLocation + $env:COMPUTERNAME + ".txt"  
295:       } else {  
296:            #Get the relative path this script was executed from  
297:            $RelativePath = Get-RelativePath  
298:            $OutputFile = $RelativePath + $env:COMPUTERNAME + ".txt"  
299:       }  
300:       If ((Test-Path $OutputFile) -eq $true) {  
301:            Remove-Item $OutputFile -Force  
302:       }  
303:       If (($UserMappedDrives -ne $null) -and ($UserMappedDrives -ne "")) {  
304:            $UserMappedDrives | Format-Table -AutoSize | Out-File $OutputFile -Width 255  
305:       }  
306:  }  
307:  If ($SCCMReporting.IsPresent) {  
308:       #Create the new WMI class to write the output data to  
309:       New-WMIClass -Class "Mapped_Drives"  
310:       #Write the output data as an instance to the WMI class  
311:       If ($UserMappedDrives -ne $null) {  
312:            New-WMIInstance -MappedDrives $UserMappedDrives -Class "Mapped_Drives"  
313:       }  
314:       #Invoke a hardware inventory to send the data to SCCM  
315:       Invoke-SCCMHardwareInventory  
316:  }  
317:  #Display list of mapped drives for each user  
318:  $UserMappedDrives | Format-Table  
319:    

14 October 2016

PowerShell: Assign Email Address to Environment Variable

Posted By: Mick Pletcher - 10:42 AM
Recently, we encountered a special situation where we needed to have an environment variable that consisted of the user's email address. That prompted me to write the following script that will do just that. The script needs to be executed as the user. I set it up in the logon script and hid the PowerShell script. This script was executed on Windows 10 machines, so I cannot guarantee it will execute on other OS versions.

NOTE: This script requires RSAT to be installed. in order to utilize the ActiveDirectory module.


You can get the script from here.


1:  <#  
2:       .SYNOPSIS  
3:            Email Address Environment Variable  
4:         
5:       .DESCRIPTION  
6:            This script will assign the user's email address to the environment variable EmailAddress.  
7:         
8:       .NOTES  
9:            ===========================================================================  
10:            Created with:      SAPIEN Technologies, Inc., PowerShell Studio 2016 v5.2.128  
11:            Created on:        10/12/2016 1:14 PM  
12:            Created by:        Mick Pletcher  
13:            Organization:  
14:            Filename:          EmailEnvVariable.ps1
15:            ===========================================================================  
16:         
17:       .PARAMETER  
18:            A description of the parameter.  
19:  #>  
20:  [CmdletBinding()]  
21:  param ()  
22:    
23:  Import-Module ActiveDirectory  
24:  #Delete Environment Variable  
25:  [System.Environment]::SetEnvironmentVariable("EmailAddress", $null, 'User')  
26:  #Get the email address associated with the username  
27:  $EmailAddress = (Get-ADUser $env:USERNAME -Properties mail).mail  
28:  #Create a user based environment variable called email address  
29:  [System.Environment]::SetEnvironmentVariable("EmailAddress", $EmailAddress, 'User')  
30:    

04 October 2016

PowerShell: Generate Cached and Online Mode Exchange Report

Posted By: Mick Pletcher - 2:04 PM














Recently, the firm started converting outlook over to cached mode. As we are converting machines, we needed a report for how many of them are done and how many are still in Online mode.

I found the some great info here on how to get the information from the exchange logs. The first thing that needs to be done is to retrieve the exchange logs from the directory where they reside on each exchange server. If the exchange servers have more than one drive, make sure you are looking on the right one. The logs are located here: %PROGRAMFILES%\Microsoft\Exchange Server\V14\Logging\RPC Client Access. You will want to copy them into the same directory as this script resides. I appended all of the logs into a single log file. The script will filter out duplicates.

I have included examples in the script documentation on command line executions of this script.

I must say that Sapien's PowerShell Studio made writing this script a breeze.

Here is the link to the file on my GitHub site.


ExchangeModeReporting.ps1
 <#  
      .SYNOPSIS  
           Exchange Cached Mode Reporting  
        
      .DESCRIPTION  
           This script will read the RPC client access logs from the exchange server and generate a report showing if a user is cached or classic mode. You will need to retrieve the RPC logs from each exchange server. To do so, go to the drive on the exchange server that has exchange installed. The following directory contains the log you will need. Delete the Log Text, #Software, #Version, #Log-type, #Date, and #Fields lines from the file. That leaves the raw data. You can merge all of the logs into one file. This script will filter out repetitive entries.   
        
      .PARAMETER LogFile  
           Name of the log file that contains the exchange RPC logs  
        
      .PARAMETER Cached  
           Generate report of systems in cached mode  
        
      .PARAMETER Online  
           Generate report of all systems in online mode  
        
      .PARAMETER Full  
           Generate report showing both cached and online users  
        
      .PARAMETER OutputFile  
           Name of the file to write the output to. If left blank, no file is written to.  
        
      .EXAMPLE  
           Generate a list of all machines in cached mode  
                powershell.exe -file ExchangeModeReporting.ps1 -Cached -LogFile "Exchange.LOG"  
   
           Generate a list of all machines in cached mode and export list to a .CSV  
                powershell.exe -file ExchangeModeReporting.ps1 -Cached -LogFile "Exchange.LOG" -OutputFile "Report.csv"  
   
           Generate a list of all machines in Online mode  
                powershell.exe -file ExchangeModeReporting.ps1 -Online -LogFile "Exchange.LOG"  
   
           Generate a list of all machines in either cached or online mode  
                powershell.exe -file ExchangeModeReporting.ps1 -Full -LogFile "Exchange.LOG"  
   
      .NOTES  
           ===========================================================================  
           Created with:     SAPIEN Technologies, Inc., PowerShell Studio 2016 v5.2.128  
           Created on:       10/4/2016 10:13 AM  
           Created by:       Mick Pletcher  
           Organization:  
           Filename:         ExchangeModeReporting.ps1  
           ===========================================================================  
 #>  
 [CmdletBinding()]  
 param  
 (  
      [ValidateNotNullOrEmpty()][string]  
      $LogFile,  
      [switch]  
      $Cached,  
      [switch]  
      $Online,  
      [switch]  
      $Full,  
      [string]  
      $OutputFile  
 )  
   
 function Get-RelativePath {  
 <#  
      .SYNOPSIS  
           Get the relative path  
        
      .DESCRIPTION  
           Returns the location of the currently running PowerShell script  
        
      .NOTES  
           Additional information about the function.  
 #>  
        
      [CmdletBinding()][OutputType([string])]  
      param ()  
        
      $Path = (split-path $SCRIPT:MyInvocation.MyCommand.Path -parent) + "\"  
      Return $Path  
 }  
   
 function Get-AccountInfo {  
 <#  
      .SYNOPSIS  
           Retrieve and format account infomation  
        
      .DESCRIPTION  
           This function will read the exchange log and extract the username and mailbox status, while putting the data into an object.  
        
      .EXAMPLE  
           PS C:\> Get-AccountInfo  
        
      .NOTES  
           Additional information about the function.  
 #>  
        
      [CmdletBinding()][OutputType([array])]  
      param ()  
        
      $RelativePath = Get-RelativePath  
      $Logs = Get-Content $RelativePath$LogFile  
      $Systems = @()  
      foreach ($Log in $Logs) {  
           $System = New-Object System.Management.Automation.PSObject  
           $SplitLog = $Log.split(",")  
           $Username = ((($SplitLog | Where-Object { $_ -like "*cn=*" }).split("/") | Where-Object { ($_ -like "*cn=*") -and ($_ -notcontains "cn=Recipients") }).split("="))[1]  
           $Mode = $SplitLog | Where-Object { ($_ -contains "Classic") -or ($_ -contains "Cached") }  
           If ($Mode -eq "Classic") {  
                $Mode = "Online"  
           }  
           $System | Add-Member -type NoteProperty -Name Username -Value $Username  
           $System | Add-Member -type NoteProperty -Name Mode -Value $Mode  
           If ($Systems.Username -notcontains $Username) {  
                $Systems += $System  
           }  
      }  
      $Systems = $Systems | Sort-Object  
      Return $Systems  
 }  
   
 $Logs = Get-AccountInfo  
 if ($Cached.IsPresent) {  
      $Logs = $Logs | Where-Object { $_.Mode -eq "Cached" } | Sort-Object Username  
      $Logs | Format-Table  
 }  
 if ($Online.IsPresent) {  
      $Logs = $Logs | Where-Object { ($_.Mode -eq "Online") } | Sort-Object Username  
      $Logs | Format-Table  
 }  
 if ($Full.IsPresent) {  
      $Logs | Sort-Object Username  
 }  
 if (($OutputFile -ne $null) -and ($OutputFile -ne "")) {  
      $RelativePath = Get-RelativePath  
      $Logs | Sort-Object Username | Export-Csv $RelativePath$OutputFile -NoTypeInformation  
 }  

02 September 2016

PowerShell: Taking Ownership of Files and Folders

Posted By: Mick Pletcher - 12:27 PM













Recently, I ran into a situation where a deployment required taking ownership of a specific folder and all subfolders, including files. While formulating a method of doing this, I wanted to also make sure the script not only took ownership, but also verified it happened.

With the help of Sapien's PowerShell Studio, I wrote the following function that will do just that. It will take ownership using the credentials the script is executed under. It will then query the item(s) that is takes ownership of and verify the ownership of the item matches the ownership the script is being executed under. It will then return an success/failure screen output that is color coded yellow for success and red for failure. The script can be used for either single folders or files, or using the -Recurse tells it to change ownership for all subfolders and files.

You can download the script from here.

TakeOwnership.ps1

1:  <#  
2:       .SYNOPSIS  
3:            A brief description of the TakeOwnership.ps1 file.  
4:         
5:       .DESCRIPTION  
6:            This script will grant ownership of files to the credentials this script is being executed under.  
7:         
8:       .PARAMETER FilesFolders  
9:            Files and folders to change permissions on.  
10:    
11:       .EXAMPLE  
12:            powershell.exe -executionpolicy bypass -file TakeOwnership.ps1 -FilesFolders "c:\Users\Mick\AppData\Roaming\Microsoft\Windows"  
13:         
14:       .NOTES  
15:            ===========================================================================  
16:            Created with:     SAPIEN Technologies, Inc., PowerShell Studio 2016 v5.2.127  
17:            Created on:       9/2/2016 9:49 AM  
18:            Created by:       Mick Pletcher  
19:            Organization:  
20:            Filename:         TakeOwnership.ps1  
21:            ===========================================================================  
22:  #>  
23:  [CmdletBinding()]  
24:  param  
25:  (  
26:       [ValidateNotNullOrEmpty()][string]  
27:       $FilesFolders  
28:  )  
29:    
30:  function Grant-FolderOwnership {  
31:  <#  
32:       .SYNOPSIS  
33:            Take FileFolder Ownership  
34:         
35:       .DESCRIPTION  
36:            Take ownership of the FileFolder  
37:         
38:       .PARAMETER FileFolder  
39:            File or FileFolder to take ownership of  
40:         
41:       .PARAMETER Recurse  
42:            Take ownership of all subfolders  
43:         
44:       .EXAMPLE  
45:            PS C:\> Grant-FolderOwnership -FileFolder 'Value1'  
46:         
47:       .NOTES  
48:            Additional information about the function.  
49:  #>  
50:         
51:       [CmdletBinding()]  
52:       param  
53:       (  
54:            [ValidateNotNullOrEmpty()][string]  
55:            $FileFolder,  
56:            [switch]  
57:            $Recurse  
58:       )  
59:         
60:       $Errors = $false  
61:       If ((Test-Path $FileFolder) -eq $true) {  
62:            $Output = "Taking ownership of " + $FileFolder + "....."  
63:            If ($Recurse.IsPresent) {  
64:                 #Take ownership of the top folder  
65:                 $Items = takeown.exe /F $FileFolder  
66:                 #Take ownership of all child folders and files  
67:                 $Items = Get-ChildItem $FileFolder -Recurse | ForEach-Object { takeown.exe /F $_.FullName }  
68:            } else {  
69:                 #Take ownership of the individual folder  
70:                 $Executable = takeown.exe /F $FileFolder  
71:            }  
72:       }  
73:       #Get the current user this script is being executed under  
74:       [string]$CurrentUser = [Environment]::UserDomainName + "\" + [Environment]::UserName  
75:       If ($Recurse.IsPresent) {  
76:            #Test if files are owned by the current user this script is being executed under  
77:            $Item = Get-Item $FileFolder | where-object { (get-acl $_.FullName).owner -ne $CurrentUser }  
78:            $Items = Get-ChildItem $FileFolder -Recurse | where-object { (get-acl $_.FullName).owner -ne $CurrentUser }  
79:            #If no files/folders were added to $Items, then it is a success  
80:            If ((($Item -ne "") -and ($Item -ne $null)) -and (($Items -ne "") -and ($Items -ne $null))) {  
81:                 $Output += "Failed"  
82:            } else {  
83:                 $Output += "Success"  
84:            }  
85:       } else {  
86:            [string]$FolderOwner = (get-acl $FileFolder).owner  
87:            If ($CurrentUser -ne $FolderOwner) {  
88:                 $Output += "Failed"  
89:                 $Errors = $true  
90:            } else {  
91:                 $Output += "Success"  
92:            }  
93:       }  
94:       Write-ToDisplay -Output $Output  
95:       If ($Errors -eq $true) {  
96:            #Error 5 is an arbitrary number I chose to flag if this fails  
97:            Exit 5  
98:       }  
99:  }  
100:    
101:  function Write-ToDisplay {  
102:  <#  
103:       .SYNOPSIS  
104:            Output Success/Failure to Display  
105:         
106:       .DESCRIPTION  
107:            Write the output to the Display color coded yellow for success and red for failure  
108:         
109:       .PARAMETER Output  
110:            Data to display to the screen  
111:         
112:       .EXAMPLE  
113:                      PS C:\> Write-ToDisplay -Output 'Value1'  
114:         
115:       .NOTES  
116:            Additional information about the function.  
117:  #>  
118:         
119:       [CmdletBinding()]  
120:       param  
121:       (  
122:            [ValidateNotNullOrEmpty()]$Output  
123:       )  
124:         
125:       $OutputSplit = (($Output.Replace(".", " ")).Replace("   ", ".")).Split(".")  
126:       Write-Host $OutputSplit[0]"....." -NoNewline  
127:       If ($OutputSplit[1] -like "*Success*") {  
128:            Write-Host $OutputSplit[1] -ForegroundColor Yellow  
129:       } elseif ($OutputSplit[1] -like "*Fail*") {  
130:            Write-Host $OutputSplit[1] -ForegroundColor Red  
131:       }  
132:  }  
133:    
134:  Grant-FolderOwnership -FileFolder $FilesFolders  
135:    

29 August 2016

PowerShell: Configuring Power Settings

Posted By: Mick Pletcher - 1:15 PM













This is another part of the Windows 10 project I am working on automating. This script will set the Windows 10 power settings. I created this, with the help of Sapien's PowerShell Studio that helped make this script much more robust, so that it can be executed during the image to configure the settings there. This allows me to take the settings out of GPO and put them on the local machine to help speed up the logon process. The users at the firm I work at do not have local administrator privileges, so this works great.

The advantage to using this script for configuring power settings, over using powercfg.exe directly, is that the script will verify the setting took place. It goes back and checks the new value to make sure it coincides with the desired value. If it was not successful, the script will return an error code 5, which I arbitrarily chose. This allows you to use the script in a build and it will report the error back to alert you at the end of the build if the power setting was unsuccessful.

The script can set individual power settings per the command line, you can hardcode the settings into the script (I commented out an example), or you can import a power scheme. I also included the feature to generate a detailed and formatted report of all power settings on a machine. The report not only displays to the screen, but it also generates a file named PowerSchemeReport.txt in the same directory as the script. I have included command line examples in the comments of the script.

Here is an example of the script generating a report:


Here is an example of setting the Monitor Timeout while plugged in to 120 minutes:


Here is an example of setting the power scheme from high performance to balanced:


Here is an example of the script importing a power scheme configuration file and setting it as the default:




You can download the file from here.


Set-PowerScheme.ps1

1:  <#  
2:       .SYNOPSIS  
3:            Set the Power Options  
4:         
5:       .DESCRIPTION  
6:            This script will set the preferred plan. It can also customize specific settings within a plan. The is an option to use the script for generating a report on the currently selected plan, along with all of the plan settings that is written both to the screen and to a log file. The script will exit with an error code 5 if any power setting failed. This allows for an error flag when used during a build.  
7:         
8:       .PARAMETER Balanced  
9:            Selects the balanced plan  
10:         
11:       .PARAMETER ConsoleTitle  
12:            Name for the PowerShell Console Title  
13:         
14:       .PARAMETER Custom  
15:            Enter a name to create a custom Power Plan  
16:         
17:       .PARAMETER HighPerformance  
18:            Selects the High Performance Plan  
19:         
20:       .PARAMETER ImportPowerSchemeFile  
21:            Import a power scheme file  
22:         
23:       .PARAMETER PowerSaver  
24:            Selects the Power Saver Plan  
25:         
26:       .PARAMETER PowerSchemeName  
27:            Name to use when renaming an imported scheme  
28:         
29:       .PARAMETER Report  
30:            Select this switch to generate a report of the currently selected plan  
31:         
32:       .PARAMETER SetPowerSchemeSetting  
33:            Set individual power scheme setting  
34:         
35:       .PARAMETER SetPowerSchemeSettingValue  
36:            Value associated with the Power Scheme Setting  
37:         
38:       .PARAMETER SetImportedPowerSchemeDefault  
39:            This is used in conjunction with the ImportPowerSchemeFile parameter. This tells the script to set the imported power scheme as the default.  
40:         
41:       .EXAMPLE  
42:            Set Power Settings to Balanced  
43:            powershell.exe -executionpolicy bypass -file Set-PowerScheme.ps1 -Balanced  
44:              
45:            Set Power Settings to High Performance  
46:            powershell.exe -executionpolicy bypass -file Set-PowerScheme.ps1 -HighPerformance  
47:              
48:            Set Power Settings to Power Saver  
49:            powershell.exe -executionpolicy bypass -file Set-PowerScheme.ps1 -PowerSaver  
50:              
51:            Generate a report named PowerSchemeReport.txt that resides in the same directory as this script. It contains a list of all power settings.  
52:            powershell.exe -executionpolicy bypass -file Set-PowerScheme.ps1 -Report  
53:              
54:            Set individual power scheme setting  
55:            powershell.exe -executionpolicy bypass -file Set-PowerScheme.ps1 -SetPowerSchemeSetting -MonitorTimeoutAC 120  
56:              
57:            Import power scheme file that resides in the same directory as this script and renames the scheme to the name defined under PowerSchemeName  
58:            powershell.exe -executionpolicy bypass -file Set-PowerScheme.ps1 -ImportPowerSchemeFile "CustomScheme.cfg" -PowerSchemeName "Custom"  
59:         
60:       .NOTES  
61:            ===========================================================================  
62:            Created with:     SAPIEN Technologies, Inc., PowerShell Studio 2016 v5.2.127  
63:            Created on:       8/16/2016 10:13 AM  
64:            Created by:       Mick Pletcher  
65:            Organization:  
66:            Filename:        Set-PowerScheme.ps1  
67:            ===========================================================================  
68:  #>  
69:  [CmdletBinding()]  
70:  param  
71:  (  
72:       [switch]  
73:       $Balanced,  
74:       [string]  
75:       $ConsoleTitle = 'PowerScheme',  
76:       [string]  
77:       $Custom,  
78:       [switch]  
79:       $HighPerformance,  
80:       [string]  
81:       $ImportPowerSchemeFile,  
82:       [switch]  
83:       $PowerSaver,  
84:       [string]  
85:       $PowerSchemeName,  
86:       [switch]  
87:       $Report,  
88:       [ValidateSet('MonitorTimeoutAC', 'MonitorTimeoutDC', 'DiskTimeoutAC', 'DiskTimeoutDC', 'StandbyTimeoutAC', 'StandbyTimeoutDC', 'HibernateTimeoutAC', 'HibernateTimeoutDC')][string]  
89:       $SetPowerSchemeSetting,  
90:       [string]  
91:       $SetPowerSchemeSettingValue,  
92:       [switch]  
93:       $SetImportedPowerSchemeDefault  
94:  )  
95:    
96:  function Get-PowerScheme {  
97:  <#  
98:       .SYNOPSIS  
99:            Get the currently active PowerScheme  
100:         
101:       .DESCRIPTION  
102:            This will query the current power scheme and return the GUID and user friendly name  
103:         
104:       .EXAMPLE  
105:            PS C:\> Get-PowerScheme  
106:         
107:       .NOTES  
108:            Additional information about the function.  
109:  #>  
110:         
111:       [CmdletBinding()][OutputType([object])]  
112:       param ()  
113:         
114:       #Get the currently active power scheme  
115:       $Query = powercfg.exe /getactivescheme  
116:       #Get the alias name of the active power scheme  
117:       $ActiveSchemeName = ($Query.Split("()").Trim())[1]  
118:       #Get the GUID of the active power scheme  
119:       $ActiveSchemeGUID = ($Query.Split(":(").Trim())[1]  
120:       $Query = powercfg.exe /query $ActiveSchemeGUID  
121:       $GUIDAlias = ($Query | where { $_.Contains("GUID Alias:") }).Split(":")[1].Trim()  
122:       $Scheme = New-Object -TypeName PSObject  
123:       $Scheme | Add-Member -Type NoteProperty -Name PowerScheme -Value $ActiveSchemeName  
124:       $Scheme | Add-Member -Type NoteProperty -Name GUIDAlias -Value $GUIDAlias  
125:       $Scheme | Add-Member -Type NoteProperty -Name GUID -Value $ActiveSchemeGUID  
126:       Return $Scheme  
127:  }  
128:    
129:  function Get-PowerSchemeSubGroupSettings {  
130:  <#  
131:       .SYNOPSIS  
132:            Get the Power Scheme SubGroup Settings  
133:         
134:       .DESCRIPTION  
135:            Retrieve all Settings and values within a subgroup  
136:         
137:       .PARAMETER Subgroup  
138:            Name and GUID of desired subgroup  
139:         
140:       .PARAMETER ActivePowerScheme  
141:            GUID and name of the active ActivePowerScheme  
142:         
143:       .EXAMPLE  
144:            PS C:\> Get-PowerSchemeSubGroupSettings -Subgroup $value1  
145:         
146:       .NOTES  
147:            Additional information about the function.  
148:  #>  
149:         
150:       [CmdletBinding()]  
151:       param  
152:       (  
153:            [ValidateNotNullOrEmpty()]$Subgroup,  
154:            [ValidateNotNullOrEmpty()][object]  
155:            $ActivePowerScheme  
156:       )  
157:         
158:       $Query = powercfg.exe /query $ActivePowerScheme.GUID $Subgroup.GUID  
159:       $Query = $Query | where { ((!($_.Contains($ActivePowerScheme.GUID))) -and (!($_.Contains($ActivePowerScheme.GUIDAlias)))) }  
160:       $Settings = @()  
161:       For ($i = 0; $i -lt $Query.Length; $i++) {  
162:            If ($Query[$i] -like "*Power Setting GUID:*") {  
163:                 $Setting = New-Object System.Object  
164:                 #Get the friendly name of the Power Setting  
165:                 $SettingName = $Query[$i].Split("()").Trim()  
166:                 $SettingName = $SettingName[1]  
167:                 #Get the alias of the power setting  
168:                 If ($Query[$i + 1] -like "*GUID Alias:*") {  
169:                      $SettingAlias = $Query[$i + 1].Split(":").Trim()  
170:                      $SettingAlias = $SettingAlias[1]  
171:                 } else {  
172:                      $SettingAlias = $null  
173:                 }  
174:                 #Get the GUID of the power setting  
175:                 $SettingGUID = $Query[$i].Split(":(").Trim()  
176:                 $SettingGUID = $SettingGUID[1]  
177:                 #Get the AC and DC power settings  
178:                 $j = $i  
179:                 Do {  
180:                      $j++  
181:                 }  
182:                 while ($Query[$j] -notlike "*Current AC Power Setting*")  
183:                 $SettingAC = $Query[$j].Split(":").Trim()  
184:                 $SettingAC = [Convert]::ToInt32($SettingAC[1], 16)  
185:                 $SettingDC = $Query[$j + 1].Split(":").Trim()  
186:                 $SettingDC = [Convert]::ToInt32($SettingDC[1], 16)  
187:                 $Setting | Add-Member -Type NoteProperty -Name Subgroup -Value $Subgroup.Subgroup  
188:                 $Setting | Add-Member -Type NoteProperty -Name Name -Value $SettingName  
189:                 $Setting | Add-Member -Type NoteProperty -Name Alias -Value $SettingAlias  
190:                 $Setting | Add-Member -Type NoteProperty -Name GUID -Value $SettingGUID  
191:                 $Setting | Add-Member -Type NoteProperty -Name AC -Value $SettingAC  
192:                 $Setting | Add-Member -Type NoteProperty -Name DC -Value $SettingDC  
193:                 $Settings += $Setting  
194:            }  
195:       }  
196:       Return $Settings  
197:  }  
198:    
199:  function Get-RelativePath {  
200:  <#  
201:       .SYNOPSIS  
202:            Get the relative path  
203:         
204:       .DESCRIPTION  
205:            Returns the location of the currently running PowerShell script  
206:         
207:       .NOTES  
208:            Additional information about the function.  
209:  #>  
210:         
211:       [CmdletBinding()][OutputType([string])]  
212:       param ()  
213:         
214:       $Path = (split-path $SCRIPT:MyInvocation.MyCommand.Path -parent) + "\"  
215:       Return $Path  
216:  }  
217:    
218:  function Get-SubGroupsList {  
219:  <#  
220:       .SYNOPSIS  
221:            Generate a list of subgroups  
222:         
223:       .DESCRIPTION  
224:            This will generate a list of the subgroups within the designated power scheme  
225:         
226:       .PARAMETER ActivePowerScheme  
227:            GUID and name of the active ActivePowerScheme  
228:         
229:       .EXAMPLE  
230:            PS C:\> Get-SubGroupsList  
231:         
232:       .NOTES  
233:            Additional information about the function.  
234:  #>  
235:         
236:       [CmdletBinding()][OutputType([object])]  
237:       param  
238:       (  
239:            [ValidateNotNullOrEmpty()][object]  
240:            $ActivePowerScheme  
241:       )  
242:         
243:       #Get all settings for the active power scheme  
244:       $Query = powercfg.exe /query $ActivePowerScheme.GUID  
245:       #Get a list of the subgroups  
246:       $Subgroups = @()  
247:       for ($i = 0; $i -lt $Query.Length; $i++) {  
248:            If (($Query[$i] -like "*Subgroup GUID:*") -and ($Query[$i + 1] -notlike "*Subgroup GUID:*")) {  
249:                 $Subgroup = New-Object System.Object  
250:                 $SubgroupName = $Query[$i].Split("()").Trim()  
251:                 $SubgroupName = $SubgroupName[1]  
252:                 If ($Query[$i + 1] -like "*GUID Alias:*") {  
253:                      $SubgroupAlias = $Query[$i + 1].Split(":").Trim()  
254:                      $SubgroupAlias = $SubgroupAlias[1]  
255:                 } else {  
256:                      $SubgroupAlias = $null  
257:                 }  
258:                 $SubgroupGUID = $Query[$i].Split(":(").Trim()  
259:                 $SubgroupGUID = $SubgroupGUID[1]  
260:                 $Subgroup | Add-Member -Type NoteProperty -Name Subgroup -Value $SubgroupName  
261:                 $Subgroup | Add-Member -Type NoteProperty -Name Alias -Value $SubgroupAlias  
262:                 $Subgroup | Add-Member -Type NoteProperty -Name GUID -Value $SubgroupGUID  
263:                 $Subgroups += $Subgroup  
264:            }  
265:       }  
266:       Return $Subgroups  
267:  }  
268:    
269:  function Import-PowerScheme {  
270:  <#  
271:       .SYNOPSIS  
272:            Import a Power Scheme  
273:         
274:       .DESCRIPTION  
275:            Imports a power scheme configuration file  
276:         
277:       .PARAMETER File  
278:            Name of the configuration file. This must reside in the same directory as this script.  
279:         
280:       .PARAMETER PowerSchemeName  
281:            Desired name for the imported power scheme  
282:         
283:       .PARAMETER SetActive  
284:            Set the imported scheme to active  
285:         
286:       .EXAMPLE  
287:            PS C:\> Import-PowerScheme -File 'Value1'  
288:         
289:       .NOTES  
290:            Additional information about the function.  
291:  #>  
292:         
293:       [CmdletBinding()][OutputType([boolean])]  
294:       param  
295:       (  
296:            [ValidateNotNullOrEmpty()][string]  
297:            $File,  
298:            [ValidateNotNullOrEmpty()][string]  
299:            $PowerSchemeName,  
300:            [switch]  
301:            $SetActive  
302:       )  
303:         
304:       $RelativePath = Get-RelativePath  
305:       $File = $RelativePath + $File  
306:       #Get list of all power schemes  
307:       $OldPowerSchemes = powercfg.exe /l  
308:       #Filter out all data except for the GUID  
309:       $OldPowerSchemes = $OldPowerSchemes | where { $_ -like "*Power Scheme GUID*" } | ForEach-Object { $_ -replace "Power Scheme GUID: ", "" } | ForEach-Object { ($_.split("?("))[0] }  
310:       Write-Host "Importing Power Scheme....." -NoNewline  
311:       #Import Power Scheme  
312:       $Output = powercfg.exe -import $File  
313:       #Get list of all power schemes  
314:       $NewPowerSchemes = powercfg.exe /l  
315:       #Filter out all data except for the GUID  
316:       $NewScheme = $NewPowerSchemes | where { $_ -like "*Power Scheme GUID*" } | ForEach-Object { $_ -replace "Power Scheme GUID: ", "" } | ForEach-Object { ($_.split("?("))[0] } | where { $OldPowerSchemes -notcontains $_ }  
317:       If ($NewScheme -ne $null) {  
318:            Write-Host "Success" -ForegroundColor Yellow  
319:            $Error = $false  
320:       } else {  
321:            Write-Host "Failed" -ForegroundColor Red  
322:            $Error = $true  
323:       }  
324:       #Rename imported power scheme  
325:       Write-Host "Renaming imported power scheme to"$PowerSchemeName"....." -NoNewline  
326:       $Switches = "/changename" + [char]32 + $NewScheme.Trim() + [char]32 + [char]34 + $PowerSchemeName + [char]34  
327:       $ErrCode = (Start-Process -FilePath "powercfg.exe" -ArgumentList $Switches -WindowStyle Minimized -Wait -Passthru).ExitCode  
328:       $NewPowerSchemes = powercfg.exe /l  
329:       If ($ErrCode -eq 0) {  
330:            $Test = $NewPowerSchemes | where { $_ -like ("*" + $PowerSchemeName + "*") }  
331:            If ($Test -ne $null) {  
332:                 Write-Host "Success" -ForegroundColor Yellow  
333:                 $Error = $false  
334:            } else {  
335:                 Write-Host "Failed" -ForegroundColor Red  
336:                 $Error = $true  
337:                 Return $Error  
338:            }  
339:       }  
340:       Write-Host "Setting"$PowerSchemeName" to default....." -NoNewline  
341:       $Switches = "-setactive " + $NewScheme.Trim()  
342:       $ErrCode = (Start-Process -FilePath "powercfg.exe" -ArgumentList $Switches -WindowStyle Minimized -Wait -Passthru).ExitCode  
343:       $Query = powercfg.exe /getactivescheme  
344:       #Get the alias name of the active power scheme  
345:       $ActiveSchemeName = (powercfg.exe /getactivescheme).Split("()").Trim()[1]  
346:       If ($ActiveSchemeName -eq $PowerSchemeName) {  
347:            Write-Host "Success" -ForegroundColor Yellow  
348:            $Error = $false  
349:       } else {  
350:            Write-Host "Failed" -ForegroundColor Red  
351:            $Error = $true  
352:       }  
353:       Return $Error  
354:  }  
355:    
356:  function Publish-Report {  
357:  <#  
358:       .SYNOPSIS  
359:            Publish a Power Scheme Report  
360:         
361:       .DESCRIPTION  
362:            This will publish a report of the currently active power scheme, a list of the power scheme subgroups, and a list of all subgroup settings.  
363:         
364:       .EXAMPLE  
365:            PS C:\> Publish-Report  
366:         
367:       .NOTES  
368:            Additional information about the function.  
369:  #>  
370:         
371:       [CmdletBinding()]  
372:       param ()  
373:         
374:       #Get the relative path this script is being executed from  
375:       $RelativePath = Get-RelativePath  
376:       #Get the currently enabled power scheme data  
377:       $ActivePowerScheme = Get-PowerScheme  
378:       #Get a list of all available subgroups  
379:       $PowerSchemeSubGroups = Get-SubGroupsList -ActivePowerScheme $ActivePowerScheme  
380:       #Get a list of all settings under each subgroup  
381:       $PowerSchemeSettings = @()  
382:       for ($i = 0; $i -lt $PowerSchemeSubGroups.Length; $i++) {  
383:            $PowerSchemeSubGroupSettings = Get-PowerSchemeSubGroupSettings -ActivePowerScheme $ActivePowerScheme -Subgroup $PowerSchemeSubGroups[$i]  
384:            $PowerSchemeSettings += $PowerSchemeSubGroupSettings  
385:       }  
386:       #Define the Report text file to write to  
387:       $ReportFile = $RelativePath + "PowerSchemeReport.txt"  
388:       #Remove old report if it exists  
389:       If ((Test-Path $ReportFile) -eq $true) {  
390:            Remove-Item -Path $ReportFile -Force  
391:       }  
392:       #Generate Header for Power Scheme Report  
393:       $Header = "ACTIVE POWER SCHEME REPORT"  
394:       $Header | Tee-Object -FilePath $ReportFile -Append  
395:       $Header = "--------------------------------------------------------------------------------"  
396:       $Header | Tee-Object -FilePath $ReportFile -Append  
397:       #Get Active Power Scheme report  
398:       $Output = $ActivePowerScheme | Format-Table  
399:       #Write output to report screen and file  
400:       $Output | Tee-Object -FilePath $ReportFile -Append  
401:       #Generate Header for power scheme subgroups report  
402:       $Header = "POWER SCHEME SUBGROUPS REPORT"  
403:       $Header | Tee-Object -FilePath $ReportFile -Append  
404:       $Header = "--------------------------------------------------------------------------------"  
405:       $Header | Tee-Object -FilePath $ReportFile -Append  
406:       $Output = $PowerSchemeSubgroups | Format-Table  
407:       #Write output to report screen and file  
408:       $Output | Tee-Object -FilePath $ReportFile -Append  
409:       #Generate Header for power scheme subgroup settings report  
410:       $Header = "POWER SCHEME SUBGROUP SETTINGS REPORT"  
411:       $Header | Tee-Object -FilePath $ReportFile -Append  
412:       $Header = "--------------------------------------------------------------------------------"  
413:       $Header | Tee-Object -FilePath $ReportFile -Append  
414:       $Output = $PowerSchemeSettings | Format-Table  
415:       #Write output to report screen and file  
416:       $Output | Tee-Object -FilePath $ReportFile -Append  
417:  }  
418:    
419:  function Set-ConsoleTitle {  
420:  <#  
421:       .SYNOPSIS  
422:            Console Title  
423:         
424:       .DESCRIPTION  
425:            Sets the title of the PowerShell Console  
426:         
427:       .PARAMETER Title  
428:            Title of the PowerShell Console  
429:         
430:       .NOTES  
431:            Additional information about the function.  
432:  #>  
433:         
434:       [CmdletBinding()]  
435:       param  
436:       (  
437:            [Parameter(Mandatory = $true)][String]  
438:            $Title  
439:       )  
440:         
441:       $host.ui.RawUI.WindowTitle = $Title  
442:  }  
443:    
444:  function Set-PowerScheme {  
445:  <#  
446:       .SYNOPSIS  
447:            Set the power scheme to the specified scheme  
448:         
449:       .DESCRIPTION  
450:            Sets the power scheme to the specified scheme  
451:         
452:       .PARAMETER PowerScheme  
453:            Friendly power scheme name  
454:         
455:       .PARAMETER CustomPowerScheme  
456:            Create a custom power scheme  
457:         
458:       .EXAMPLE  
459:            PS C:\> Set-PowerScheme -PowerScheme 'Value1'  
460:         
461:       .NOTES  
462:            Additional information about the function.  
463:  #>  
464:         
465:       [CmdletBinding()][OutputType([boolean])]  
466:       param  
467:       (  
468:            [ValidateSet('Balanced', 'High Performance', 'Power Saver')][string]  
469:            $PowerScheme,  
470:            [string]  
471:            $CustomPowerScheme  
472:       )  
473:         
474:       #Get list of existing power schemes  
475:       $PowerSchemes = powercfg.exe /l  
476:       If ($PowerScheme -ne $null) {  
477:            #Filter out all schemes except for $PowerScheme and return the GUID  
478:            $PowerSchemes = ($PowerSchemes | where { $_ -like "*" + $PowerScheme + "*" }).Split(":(").Trim()[1]  
479:            #Set power scheme  
480:            $ActivePowerScheme = Get-PowerScheme  
481:            $ActivePowerScheme.PowerScheme  
482:            Write-Host "Setting Power Scheme from"$ActivePowerScheme.PowerScheme"to"$PowerScheme"....." -NoNewline  
483:            $Output = powercfg.exe -setactive $PowerSchemes  
484:            $ActivePowerScheme = Get-PowerScheme  
485:            If ($PowerScheme -eq $ActivePowerScheme.PowerScheme) {  
486:                 Write-Host "Success" -ForegroundColor Yellow  
487:                 Return $false  
488:            } else {  
489:                 Write-Host "Failed" -ForegroundColor Red  
490:                 Return $true  
491:            }  
492:       }  
493:  }  
494:    
495:  function Set-PowerSchemeSettings {  
496:  <#  
497:       .SYNOPSIS  
498:            Modify current power scheme  
499:         
500:       .DESCRIPTION  
501:            This will modify settings of the currently active power scheme.  
502:         
503:       .PARAMETER MonitorTimeoutAC  
504:            Modify the time until the screensaver turns on while plugged into AC outlet  
505:         
506:       .PARAMETER MonitorTimeoutDC  
507:            Modify the time until the screensaver turns on while on battery power  
508:         
509:       .PARAMETER DiskTimeoutAC  
510:            Time that windows will wait for a hard disk to respond to a command while plugged into AC outlet  
511:         
512:       .PARAMETER DiskTimeoutDC  
513:            Time that windows will wait for a hard disk to respond to a command while on battery power  
514:         
515:       .PARAMETER StandbyTimeoutAC  
516:            Amount of time before a computer is put on standby while plugged into AC outlet  
517:         
518:       .PARAMETER StandbyTimeoutDC  
519:            Amount of time before a computer is put on standby while on battery power  
520:         
521:       .PARAMETER HibernateTimeoutAC  
522:            Amount of time before a computer is put in hibernation while plugged into AC outlet  
523:         
524:       .PARAMETER HibernateTimeoutDC  
525:            Amount of time before a computer is put in hibernation while on battery power  
526:         
527:       .EXAMPLE  
528:            PS C:\> Set-PowerSchemeSettings -MonitorTimeoutAC $value1 -MonitorTimeoutDC $value2  
529:         
530:       .NOTES  
531:            Additional information about the function.  
532:  #>  
533:         
534:       [CmdletBinding()]  
535:       param  
536:       (  
537:            [string]  
538:            $MonitorTimeoutAC,  
539:            [string]  
540:            $MonitorTimeoutDC,  
541:            [string]  
542:            $DiskTimeoutAC,  
543:            [string]  
544:            $DiskTimeoutDC,  
545:            [string]  
546:            $StandbyTimeoutAC,  
547:            [string]  
548:            $StandbyTimeoutDC,  
549:            [string]  
550:            $HibernateTimeoutAC,  
551:            [string]  
552:            $HibernateTimeoutDC  
553:       )  
554:         
555:       $Scheme = Get-PowerScheme  
556:       If (($MonitorTimeoutAC -ne $null) -and ($MonitorTimeoutAC -ne "")) {  
557:            Write-Host "Setting monitor timeout on AC to"$MonitorTimeoutAC" minutes....." -NoNewline  
558:            $Switches = "/change" + [char]32 + "monitor-timeout-ac" + [char]32 + $MonitorTimeoutAC  
559:            $TestKey = "Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Power\User\PowerSchemes\" + $Scheme.GUID + "\7516b95f-f776-4464-8c53-06167f40cc99\3c0bc021-c8a8-4e07-a973-6b14cbcb2b7e"  
560:            $TestValue = $MonitorTimeoutAC  
561:            $PowerIndex = "ACSettingIndex"  
562:       }  
563:       If (($MonitorTimeoutDC -ne $null) -and ($MonitorTimeoutDC -ne "")) {  
564:            Write-Host "Setting monitor timeout on DC to"$MonitorTimeoutDC" minutes....." -NoNewline  
565:            $Switches = "/change" + [char]32 + "monitor-timeout-dc" + [char]32 + $MonitorTimeoutDC  
566:            $TestKey = "Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Power\User\PowerSchemes\" + $Scheme.GUID + "\7516b95f-f776-4464-8c53-06167f40cc99\3c0bc021-c8a8-4e07-a973-6b14cbcb2b7e"  
567:            $TestValue = $MonitorTimeoutDC  
568:            $PowerIndex = "DCSettingIndex"  
569:       }  
570:       If (($DiskTimeoutAC -ne $null) -and ($DiskTimeoutAC -ne "")) {  
571:            Write-Host "Setting disk timeout on AC to"$DiskTimeoutAC" minutes....." -NoNewline  
572:            $Switches = "/change" + [char]32 + "disk-timeout-ac" + [char]32 + $DiskTimeoutAC  
573:            $TestKey = "Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Power\User\PowerSchemes\" + $Scheme.GUID + "\0012ee47-9041-4b5d-9b77-535fba8b1442\6738e2c4-e8a5-4a42-b16a-e040e769756e"  
574:            $TestValue = $DiskTimeoutAC  
575:            $PowerIndex = "ACSettingIndex"  
576:       }  
577:       If (($DiskTimeoutDC -ne $null) -and ($DiskTimeoutDC -ne "")) {  
578:            Write-Host "Setting disk timeout on DC to"$DiskTimeoutDC" minutes....." -NoNewline  
579:            $Switches = "/change" + [char]32 + "disk-timeout-dc" + [char]32 + $DiskTimeoutDC  
580:            $TestKey = "Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Power\User\PowerSchemes\" + $Scheme.GUID + "\0012ee47-9041-4b5d-9b77-535fba8b1442\6738e2c4-e8a5-4a42-b16a-e040e769756e"  
581:            $TestValue = $DiskTimeoutDC  
582:            $PowerIndex = "DCSettingIndex"  
583:       }  
584:       If (($StandbyTimeoutAC -ne $null) -and ($StandbyTimeoutAC -ne "")) {  
585:            Write-Host "Setting standby timeout on AC to"$StandbyTimeoutAC" minutes....." -NoNewline  
586:            $Switches = "/change" + [char]32 + "standby-timeout-ac" + [char]32 + $StandbyTimeoutAC  
587:            $TestKey = "Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Power\User\PowerSchemes\" + $Scheme.GUID + "\238c9fa8-0aad-41ed-83f4-97be242c8f20\29f6c1db-86da-48c5-9fdb-f2b67b1f44da"  
588:            $TestValue = $StandbyTimeoutAC  
589:            $PowerIndex = "ACSettingIndex"  
590:       }  
591:       If (($StandbyTimeoutDC -ne $null) -and ($StandbyTimeoutDC -ne "")) {  
592:            Write-Host "Setting standby timeout on DC to"$StandbyTimeoutDC" minutes....." -NoNewline  
593:            $Switches = "/change" + [char]32 + "standby-timeout-dc" + [char]32 + $StandbyTimeoutDC  
594:            $TestKey = "Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Power\User\PowerSchemes\" + $Scheme.GUID + "\238c9fa8-0aad-41ed-83f4-97be242c8f20\29f6c1db-86da-48c5-9fdb-f2b67b1f44da"  
595:            $TestValue = $StandbyTimeoutDC  
596:            $PowerIndex = "DCSettingIndex"  
597:       }  
598:       If (($HibernateTimeoutAC -ne $null) -and ($HibernateTimeoutAC -ne "")) {  
599:            Write-Host "Setting hibernate timeout on AC to"$HibernateTimeoutAC" minutes....." -NoNewline  
600:            $Switches = "/change" + [char]32 + "hibernate-timeout-ac" + [char]32 + $HibernateTimeoutAC  
601:            $TestKey = "Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Power\User\PowerSchemes\" + $Scheme.GUID + "\238c9fa8-0aad-41ed-83f4-97be242c8f20\9d7815a6-7ee4-497e-8888-515a05f02364"  
602:            [int]$TestValue = $HibernateTimeoutAC  
603:            $PowerIndex = "ACSettingIndex"  
604:       }  
605:       If (($HibernateTimeoutDC -ne $null) -and ($HibernateTimeoutDC -ne "")) {  
606:            Write-Host "Setting hibernate timeout on DC to"$HibernateTimeoutDC" minutes....." -NoNewline  
607:            $Switches = "/change" + [char]32 + "hibernate-timeout-dc" + [char]32 + $HibernateTimeoutDC  
608:            $TestKey = "Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Power\User\PowerSchemes\" + $Scheme.GUID + "\238c9fa8-0aad-41ed-83f4-97be242c8f20\9d7815a6-7ee4-497e-8888-515a05f02364"  
609:            $TestValue = $HibernateTimeoutDC  
610:            $PowerIndex = "DCSettingIndex"  
611:       }  
612:       $ErrCode = (Start-Process -FilePath "powercfg.exe" -ArgumentList $Switches -WindowStyle Minimized -Wait -Passthru).ExitCode  
613:       $RegValue = (((Get-ItemProperty $TestKey).$PowerIndex) /60)  
614:       #Round down to the nearest tenth due to hibernate values being 1 decimal off  
615:       $RegValue = $RegValue - ($RegValue % 10)  
616:       If (($RegValue -eq $TestValue) -and ($ErrCode -eq 0)) {  
617:            Write-Host "Success" -ForegroundColor Yellow  
618:            $Errors = $false  
619:       } else {  
620:            Write-Host "Failed" -ForegroundColor Red  
621:            $Errors = $true  
622:       }  
623:       Return $Errors  
624:  }  
625:    
626:    
627:  cls  
628:  #Set Errors variable to false to begin the script with no errors  
629:  $Errors = $false  
630:  #Set the title of the PowerShell console  
631:  Set-ConsoleTitle -Title $ConsoleTitle  
632:    
633:  <#Hardcoded Power Scheme Settings  
634:  $Errors = Set-PowerSchemeSettings -MonitorTimeoutAC 120  
635:  $Errors = Set-PowerSchemeSettings -MonitorTimeoutDC 120  
636:  $Errors = Set-PowerSchemeSettings -DiskTimeOutAC 120  
637:  $Errors = Set-PowerSchemeSettings -DiskTimeOutDC 120  
638:  $Errors = Set-PowerSchemeSettings -StandbyTimeoutAC 120  
639:  $Errors = Set-PowerSchemeSettings -StandbyTimeoutDC 120  
640:  $Errors = Set-PowerSchemeSettings -HibernateTimeoutAC 60  
641:  $Errors = Set-PowerSchemeSettings -HibernateTimeoutDC 60  
642:  #>  
643:    
644:  #Generate a report if -Report is specified  
645:  If ($Report.IsPresent) {  
646:       Publish-Report  
647:  }  
648:  #Set the Power Scheme to Balanced  
649:  If ($Balanced.IsPresent) {  
650:       $Errors = Set-PowerScheme -PowerScheme 'Balanced'  
651:  }  
652:  #Set the Power Scheme to Power Saver  
653:  If ($PowerSaver.IsPresent) {  
654:       $Errors = Set-PowerScheme -PowerScheme 'Power Saver'  
655:  }  
656:  #Set the Power Scheme to High Performance  
657:  If ($HighPerformance.IsPresent) {  
658:       $Errors = Set-PowerScheme -PowerScheme 'High Performance'  
659:  }  
660:  #Set the Power Scheme to Custom  
661:  If (($Custom -ne $null) -and ($Custom -ne "")) {  
662:       $Errors = Set-PowerScheme -PowerScheme $Custom  
663:  }  
664:  #Import a power scheme  
665:  If (($ImportPowerSchemeFile -ne $null) -and ($ImportPowerSchemeFile -ne "")) {  
666:       If ($SetImportedPowerSchemeDefault.IsPresent) {  
667:            $Errors = Import-PowerScheme -File $ImportPowerSchemeFile -PowerSchemeName $PowerSchemeName -SetActive  
668:       } else {  
669:            $Errors = Import-PowerScheme -File $ImportPowerSchemeFile -PowerSchemeName $PowerSchemeName  
670:       }  
671:  }  
672:  #Set individual power scheme setting from command line  
673:  If (($SetPowerSchemeSetting -ne $null) -and ($SetPowerSchemeSetting -ne "")) {  
674:       switch ($SetPowerSchemeSetting) {  
675:            "MonitorTimeoutAC" { $Errors = Set-PowerSchemeSettings -MonitorTimeoutAC $SetPowerSchemeSettingValue }  
676:            "MonitorTimeoutDC" { $Errors = Set-PowerSchemeSettings -MonitorTimeoutDC $SetPowerSchemeSettingValue }  
677:            "DiskTimeOutAC" { $Errors = Set-PowerSchemeSettings -DiskTimeOutAC $SetPowerSchemeSettingValue }  
678:            "DiskTimeOutDC" { $Errors = Set-PowerSchemeSettings -DiskTimeOutDC $SetPowerSchemeSettingValue }  
679:            "StandbyTimeoutAC" { $Errors = Set-PowerSchemeSettings -StandbyTimeoutAC $SetPowerSchemeSettingValue }  
680:            "StandbyTimeoutDC" { $Errors = Set-PowerSchemeSettings -StandbyTimeoutDC $SetPowerSchemeSettingValue }  
681:            "HibernateTimeoutAC" { $Errors = Set-PowerSchemeSettings -HibernateTimeoutAC $SetPowerSchemeSettingValue }  
682:            "HibernateTimeoutDC" { $Errors = Set-PowerSchemeSettings -HibernateTimeoutDC $SetPowerSchemeSettingValue }  
683:       }  
684:  }  
685:  #Exit with an error code 5 if errors were encountered during any of the power settings  
686:  If ($Errors -eq $true) {  
687:       Exit 5  
688:  }  
689:    

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