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:
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:
The script is available on my GitHub site located here.
MappedDriveReport.ps1
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:
- Go into SCCM--->Administration Tab--->Client Settings---> Default Client Settings--->Hardware Inventory--->Set Classes.
- Click Add--->Connect.
- 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.
- Click Connect
- Click on the Class Name tab to sort by class name
- Scroll down to find MappedDrives and check the box
- 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:
- Right-click on a machine in the Assets and Compliance--->Devices
- Click Start--->Resource Explorer
- Click the plus beside Hardware
- 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:
SCCM is reporting one mapped drive instead of 4 for my test machine
ReplyDelete