26 October 2016

SCCM Mapped Drives Report

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:    

1 comments:

  1. SCCM is reporting one mapped drive instead of 4 for my test machine

    ReplyDelete