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:    

14 October 2016

PowerShell: Assign Email Address to Environment Variable

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

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  
 }