Continuing the PowerShell experience, this time by searching the network for shares and listing the permissions assigned to each share. One reason to do this is to discover if any individual users have been assigned access rights on a share. It’s a best practice to only assign share rights to security groups because that centralizes rights assignment and reduces effort and chances for error. So, since we’re going to perform the same task against (potentially) a lot of servers and shares, it makes sense to develop a PowerShell function to perform the repetitive task. (One cool thing about PowerShell is that if it doesn’t do something that you want, you can almost always write some PowerShell to make it do it.) OK, so here’s the script (I’ll explain what happens right after the listing): function Get-SharePermission { Param($ComputerName = $env:ComputerName) $ShareData = @{} $Shares = @() Try { Get-WMIObject win32_Share -ComputerName $ComputerName -ErrorAction Stop | Foreach {$ShareData += @{$_.Name= @{'Path'=$_.Path;'Description'=$_.Description}}} $ShareSecurity = Get-WMIObject win32_LogicalShareSecuritySetting -comp $ComputerName foreach($Share in $ShareSecurity) { $ShareName = $Share.Name $ACLS = $Share.GetSecurityDescriptor().Descriptor.DACL foreach($ACL in $ACLS) { $User = "$($ACL.Trustee.Domain)\$($ACL.Trustee.Name)" switch ($ACL.AccessMask) { 2032127 {$Perm = "Full Control"} 1245631 {$Perm = "Change"} 1179817 {$Perm = "Read"} } $Shares += New-Object PSobject -Property @{ 'Server' = $ComputerName 'ShareName' = $ShareName 'Path' = $ShareData[$ShareName].Path 'Description' = $ShareData[$ShareName].Description 'User' = $User 'Permission' = $Perm } # End property specification } # End each ACL } # End each Share } # End Try Catch {} # End Catch Return $Shares } The function command defines a new function (Get-SharePermission, in this case). The script that tells how to do that is enclosed in the outermost curly braces. The function begins be setting up some internal variables. The Param statement identifies the parameters that are passed into function for to operate on, in this case to be inserted into a variable named $ComputerName, with a default value of the current computer. This is followed by the creation of two empty local variables ($ShareData [a hash table or .NET Dictionary] and $Share [an array]). (Hash tables are indicated by @{} and arrays are indicated by @().) If you don’t know what a hash table is, it’ll become clear shortly. The actual work of this function happens in a Try/Catch block. In this case, the Catch block is empty because we don’t care if a share doesn’t exist. The Try block begins with a call to collect the win32_Share WMI data from the computer parameter. The –ErrorAction Stop clause causes an immediate transfer to the Catch block which essentially exits the function. Given that there are shares on the server, we add a hash table with the share name as the key and a nested hash table (with the path and description values) as the value. Later in the script we’ll need to be able to retrieve these. The next step is to collect all the LogicalShareSecuritySettings from WMI. For each of these, we then get the ACLs assigned to the shares (please note here that most of the admin shares (like C$) don’t have ACLs assigned and won’t show up here. But, that’s OK in this case because we’re really only looking for ACLs assigned to individual users and the admin shares are only accessible to the local administrators group. From the ACLs, we’re going to create a custom object with the data we have collected. We extract the user name from the ACL’s Trustee property, joining the domain and user names with a “/”. We then translate the AccessMask integers into text in the Switch block. With that, we can populate the $Share object with the data we need. The final statement in the function returns the data that we collected as the value of the function. Now that the function is defined, we can start collecting data from the servers. $AllShares = @() foreach ($Svr in (Get-ADComputer -Filter {OperatingSystem -like "*Server*"})) { If (Test-Connection $Svr.Name -Count 1 -Quiet) { Write-Host "Collecting Share Data from $($Svr.Name)" foreach ($shr in (Get-SharePermission $Svr.Name | Group-Object ShareName)) { $Perms = ($Shr.Group | Select @{N='Permissions';E={"$($_.User) ($($_.Permission))"}}).Permissions -join "`n" $AllShares += [psCustomObject]@{'Server'=$Svr.Name;'ShareName'=$Shr.Name;'Path'=$Shr.Group[0].Path;'Permissions'=$Perms} } # end each Share } # end ping server } # end for each server This is accomplished by first creating an empty array ($AllShares) that we’re going to populate with the results of the function call. The foreach block works by collecting the names of all the computers in AD that have “Server” in the operating system name. The first step in this is to ping the server. Note that this is performed in an IF-Then-Else block because test-connection –quiet call normally returns True or False, and only returns an error if it can’t find the computer in DNS. Thus, in a Try block, it would continue executing even if the server were not reachable. The next statement prints the name of the server being examined to the console. We then process the share data collected. That data is grouped by share name in order to consolidate the results under the server and share. The $Perms line joins each of the users and their permissions for that particular share. A new object containing the desired results is added to the AllShares collection. We can then do what we need to with the finished data, such as export it to a CSV file using the ExportCsv cmdlet and then open it in Excel for further analysis.