ACLReportTools.psm1
#Requires -Version 2.0 ############################################################################################ # Data Sections ############################################################################################ $Script:Html_Header = Data { @' <!doctype html><html><head><title>{0}</title> <style type="text/css"> h1, h2, h3, h4, h5, h6, p, a, ul, li, ol, td, label, input, span, div {{font-weight:normal !important; font-family:Tahoma, Arial, Helvetica, sans-serif;}} .nochange {{ color: gray; font-style: italic;}} .computeradded {{color: green; font-weight: bold;}} .computerremoved {{color: red; font-weight: bold;}} .shareremoved {{color: red; font-weight: bold;}} .shareadded {{color: green; font-weight: bold;}} .shareremoved {{color: red; font-weight: bold;}} .permissionremoved {{color: red; font-weight: bold;}} .permissionchanged {{color: orange; font-weight: bold;}} .permissionadded {{ color: green; font-weight: bold;}} .typelabel {{ color: gray; font-weight: heavy;}} .h2 {{ color: black; font-weight: heavy;}} .h3 {{ color: black; font-weight: heavy; font-style: italic;}} </style> </head> <body> <h1>{0}</h1> '@ } $Script:Html_Footer = Data { @' </body></html> '@ } $Script:Html_ComputerName = Data { @' <h2>Differences on Computer {0}</h2> '@ } $Script:Html_ShareName = Data { @' <h3>Differences in Share {0}</h3> '@ } $Script:Html_DifferenceLine = Data { @' <span class='typelabel'>{0}: </span><span class='{1}'>{2}</span><br> '@ } ############################################################################################ # Main CmdLets ############################################################################################ <# .SYNOPSIS Creates a list of Share, File and Folder ACLs for the specified shares/computers. .DESCRIPTION Produces an array of [ACLReportTools.Permission] objects for the computers provided. Specific shares can be specified or excluded using the Include/Exclude parameters. The report can be stored for use as a comparison in either a variable or as a file using the Export-ACLReport cmdlet (found in this module). For example: New-ACLShareReport -ComputerName CLIENT01 -Include MyShare,OtherShare | Export-ACLReport -path c:\ACLReports\CLIENT01_2014_11_14.acl .PARAMETER ComputerName This is the computer(s) to create the ACL Share report for. The Computer names can also be passed in via the pipeline. .PARAMETER Include This is a list of shares to include from the report. If this parameter is not set it will default to including all shares. This parameter can't be set if the Exclude parameter is set. .PARAMETER Exclude This is a list of shares to exclude from the report. If this parameter is not set it will default to excluding no shares. This parameter can't be set if the Include parameter is set. .PARAMETER IncludeInherited Setting this switch will cause the non inherited file/folder ACLs to be pulled recursively. .EXAMPLE New-ACLShareReport -ComputerName CLIENT01 Creates a report of all the Share and file/folder ACLs on the CLIENT01 machine. .EXAMPLE New-ACLShareReport -ComputerName CLIENT01 -Include MyShare,OtherShare Creates a report of all the Share and file/folder ACLs on the CLIENT01 machine that are in shares named either MyShare or OtherShare. .EXAMPLE New-ACLShareReport -ComputerName CLIENT01 -Exclude SysVol Creates a report of all the Share and file/folder ACLs on the CLIENT01 machine that are in shares not named SysVol. #> Function New-ACLShareReport { [CmdLetBinding()] param ( [Parameter( ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)] [String[]]$ComputerName=$env:computername, [String[]]$Include, [String[]]$Exclude, [Switch]$IncludeInherited ) # param begin { [ACLReportTools.Permission[]]$acls = $null $null = $PSBoundParameters.Remove('includeinherited') } # Begin process { $Shares = Get-ACLShare @PSBoundParameters $acls += $Shares | Get-ACLShareACL if ($IncludeInherited) { $acls += $Shares | Get-ACLShareFileACL -Recurse } else { $acls += $Shares | Get-ACLShareFileACL -Recurse -IncludeInherited } } # Process end { return $acls } # End } # Function New-ACLShareReport <# .SYNOPSIS Creates a list of File and Folder ACLs for the provided path(s). .DESCRIPTION Produces an array of [ACLReportTools.Permission] objects for the list of paths provided. The report can be stored for use as a comparison in either a variable or as a file using the Export-ACLReport cmdlet (found in this module). For example: New-ACLPathFileReport -Path e:\public | Export-ACLReport -path c:\ACLReports\Public_2015-04-04.acl .PARAMETER Path This is the path(s) to create the ACL PathFile report for. .PARAMETER IncludeInherited Setting this switch will cause the non inherited file/folder ACLs to be pulled recursively. .EXAMPLE New-ACLPathFileReport -Path e:\public Creates a report of all the file/folder ACLs in the e:\public folder on this machine. #> Function New-ACLPathFileReport { [CmdLetBinding()] param ( [Parameter( ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)] [String[]]$Path=(Convert-path -Path .), [Switch]$IncludeInherited ) # param begin { [ACLReportTools.Permission[]]$acls = $null $null = $PSBoundParameters.Remove('path') } # Begin process { Foreach ($p in $Path) { $acls += Get-ACLPathFileACL -Path $p -Recurse @PSBoundParameters } } # Process end { return $acls } # End } # Function New-ACLPathFileReport <# .SYNOPSIS Export an ACL Report as a file. .DESCRIPTION This Cmdlet will save whatever ACL Report that is in the pipeline to a file. This cmdlet just calls Export-ACLPermission although at some point will add additional functionality. .PARAMETER Path This is the path to the ACL Permission Report output file. This parameter is required. .PARAMETER InputObject Specifies the Permissions objects to export to the file. Enter a variable that contains the objects or type a command or expression that gets the objects. You can also pipe ACLReportTools.Permission objects to this cmdlet. .PARAMETER Force Causes the file to be overwritten if it exists. .EXAMPLE New-ACLShareReport -ComputerName CLIENT01 -Include MyShare,OtherShare | Export-ACLReport -path c:\ACLReports\CLIENT01_2014_11_14.acl Creates a new ACL Share Report for Computer Client01 for the MyShare and OtherShares and exports it to the file C:\ACLReports\CLIENT01_2014_11_14.acl. .EXAMPLE Export-ACLReport -Path C:\ACLReports\server01.acl -InputObject $ShareReport Saves the ACLs in the $ShareReport variable to the file C:\ACLReports\server01.acl. .EXAMPLE Export-ACLReport ` -Path C:\ACLReports\server01.acl ` -InputObject (New-ACLShareReport -ComputerName SERVER01) -Force Saves the file ACLs for all shares on the compuer SERVER01 to the file C:\ACLReports\server01.acl. If the file exists it will be overwritten. .EXAMPLE New-ACLShareReport -ComputerName SERVER01 | Export-ACLReport -Path C:\ACLReports\server01.acl -Force Saves the file ACLs for all shares on the compuer SERVER01 to the file C:\ACLReports\server01.acl. If the file exists it will be overwritten. #> function Export-ACLReport { [CmdLetBinding()] param ( [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [String]$Path, [Parameter(Mandatory=$true, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)] [ValidateScript({$_.GetType().FullName -ne 'ACLReportTools.Permission[]'})] [ACLReportTools.Permission[]]$InputObject, [Switch]$Force ) # param begin { [ACLReportTools.Permission[]]$InputObjectNew = $Null } process { foreach ($I in $InputObject) { $InputObjectNew += $I } } end { [Void]$PSBoundParameters.Remove('InputObject') $InputObjectNew | Export-ACLPermission @PSBoundParameters } } # Function Export-ACLReport <# .SYNOPSIS Export an ACL Permission Diff Report as a file. .DESCRIPTION This Cmdlet will save whatever ACL Permission Diff Report that is in the pipeline to a file. This cmdlet just calls Export-ACLPermissionDiff although at some point will add additional functionality. .PARAMETER Path This is the path to the ACL Permission Diff Report output file. This parameter is required. .PARAMETER InputObject Specifies the Permissions objects to export to the file. Enter a variable that contains the objects or type a command or expression that gets the objects. You can also pipe ACLReportTools.PermissionDiff objects to Export-ACLReport. .PARAMETER Force Causes the file to be overwritten if it exists. .EXAMPLE Compare-ACLReports -Baseline (Import-ACLReports ` -Path c:\ACLReports\CLIENT01_2014_11_14.acl) ` -With (Get-ACLReport -ComputerName CLIENT01) | Export-ACLDiffReport -Path "$HOME\Documents\Compare.acr" This will perform a comparison of the current share ACL report from computer CLIENT01 with the stored share ACL report in file c:\ACLReports\CLIENT01_2014_11_14.acl and then export the report file to $HOME\Documents\Compare.acr #> function Export-ACLDiffReport { [CmdLetBinding()] param ( [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [String]$Path, [Parameter(Mandatory=$true, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)] [ValidateScript({$_.GetType().FullName -ne 'ACLReportTools.PermissionDiff[]'})] [ACLReportTools.PermissionDiff[]]$InputObject, [Switch]$Force ) # param begin { [ACLReportTools.PermissionDiff[]]$InputObjectNew = $Null } process { foreach ($I in $InputObject) { $InputObjectNew += $I } } end { [Void]$PSBoundParameters.Remove('InputObject') $InputObjectNew | Export-ACLPermissionDiff @PSBoundParameters } } # Function Export-ACLDiffReport <# .SYNOPSIS Import the ACL Report that is in a file. .DESCRIPTION This Cmdlet will import all the ACL Report (ACLReportTools.Permission) objects from a specified file into the pipeline. This cmdlet just calls Import-ACLPermission although at some point will add additional functionality. .PARAMETER Path This is the path to the ACL Permission Report file to import. This parameter is required. .EXAMPLE Import-ACLReport -Path C:\ACLReports\server01.acl Imports the ACL Share Report from the file C:\ACLReports\server01.acl and puts it into the pipeline #> function Import-ACLReport { [CmdLetBinding()] # [OutputType([ACLReportTools.Permission])] param ( [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [String]$Path ) # param Import-ACLPermission @PSBoundParameters } # Function Import-ACLReport <# .SYNOPSIS Import the ACL Difference Report that is in a file. .DESCRIPTION This Cmdlet will import all the ACL Difference Report (ACLReportTools.PermissionDiff) objects from a specified file into the pipeline. This cmdlet just calls Import-ACLPermissionDiff although at some point will add additional functionality. .PARAMETER Path This is the path to the ACL Permission Report file to import. This parameter is required. .EXAMPLE Import-ACLDiffReport -Path C:\ACLReports\server01.acr Imports the ACL Share Report from the file C:\ACLReports\server01Permission and puts it into the pipeline #> function Import-ACLDiffReport { [CmdLetBinding()] # [OutputType([ACLReportTools.PermissionDiff])] param ( [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [String]$Path ) # param Import-ACLPermissionDiff @PSBoundParameters } # Function Import-ACLDiffReport <# .SYNOPSIS Compares two ACL reports and produces an ACL Difference report. .DESCRIPTION This cmdlets compares two ACL Share reports and produces a difference list in the pipeline that can then be reported on. A baseline report (usually from importing a previous ACL Share Report) must be provided. The second ACL Share report (called the current ACL Share report) will be compared against the baseline report. The current ACL report will be either generated by the New-ACLShareReport or New-ACLPathFileReport cmdlets (depending on parameters) or it can be passed in via the With variable. .PARAMETER Baseline This is the baseline report data the comparison will focus on. It will usually be pulled in from a previously saved Share ACL report via the Import-ACLReports .PARAMETER ComputerName This is the computer(s) to generate the current list of Share ACLs for to perform the comparison with the baseline. The Computer names can also be passed in via the pipeline. This parameter should not be used if the With Parameter is provided. .PARAMETER Include This is a list of shares to include from the comparison. If this parameter is not set it will default to including all shares. This parameter can't be set if the Exclude parameter is set. This parameter should not be used if the With Parameter is provided. .PARAMETER Exclude This is a list of shares to exclude from the comparison. If this parameter is not set it will default to excluding no shares. This parameter can't be set if the Include parameter is set. This parameter should not be used if the With Parameter is provided. .PARAMETER With This parameter provides an ACL Share report to compare with the Baseline ACL Share report. This parameter should not be used if the ComputerName Parameter is provided. .PARAMETER ReportNoChange Setting this switch will cause a 'No Change' report item to be shown when a share is identical in both the baseline and current reports. .PARAMETER IncludeInherited Setting this switch will cause the non inherited file/folder ACLs to be pulled recursively. .EXAMPLE Compare-ACLReports ` -Baseline (Import-ACLReports -Path c:\ACLReports\CLIENT01_2014_11_14.acl) ` -With (Get-ACLReport -ComputerName CLIENT01) This will perform a comparison of the current share ACL report from computer CLIENT01 with the stored share ACL report in file c:\ACLReports\CLIENT01_2014_11_14.acl .EXAMPLE Compare-ACLReports ` -Baseline (Import-ACLReports -Path c:\ACLReports\CLIENT01_2014_11_14.acl) ` -ComputerName CLIENT01 This will perform a comparison of the current share ACL report from computer CLIENT01 with the stored share ACL report in file c:\ACLReports\CLIENT01_2014_11_14.acl .EXAMPLE Compare-ACLReports ` -Baseline (Import-ACLReports -Path c:\ACLReports\CLIENT01_2014_11_14_SHARE01_ONLY.acl) ` -ComputerName CLIENT01 ` -Include SHARE01 This will perform a comparison of the current share ACL report from computer CLIENT01 for only SHARE01 with the stored share ACL report in file c:\ACLReports\CLIENT01_2014_11_14_SHARE01_ONLY.acl .EXAMPLE "CLIENT01" | Compare-ACLReports ` -Baseline (Import-ACLReports -Path c:\ACLReports\CLIENT01_2014_11_14.acl) This will perform a comparison of the current share ACL report from computer CLIENT01 with the stored share ACL report in file c:\ACLReports\CLIENT01_2014_11_14.acl .EXAMPLE Compare-ACLReports ` -Baseline (Import-ACLReports -Path c:\ACLReports\CLIENT01_2014_11_14.acl) ` -With (Import-ACLReports -Path c:\ACLReports\CLIENT01_2014_06_01.acl) This will perform a comparison of the share ACL report in file c:\ACLReports\CLIENT01_2014_06_01.acl with the stored share ACL report in file c:\ACLReports\CLIENT01_2014_11_14.acl #> Function Compare-ACLReports { [CmdLetBinding()] param ( [Parameter( Mandatory=$true)] [ValidateScript( { ($_.GetType() -ne 'ACLReportTools.Permission') ` -and ($_.GetType() -ne 'Deserialized.ACLReportTools.Permission') } )] [Object[]]$Baseline, [Parameter( ParameterSetName='CompareToCurrentShares', ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)] [String[]]$ComputerName=$env:computername, [Parameter( ParameterSetName='CompareToCurrentShares')] [String[]]$Include, [Parameter( ParameterSetName='CompareToCurrentShares')] [String[]]$Exclude, [Parameter( ParameterSetName='CompareToCurrentFiles')] [String[]]$Path, [Parameter( ParameterSetName='CompareToOther')] [ACLReportTools.Permission[]]$With, [Switch]$ReportNoChange, [Switch]$IncludeInherited ) # param begin { [ACLReportTools.PermissionDiff[]]$Comparison = $Null } # Begin process { switch ($PSCmdLet.ParameterSetName) { 'CompareToCurrentShares' { # A report to compare to wasn't specified so we need to generate # the current report using the other parameters passed. $null = $PSBoundParameters.Remove('Baseline') $null = $PSBoundParameters.Remove('With') $null = $PSBoundParameters.Remove('ReportNoChange') $null = $PSBoundParameters.Remove('Path') Write-Verbose -Message 'Assembling current ACL Share report for comparison.' [ACLReportTools.Permission[]]$With += New-ACLShareReport @PSBoundParameters Break } 'CompareToCurrentFiles' { # A report to compare to wasn't specified so we need to generate # the current report using the other parameters passed. $null = $PSBoundParameters.Remove('Baseline') $null = $PSBoundParameters.Remove('With') $null = $PSBoundParameters.Remove('ReportNoChange') $null = $PSBoundParameters.Remove('ComputerName') $null = $PSBoundParameters.Remove('Include') $null = $PSBoundParameters.Remove('Exclude') Write-Verbose -Message 'Assembling current ACL Path File report for comparison.' [ACLReportTools.Permission[]]$With += New-ACLPathFileReport @PSBoundParameters Break } } } # Process end { # The actual comparions is performed now # Get list of shares and computers we are going to compare the ACLs from $Current_Computers = $With | Select-Object -ExpandProperty ComputerName -Unique if ($Current_Computers.Length -eq 0) { Write-Error 'No accessible shares were found on the computers specified.' Return } else { $Baseline_Computers = $Baseline | Select-Object -ExpandProperty ComputerName -Unique foreach ($Current_Computer in $current_Computers) { # Perform a share comparison on each of the current computers If ($baseline_Computers -contains $Current_Computer) { Write-Verbose -Message "Performing share comparison of computer $Current_Computer." # Assemble the list of shares for the computer $Current_Shares = $With | Where-Object -Property ComputerName -eq $Current_Computer | Select-Object -ExpandProperty Share -Unique $Baseline_Shares = $Baseline | Where-Object -Property ComputerName -eq $Current_Computer | Select-Object -ExpandProperty Share -Unique foreach ($current_share in $current_shares) { if ($baseline_shares -contains $current_share) { Write-Verbose -Message "Performing share comparison of share $Current_Share on computer $Current_Computer." # Assemble list of ACLS for share/computer $Filter = [ScriptBlock]::Create({ ($_.ComputerName -eq $Current_Computer) ` -and ($_.Share -eq $Current_Share) -and ($_.Type -eq [ACLReportTools.PermissionTypeEnum]::Share) }) $Current_Share_Acls = $With | Where-Object -FilterScript $Filter $Baseline_Share_Acls = $Baseline | Where-Object -FilterScript $Filter [boolean]$changes = $false # Now compare the current share ACLs wth the baseline share ACLs foreach ($current_share_acl in $current_share_acls) { [string]$c_accesscontroltype = $current_share_acl.Access.AccessControlType [string]$c_filesystemrights = $current_share_acl.Access.AccessRights [string]$c_identityreference = $current_share_acl.Access.Account [boolean]$acl_found = $false foreach ($baseline_share_acl in $baseline_share_acls) { [string]$b_accesscontroltype = $baseline_share_acl.Access.AccessControlType [string]$b_filesystemrights = $baseline_share_acl.Access.AccessRights [string]$b_identityreference = $baseline_share_acl.Access.Account if ($c_identityreference -eq $b_identityreference) { $acl_found = $true break } # If } # Foreach if ($acl_found) { # The IdentityReference (user) exists in both the Baseline and the Current ACLs # Check it's the same though if ($c_filesystemrights -ne $b_filesystemrights) { # The Permission rights are different $DiffMessage = "Share permission rights changed from '$b_filesystemrights' to '$c_filesystemrights' for '$c_identityreference'." Write-Verbose -Message $DiffMessage $Comparison += New-PermissionDiffObject ` -Type ([ACLReportTools.PermissionTypeEnum]::Share) ` -DiffType ([ACLReportTools.PermissionDiffEnum]::'Permission Rights Changed') ` -ComputerName $Current_Computer -Share $Current_Share ` -Difference $DiffMessage $changes = $true } elseif ($c_accesscontroltype -ne $b_accesscontroltype) { # The Permission access control type is different $DiffMessage = "Share permission '$c_filesystemrights $c_accesscontroltype' for '$c_identityreference' added." Write-Verbose -Message $DiffMessage $Comparison += New-PermissionDiffObject ` -Type ([ACLReportTools.PermissionTypeEnum]::Share) ` -DiffType ([ACLReportTools.PermissionDiffEnum]::'Permission Access Control Changed') ` -ComputerName $Current_Computer -Share $Current_Share ` -Difference $DiffMessage $changes = $true } # If } else { # The ACL wasn't found in the baseline so it must be newly added $DiffMessage = "Share permission '$c_filesystemrights $c_accesscontroltype' for '$c_identityreference' added." Write-Verbose -Message $DiffMessage $Comparison += New-PermissionDiffObject ` -Type ([ACLReportTools.PermissionTypeEnum]::Share) ` -DiffType ([ACLReportTools.PermissionDiffEnum]::'Permission Added') ` -ComputerName $Current_Computer -Share $Current_Share ` -Difference $DiffMessage $changes = $true } # If } # Foreach # Now compare the baseline share ACLs wth the current share ACLs # We only need to check if a ACL has been removed from the baseline foreach ($baseline_share_acl in $baseline_share_acls) { [string]$b_accesscontroltype = $baseline_share_acl.Access.AccessControlType [string]$b_filesystemrights = $baseline_share_acl.Access.AccessRights [string]$b_identityreference = $baseline_share_acl.Access.Account [boolean]$acl_found = $false foreach ($current_share_acl in $current_share_acls) { [string]$c_accesscontroltype = $current_share_acl.Access.AccessControlType [string]$c_filesystemrights = $current_share_acl.Access.AccessRights [string]$c_identityreference = $current_share_acl.Access.Account if ($c_identityreference -eq $b_identityreference) { $acl_found = $true break } # If } # Foreach if (-not $acl_found) { # The IdentityReference (user) exists in the Baseline but not in the Current $DiffMessage = "Share permission '$b_filesystemrights $b_accesscontroltype' for '$b_identityreference' removed." Write-Verbose -Message $DiffMessage $Comparison += New-PermissionDiffObject ` -Type ([ACLReportTools.PermissionTypeEnum]::Share) ` -DiffType ([ACLReportTools.PermissionDiffEnum]::'Permission Removed') ` -ComputerName $Current_Computer -Share $Current_Share ` -Difference $DiffMessage $changes = $true } # If } # Foreach # Perform the baseline to current file/folder ACL comparison $Filter = [ScriptBlock]::Create({ ($_.ComputerName -eq $Current_Computer) ` -and ($_.Share -eq $Current_Share) ` -and (($_.Type -eq [ACLReportTools.PermissionTypeEnum]::File) ` -or ($_.Type -eq [ACLReportTools.PermissionTypeEnum]::Folder)) }) $Current_file_Acls = $With | Where-Object -FilterScript $Filter $Baseline_file_Acls = $Baseline | Where-Object -FilterScript $Filter [string]$last_path = '.' foreach ($current_file_acl in $current_file_acls) { # Put all the Current File ACL props into variables for easy access. [string]$c_path = $current_file_acl.Path [string]$c_owner = $current_file_acl.Owner [string]$c_inherited = $current_file_acl.Inherited $c_access = $current_file_acl.Access [string]$c_accesscontroltype = $c_access.AccessControlType [string]$c_filesystemrights = $c_access.AccessRights [string]$c_identityreference = $c_access.Account [string]$c_appliesto=Convert-FileSystemAppliesToString -InheritanceFlags $c_access.InheritanceFlags -PropagationFlags $c_access.PropagationFlags [boolean]$acl_found = $false foreach ($baseline_file_acl in $baseline_file_acls) { [string]$b_path = $baseline_file_acl.Path [string]$b_owner = $baseline_file_acl.Owner [string]$b_inherited = $baseline_file_acl.Inherited $b_access = $baseline_file_acl.Access [string]$b_accesscontroltype = $b_access.AccessControlType [string]$b_filesystemrights = $b_access.AccessRights [string]$b_identityreference = $b_access.Account [string]$b_appliesto=Convert-FileSystemAppliesToString -InheritanceFlags $b_access.InheritanceFlags -PropagationFlags $b_access.PropagationFlags if ($c_path -eq $b_path) { # Perform an owner check on each file/folder only once # If we've already checked this path, don't bother # checking the owner again. if ($last_path -ne $c_path) { if ($c_owner -ne $b_owner) { # The Permission Owner are different $DiffMessage = "$([ACLReportTools.PermissionTypeEnum]$current_file_acl.Type) $c_path owner changed from '$b_owner' to '$c_owner'." Write-Verbose -Message $DiffMessage $Comparison += New-PermissionDiffObject ` -Type ($current_file_acl.Type) ` -Path $c_path ` -DiffType ([ACLReportTools.PermissionDiffEnum]::'Owner Changed') ` -ComputerName $Current_Computer -Share $Current_Share ` -Difference $DiffMessage $changes = $true } # If if ($c_inherited -ne $b_inherited) { # The inheritance is different $DiffMessage = "$([ACLReportTools.PermissionTypeEnum]$current_file_acl.Type) $c_path inheritance changed from '$b_inheritance' to '$c_inheritance'." Write-Verbose -Message $DiffMessage $Comparison += New-PermissionDiffObject ` -Type ($current_file_acl.Type) ` -Path $c_path ` -DiffType ([ACLReportTools.PermissionDiffEnum]::'Inheritance Changed') ` -ComputerName $Current_Computer -Share $Current_Share ` -Difference $DiffMessage $changes = $true } $last_path = $c_path } # If # Check that the Identity Reference (user) is the same # one and that the Applies To is the same if (($c_identityreference -eq $b_identityreference) ` -and ($c_appliesto -eq $b_appliesto)) { $acl_found = $true break } } # If } # Foreach if ($acl_found) { # The IdentityReference (user) and path exists in both # the Baseline and the Current ACLs # Check it's the same though if ($c_filesystemrights -ne $b_filesystemrights) { # The Permission rights are different $DiffMessage = "$([ACLReportTools.PermissionTypeEnum]$current_file_acl.Type) $c_path permission rights changed from '$b_filesystemrights' to '$c_filesystemrights' for '$c_identityreference'." Write-Verbose -Message $DiffMessage $Comparison += New-PermissionDiffObject ` -Type ($current_file_acl.Type) ` -Path $c_path ` -DiffType ([ACLReportTools.PermissionDiffEnum]::'Permission Rights Changed') ` -ComputerName $Current_Computer -Share $Current_Share ` -Difference $DiffMessage $changes = $true } # If if ($c_accesscontroltype -ne $b_accesscontroltype) { # The Permission access control type is different $DiffMessage = "$([ACLReportTools.PermissionTypeEnum]$current_file_acl.Type) $c_path permission access control type changed from '$b_accesscontroltype' to '$c_accesscontroltype' for '$c_identityreference'." Write-Verbose -Message $DiffMessage $Comparison += New-PermissionDiffObject ` -Type ($current_file_acl.Type) ` -Path $c_path ` -DiffType ([ACLReportTools.PermissionDiffEnum]::'Permission Access Control Changed') ` -ComputerName $Current_Computer -Share $Current_Share ` -Difference $DiffMessage $changes = $true } # If } else { # The Permission was not found in the baseline so it must have # been added $DiffMessage = "$([ACLReportTools.PermissionTypeEnum]$current_file_acl.Type) $c_path permission '$c_filesystemrights, $c_accesscontroltype, $c_appliesto' added for '$c_identityreference'." Write-Verbose -Message $DiffMessage $Comparison += New-PermissionDiffObject ` -Type ($current_file_acl.Type) ` -Path $c_path ` -DiffType ([ACLReportTools.PermissionDiffEnum]::'Permission Added') ` -ComputerName $Current_Computer -Share $Current_Share ` -Difference $DiffMessage $changes = $true } # If } # Foreach # Now compare the baseline file ACLs wth the current file ACLs # We only need to check if a ACL has been removed from the baseline foreach ($baseline_file_acl in $baseline_file_acls) { [string]$b_path = $baseline_file_acl.Path [string]$b_owner = $baseline_file_acl.Owner $b_access = $baseline_file_acl.Access [string]$b_accesscontroltype = $b_access.AccessControlType [string]$b_filesystemrights = $b_access.AccessRights [string]$b_identityreference = $b_access.Account [string]$b_appliesto = Convert-FileSystemAppliesToString ` -InheritanceFlags $b_access.InheritanceFlags ` -PropagationFlags $b_access.PropagationFlags [boolean]$acl_found = $false foreach ($current_file_acl in $current_file_acls) { [string]$c_path = $current_file_acl.Path [string]$c_owner = $current_file_acl.Owner $c_access = $current_file_acl.Access [string]$c_accesscontroltype = $c_access.AccessControlType [string]$c_filesystemrights = $c_access.AccessRights [string]$c_identityreference = $c_access.Account [string]$c_appliesto = Convert-FileSystemAppliesToString ` -InheritanceFlags $c_access.InheritanceFlags ` -PropagationFlags $c_access.PropagationFlags if (($c_path -eq $b_path) -and ($c_identityreference -eq $b_identityreference) -and ($c_appliesto -eq $b_appliesto)) { $acl_found = $true break } # If } # Foreach if (-not $acl_found) { # The IdentityReference (user) and path exists in the # Baseline but not in the Current $DiffMessage = "$([ACLReportTools.PermissionTypeEnum]$baseline_file_acl.Type) $b_path permission '$b_filesystemrights, $b_accesscontroltype, $b_appliesto' removed for '$b_identityreference'." Write-Verbose -Message $DiffMessage $Comparison += New-PermissionDiffObject ` -Type ($baseline_file_acl.Type) ` -Path $b_path ` -DiffType ([ACLReportTools.PermissionDiffEnum]::'Permission Removed') ` -ComputerName $Current_Computer -Share $Current_Share ` -Difference $DiffMessage $changes = $true } # If } # Foreach # If no changes have been made to any of the Share or File/Folder # ACLs then say so if (-not $changes) { $DiffMessage = "The share, file and folder permissions for the share $Current_Share on $Current_Computer have not changed." Write-Verbose -Message $DiffMessage if ($ReportNoChange) { $Comparison += New-PermissionDiffObject ` -Type ([ACLReportTools.PermissionTypeEnum]::Share) ` -DiffType ([ACLReportTools.PermissionDiffEnum]::'No Change') ` -ComputerName $Current_Computer -Share $Current_Share ` -Difference $DiffMessage } # If ($ReportNoChange) } # If } else { # The Share exists in the Current but not in the Baseline $DiffMessage = "The share $Current_Share on computer $Current_Computer has been added." Write-Verbose -Message $DiffMessage $Comparison += New-PermissionDiffObject ` -Type ([ACLReportTools.PermissionTypeEnum]::Share) ` -DiffType ([ACLReportTools.PermissionDiffEnum]::'Share Added') ` -ComputerName $Current_Computer -Share $Current_Share ` -Difference $DiffMessage # Get the Current File/Folder ACLs to an Array $Filter = [ScriptBlock]::Create({ ($_.ComputerName -eq $Current_Computer) ` -and ($_.Share -eq $Current_Share) ` -and (($_.Type -eq [ACLReportTools.PermissionTypeEnum]::File) ` -or ($_.Type -eq [ACLReportTools.PermissionTypeEnum]::Folder)) }) $Current_file_Acls = $With | Where-Object -FilterScript $Filter # Output all the current share ACLs into the report as the share is new all permissions must also be new foreach ($current_file_acl in $current_file_acls) { [string]$c_path = $current_file_acl.Path [string]$c_owner = $current_file_acl.Owner $c_access = $current_file_acl.Access [string]$c_accesscontroltype = $c_access.AccessControlType [string]$c_filesystemrights = $c_access.AccessRights [string]$c_identityreference = $c_access.Account # Because this is a new share, the permission has always been added $DiffMessage = "$($current_file_acl.Type) $c_path permission '$c_filesystemrights, $c_accesscontroltype, $c_appliesto' added for '$c_identityreference'." Write-Verbose -Message $DiffMessage $Comparison += New-PermissionDiffObject ` -Type ($current_file_acl.Type) ` -Path $c_path ` -DiffType ([ACLReportTools.PermissionDiffEnum]::'Permission Added') ` -ComputerName $Current_Computer -Share $Current_Share ` -Difference $DiffMessage } # Foreach ($current_file_acl in $current_file_acls) } # If ($baseline_shares -contains $current_share) } # Foreach ($current_share in $current_shares) # Check for any removed shares foreach ($baseline_share in $baseline_shares) { if ($current_shares -notcontains $baseline_share) { # Baseline Share does not exist in Current Shares (Share removed) $DiffMessage = "The share $baseline_share on computer $Current_Computer has been removed." Write-Verbose -Message $DiffMessage $Comparison += New-PermissionDiffObject ` -Type ([ACLReportTools.PermissionTypeEnum]::Share) ` -DiffType ([ACLReportTools.PermissionDiffEnum]::'Share Removed') ` -ComputerName $Current_Computer -Share $baseline_share ` -Difference $DiffMessage } # If ($current_shares -notcontains $baseline_share) } # Foreach ($baseline_share in $baseline_shares) } else { # The Computer exists in the Current but not in the Baseline $DiffMessage = "Skiping share comparison of computer $Current_Computer because it was not found in the baseline report." Write-Verbose -Message $DiffMessage $Comparison += New-PermissionDiffObject ` -DiffType ([ACLReportTools.PermissionDiffEnum]::'Computer Added') ` -ComputerName $Current_Computer ` -Difference $DiffMessage } # If ($baseline_Computers.Contains($Current_Computer)) } # Foreach ($Current_Computer in $current_Computers) # Check for any removed computers foreach ($baseline_computer in $baseline_computers) { if ($current_computers -notcontains $baseline_computer) { # Baseline computer does not exist in Current computer (Computer removed) $DiffMessage = "The computer $Current_Computer has been removed." Write-Verbose -Message $DiffMessage $Comparison += New-PermissionDiffObject ` -DiffType ([ACLReportTools.PermissionDiffEnum]::'Computer Removed') ` -ComputerName $Current_Computer ` -Difference $DiffMessage } # If } # Foreach ($baseline_computer in $baseline_computers) } # If # Push the comparison result objects into the pipeline $Comparison } # End } # Function Compare-ACLReports #################################################################################################### # Support CmdLets #################################################################################################### <# .SYNOPSIS Gets a list of the Shares on a specified computer(s) with specified inclusions or exclusions. .DESCRIPTION This function will pull a list of shares that are set up on the specified computer. Shares can also be included or excluded from the share list by setting the Include or Exclude properties. The Cmdlet returns an array of ACLReportTools.Share objects. .PARAMETER ComputerName This is the computer to get the shares from. If this parameter is not set it will default to the current machine. .PARAMETER Include This is a list of shares to include from the computer. If this parameter is not set it will default to including all shares. This parameter can't be set if the Exclude parameter is set. .PARAMETER Exclude This is a list of shares to exclude from the computer. If this parameter is not set it will default to excluding no shares. This parameter can't be set if the Include parameter is set. .EXAMPLE Get-ACLShare -ComputerName CLIENT01 Returns a list of all shares set up on the CLIENT01 machine. .EXAMPLE Get-ACLShare -ComputerName CLIENT01 -Include MyShare,OtherShare Returns a list of shares that are set up on the CLIENT01 machine that are named either MyShare or OtherShare. .EXAMPLE Get-ACLShare -ComputerName CLIENT01 -Exclude SysVol Returns a list of shares that are set up on the CLIENT01 machine that are not called SysVol. .EXAMPLE Get-ACLShare -ComputerName CLIENT01,CLIENT02 Returns a list of shares that are set up on the CLIENT01 and CLIENT02 machines. .EXAMPLE Get-ACLShare -ComputerName CLIENT01,CLIENT02 -Exclude SysVol Returns a list of shares that are set up on the CLIENT01 and CLIENT02 machines that are not called SysVol. #> Function Get-ACLShare { [CmdLetBinding()] param ( [Parameter( ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)] [String[]]$ComputerName=$env:computername, [String[]]$Include, [String[]]$Exclude ) # param begin { [ACLReportTools.Share[]]$SelectedShares = $null } # Begin process { foreach ($Computer in $ComputerName) { Write-Verbose -Message "Getting shares list on computer $Computer" [Array]$AllShares = Get-WMIObject -Class win32_share -ComputerName $Computer | Where-Object { $_.Name -notlike "*$" } | select -ExpandProperty Name foreach ($Share in $AllShares) { if ($Include.Count -gt 0) { if ($Share -in $Include) { Write-Verbose -Message "$Share on computer $Computer Included" $SelectedShares += New-ShareObject -ComputerName $Computer -ShareName $Share } else { Write-Verbose -Message "$Share on computer $Computer Not Included" } } elseif ($Exclude.Count -gt 0) { if ($Share -in $Exclude) { Write-Verbose -Message "$Share on computer $Computer Excluded" } else { Write-Verbose -Message "$Share on computer $Computer Not Excluded" $SelectedShares += New-ShareObject -ComputerName $Computer -ShareName $Share } } else { Write-Verbose -Message "$Share on computer $Computer Included" $SelectedShares += New-ShareObject -ComputerName $Computer -ShareName $Share } # If } # Foreach ($Share in $AllShares) } # Foreach ($Computer In $ComputerName) } # Process end { Return $SelectedShares } # End } # Function Get-ACLShare <# .SYNOPSIS Gets the ACLs for a specified Share. .DESCRIPTION This function will return the share ACLs for the specified share. .PARAMETER ComputerName This is the computer to get the share ACLs from. If this parameter is not set it will default to the current machine. .PARAMETER ShareName This is the share name to pull the share ACLs for. .PARAMETER Shares This is a pipeline parameter that should be used for passing in a list of shares and computers to pull ACLs for. This parameter expects an array of [ACLReportTools.Share] objects. This parameter is usually used with the Get-ACLShare CmdLet. For example: Get-ACLShare -ComputerName CLIENT01,CLIENT02 -Exclude SYSVOL | Get-ACLShareACL .EXAMPLE Get-ACLShareACL -ComputerName CLIENT01 -ShareName MyShre Returns the share ACLs for the MyShare Share on the CLIENT01 machine. #> function Get-ACLShareACL { [CmdLetBinding()] param ( [Parameter( ParameterSetName='ByParameters')] [String]$ComputerName=$env:computername, [Parameter( ParameterSetName='ByParameters')] [String]$ShareName, [Parameter( ParameterSetName='ByPipeline', ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)] [ACLReportTools.Share[]]$Shares ) # param begin { # Create an empty array to store all the Share ACLs. [ACLReportTools.Permission[]]$share_acls = $null } # Begin process { if ($PsCmdlet.ParameterSetName -eq 'ByPipeline') { $ComputerName = $_.ComputerName $ShareName = $_.Name } $objShareSec = Get-WMIObject ` -Class Win32_LogicalShareSecuritySetting ` -Filter "name='$ShareName'" ` -ComputerName $ComputerName try { $SD = $objShareSec.GetSecurityDescriptor().Descriptor foreach ($ace in $SD.DACL) { $UserName = $ace.Trustee.Name if ($ace.Trustee.Domain -ne $Null) { $UserName = "$($ace.Trustee.Domain)\$UserName" } if ($ace.Trustee.Name -eq $Null) { $UserName = $ace.Trustee.SIDString } $fs_rule = New-Object Security.AccessControl.FileSystemAccessRule($UserName, $ace.AccessMask, $ace.AceType) $type = [ACLReportTools.PermissionTypeEnum]::Share $acl_object = New-PermissionObject ` -Type $type ` -ComputerName $ComputerName ` -Share $ShareName ` -Access $fs_rule $share_acls += $acl_object } # Foreach } catch { Write-Error "Unable to obtain share ACLs for $ShareName" } # Try } # Process end { Return $share_acls } # End } # function Get-ACLShareACL <# .SYNOPSIS Gets all the file/folder ACLs definited within a specified Share. .DESCRIPTION This function will return a list of file/folder ACLs for the specified share. If the Recurse switch is used then files/folder ACLs will be scanned recursively. If the IncludeInherited switch is set then inherited file/folder permissions will also be returned, otherwise only non-inherited permissions will be returned. .PARAMETER ComputerName This is the computer to get the share ACLs from. If this parameter is not set it will default to the current machine. .PARAMETER ShareName This is the share name to pull the file/folder ACLs for. .PARAMETER Recurse Setting this switch will cause the file/folder ACLs to be pulled recursively. .PARAMETER IncludeInherited Setting this switch will cause the non inherited file/folder ACLs to be pulled recursively. .EXAMPLE Get-ACLShareFileACL -ComputerName CLIENT01 -ShareName MyShare Returns the file/folder ACLs for the root of MyShare Share on the CLIENT01 machine. .EXAMPLE Get-ACLShareFileACL -ComputerName CLIENT01 -ShareName MyShare -Recurse Returns the file/folder ACLs for all files/folders recursively inside the MyShare Share on the CLIENT01 machine. #> function Get-ACLShareFileACL { [CmdLetBinding()] param ( [Parameter( ParameterSetName='ByParameters')] [String]$ComputerName=$env:computername, [Parameter( ParameterSetName='ByParameters')] [String]$ShareName, [Parameter( ParameterSetName='ByPipeline', ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)] [ACLReportTools.Share[]]$Shares, [Switch]$Recurse, [Switch]$IncludeInherited ) # param begin { # Create an empty array to store all the non inherited file/folder ACLs. [ACLReportTools.Permission[]]$file_acls = $null } # Begin process { if ($PsCmdlet.ParameterSetName -eq 'ByPipeline') { $ComputerName = $_.ComputerName $ShareName = $_.Name } # Now generate the root file/folder ACLs $Path = "\\$ComputerName\$ShareName" [Security2.FileSystemAccessRule2[]]$root_file_acl = Get-NTFSAccess -Path $path [String]$owner = (Get-NTFSOwner -Path $path).Owner.AccountName foreach ($access in $root_file_acl) { # Write each non-inherited ACL from the root into the array of ACL's if ($access.IsInherited) { [String] $Inherited = "Inherited from $($access.InheritedFrom)" } else { [String] $Inherited = 'Not-inherited' } $file_acls += New-PermissionObject ` -ComputerName $ComputerName ` -Type ([ACLReportTools.PermissionTypeEnum]::Folder) ` -Path $Path ` -Owner $owner ` -Access $access ` -Share $ShareName ` -Inherited $Inherited Write-Verbose -Message "Get-ACLShareFileACL: Root ACL for $ShareName path $Path owner $Owner`n$(Convert-AccessToString($Access))" } # Foreach if ($Recurse) { # Generate all file/folder ACLs for subfolders and/or files containined within the share recursively $node_file_acls = Get-childitem -Path $Path -recurse | Get-NTFSAccess if (! $IncludeInherited) { # Generate any non-inferited file/folder ACLs for subfolders and/or files containined within the share recursively $node_file_acls = $node_file_acls | Where-Object -Property IsInherited -eq $False } $lastPath = '' foreach ($access in $node_file_acls) { # Write each non-inherited ACL from the file/folder into the array of ACL's $Path = $access.FullName if ($lastPath -ne $Path) { try { [Boolean]$IsFolder = ((Get-Item -Path $Path -ErrorAction 'Stop') -is [System.IO.DirectoryInfo]) } catch { Write-Warning "Get-ACLPathFileACL: Access Denied to $Path" $IsFolder = $True } if ($IsFolder) { $type = [ACLReportTools.PermissionTypeEnum]::Folder } else { $type = [ACLReportTools.PermissionTypeEnum]::File } [String]$Owner = (Get-NTFSOwner -Path $Path).Owner.AccountName $lastPath = $access.FullName } if ($IncludeInherited -and $access.IsInherited) { [String] $Inherited = "Inherited from $($access.InheritedFrom)" } else { [String] $Inherited = 'Not-inherited' } $file_acls += New-PermissionObject ` -ComputerName $ComputerName ` -Type $type ` -Path $Path ` -Owner $owner ` -Access $access ` -Share $ShareName ` -Inherited $Inherited Write-Verbose -Message "Get-ACLShareFileACL: $Inherited ACL for $ShareName path $Path owner $Owner`n$(Convert-AccessToString($Access))" } # Foreach } # If } # Process end { Return $file_acls } # End } # Function Get-ACLShareFileACL <# .SYNOPSIS Gets all the file/folder ACLs defined within a specified Path. .DESCRIPTION This function will return a list of file/folder ACLs for the specified share. If the Recurse switch is used then files/folder ACLs will be scanned recursively. If the IncludeInherited switch is set then inherited file/folder permissions will also be returned, otherwise only non-inherited permissions will be returned. .PARAMETER Path This is the path to pull the file/folder ACLs for. .PARAMETER Recurse Setting this switch will cause the file/folder ACLs to be pulled recursively. .PARAMETER IncludeInherited Setting this switch will cause the non inherited file/folder ACLs to be pulled recursively. .EXAMPLE Get-ACLPathFileACL -Path C:\Users Returns the file/folder ACLs for the root of C:\Users folder. .EXAMPLE Get-ACLPathFileACL -Path C:\Users -Recurse Returns the file/folder ACLs for all files/folders recursively inside the C:\Users folder. #> function Get-ACLPathFileACL { [CmdLetBinding()] param ( [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [String]$Path, [Switch]$Recurse, [Switch]$IncludeInherited ) # param # Create an empty array to store all the non inherited file/folder ACLs. [ACLReportTools.Permission[]]$file_acls = $null [String]$ComputerName = $ENV:ComputerName # Now generate the root file/folder ACLs [Security2.FileSystemAccessRule2[]]$root_file_acl = Get-NTFSAccess -Path $path [String]$owner = (Get-NTFSOwner -Path $path).Owner.AccountName foreach ($access in $root_file_acl) { # Write each non-inherited ACL from the root into the array of ACL's if ($access.IsInherited) { $Inherited = "Inherited from $($access.InheritedFrom)" } else { $Inherited = 'Not-inherited' } $file_acls += New-PermissionObject ` -ComputerName $ComputerName ` -Type ([ACLReportTools.PermissionTypeEnum]::Folder) ` -Path $Path ` -Owner $owner ` -Access $access ` -Inherited $Inherited Write-Verbose -Message "Get-ACLPathFileACL: Root ACL for $Path owner $Owner`n$(Convert-AccessToString($Access))" } # Foreach if ($Recurse) { # Generate all file/folder ACLs for subfolders and/or files containined within the share recursively $node_file_acls = Get-childitem -Path $Path -recurse | Get-NTFSAccess if (! $IncludeInherited) { # Generate any non-inferited file/folder ACLs for subfolders and/or files containined within the share recursively $node_file_acls = $node_file_acls | Where-Object -Property IsInherited -eq $False } $LastPath = '' foreach ($access in $node_file_acls) { # Write each non-inherited ACL from the file/folder into the array of ACL's $Path = $access.FullName if ($LastPath -ne $Path) { try { [Boolean]$IsFolder = ((Get-Item -Path $Path -ErrorAction 'Stop') -is [System.IO.DirectoryInfo]) } catch { Write-Warning "Get-ACLPathFileACL: Access Denied to $Path" $IsFolder = $True } if ($IsFolder) { $type = [ACLReportTools.PermissionTypeEnum]::Folder } else { $type = [ACLReportTools.PermissionTypeEnum]::File } [String] $Owner = (Get-NTFSOwner -Path $Path).Owner.AccountName $LastPath = $Path } if ($IncludeInherited -and $access.IsInherited) { $Inherited = "Inherited from $($access.InheritedFrom)" } else { $Inherited = 'Not-inherited' } $file_acls += New-PermissionObject ` -ComputerName $ComputerName ` -Type $type ` -Path $Path ` -Owner $Owner ` -Access $Access ` -Inherited $Inherited Write-Verbose -Message "Get-ACLPathFileACL: $Inherited ACL for $Path owner $Owner`n$(Convert-AccessToString($Access))" } # Foreach } # If return $file_acls } # Function Get-ACLPathFileACL <# .SYNOPSIS Export the ACL Permissions objects that are provided as a file. .DESCRIPTION This Cmdlet will save what ever ACLs (ACLReportTools.Permission) to a file. .PARAMETER Path This is the path to the ACL Permissions file output file. This parameter is required. .PARAMETER InputObject Specifies the ACL Permissions objects to export to the file. Enter a variable that contains the objects or type a command or expression that gets the objects. You can also pipe ACLReportTools.Permission objects to cmdlet. .PARAMETER Force Causes the file to be overwritten if it exists. .EXAMPLE New-ACLPathFileReport -Path e:\Shares | Export-ACLPermission -Path C:\ACLReports\server01.acl Creates a new ACL Permission report for e:\Shares and saves it to the file C:\ACLReports\server01.acl. .EXAMPLE Export-ACLPermission -Path C:\ACLReports\server01.acl -InputObject $Acls Saves the ACL Permissions in the $Acls variable to the file C:\ACLReports\server01.acl. .EXAMPLE Export-ACLPermission -Path C:\ACLReports\server01.acl -InputObject (Get-ACLShare -ComputerName SERVER01 | Get-ACLShareFileACL -Recurse) Saves the file ACLs for all shares on the compuer SERVER01 to the file C:\ACLReports\server01.acl. #> function Export-ACLPermission { [CmdLetBinding()] param ( [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [String]$Path, [Parameter(Mandatory=$true, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)] [ValidateScript({ $_.GetType().FullName -ne 'ACLReportTools.Permission[]' })] [ACLReportTools.Permission[]] $InputObject, [Switch]$Force ) # param begin { if ((Test-Path -Path $Path -PathType Leaf) -and ($force -eq $false)) { Write-Error "The file $Path already exists. Use Force to overwrite it." return } [array]$Output = $null } # Begin process { foreach ($Permission in $InputObject) { $Output += $Permission } # Foreach } # Process end { try { $Output | Export-Clixml -Path $Path -Force } catch { Write-Error "Unable to export the ACL Permissions file $Path." } } # End } # Function Export-ACLPermission <# .SYNOPSIS Export the ACL Difference Objects that are provided as a file. .DESCRIPTION This Cmdlet will export an array of provided Permission Difference [ACLReportTools.PermissionDiff] records to a file. .PARAMETER Path This is the path to the ACL Permission Diff file. This parameter is required. .PARAMETER InputObject Specifies the Permissions objects to export to th file. Enter a variable that contains the objects or type a command or expression that gets the objects. You can also pipe ACLReportTools.PermissionDiff objects to this cmdlet. .PARAMETER Force Causes the file to be overwritten if it exists. .EXAMPLE Export-ACLPermissionDiff -Path C:\ACLReports\server01.acr -InputObject $DiffReport Saves the ACL Difference objects in the $DiffReport variable to the file C:\ACLReports\server01.acr. If the file exists it will be overwritten if the Force switch is set. #> function Export-ACLPermissionDiff { [CmdLetBinding()] param ( [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [String]$Path, [Parameter(Mandatory=$true, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)] [ValidateScript({$_.GetType().FullName -ne 'ACLReportTools.PermissionDiff[]'})] [ACLReportTools.PermissionDiff[]]$InputObject, [Switch]$Force ) # param begin { if ((Test-Path -Path $Path -PathType Leaf) -and ($force -eq $false)) { Write-Error "The file $Path already exists. Use Force to overwrite it." return } [array]$Output = $null } # Begin process { foreach ($PermissionDiff in $InputObject) { $Output += $PermissionDiff } # Foreach } # Process end { try { $Output | Export-Clixml -Path $Path -Force } catch { Write-Error "Unable to export the ACL Permission Diff $Path." } } # End } # Function Export-ACLPermissionDiff <# .SYNOPSIS Import the a File containing serialized ACL Permission objects that are in a file back into the pipeline. .DESCRIPTION This Cmdlet will load all the ACLs (ACLReportTools.Permission) records from a specified file. .PARAMETER Path This is the path to the file containing ACL Permission objects. This parameter is required. .EXAMPLE Import-ACLPermission -Path C:\ACLReports\server01.acl Loads the ACLs in the file C:\ACLReports\server01.acl. #> function Import-ACLPermission { [CmdLetBinding()] # [OutputType([ACLReportTools.Permission])] param ( [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [String]$Path ) # param if ((Test-Path -Path $Path -PathType Leaf) -eq $false) { Write-Error "The file $Path does not exist." return } try { Import-Clixml -Path $Path } catch { Write-Error "Unable to import the ACL file $Path." } # Try } # Function Import-ACLPermission <# .SYNOPSIS Import the a File containing serialized ACL Permission Diff objects that are in a file back into the pipeline. .DESCRIPTION This Cmdlet will load all the ACLs (ACLReportTools.PermissionDiff) records from a specified file. .PARAMETER Path This is the path to the file containing ACL Permission Diff objects. This parameter is required. .EXAMPLE Import-ACLPermissionDiff -Path C:\ACLReports\server01.acr Loads the ACL Permission Diff objects in the file C:\ACLReports\server01.acr. #> function Import-ACLPermissionDiff { [CmdLetBinding()] # [OutputType([ACLReportTools.PermissionDiff])] param ( [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [String]$Path ) # param if ((Test-Path -Path $Path -PathType Leaf) -eq $false) { Write-Error "The file $Path does not exist." return } try { Import-Clixml -Path $Path } catch { Write-Error "Unable to import the ACL file $Path." } # Try } # Function Import-ACLPermissionDiff <# .SYNOPSIS Export the ACL Difference Objects that are provided as an HTML file. .DESCRIPTION This Cmdlet will export an array of provided Permission Difference [ACLReportTools.PermissionDiff] records to an HTML file for easy viewing and reporting. .PARAMETER Path This is the path to the HTML output file. This parameter is required. .PARAMETER InputObject Specifies the Permissions DIff objects to export to the as HTML. Enter a variable that contains the objects or type a command or expression that gets the objects. You can also pipe ACLReportTools.PermissionDiff objects to this cmdlet. .PARAMETER Force Causes the file to be overwritten if it exists. .PARAMETER Title Optional Title text to write into the report. .EXAMPLE Compare-ACLReports -Baseline (Import-ACLReports -Path c:\ACLReports\server01.acl) -With (Get-ACLReport -ComputerName Server01) | Export-ACLPermissionDiffHTML -Path C:\ACLReports\server01.htm Performs a comparison using the Baseline file c:\ACLReports\Server01.acl and the shares on Server01 and outputs ACL Difference Report as an HTML file. #> function Export-ACLPermissionDiffHTML { [CmdLetBinding()] param ( [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [String]$Path, [Parameter(Mandatory=$true, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)] [ValidateScript({$_.GetType().FullName -ne 'ACLReportTools.PermissionDiff[]'})] [ACLReportTools.PermissionDiff[]]$InputObject, [Switch]$Force, [String]$Title = 'ACL Difference Report' ) # param begin { if ((Test-Path -Path $Path -PathType Leaf) -and ($force -eq $false)) { Write-Error "The file $Path already exists. Use Force to overwrite it." return } Set-Content -Path $Path -Value ( CreateHTMLReportHeader -Title $Title ) -Force [String]$LastComputer = '' [String]$LastShare = '' } # Begin process { foreach ($PermissionDiff in $InputObject) { if (($ComputerName -ne '') -and ($PermissionDiff.ComputerName -ne $LastComputer)) { $LastComputer = $PermissionDiff.ComputerName Add-Content -Path $Path -Value ( CreateHTMLComputerNameLine -ComputerName $PermissionDiff.ComputerName ) -Force } if (($PermissionDiff.Share -ne '') -and ($PermissionDiff.Share -ne $LastShare )) { $LastShare = $PermissionDiff.Share Add-Content -Path $Path -Value ( CreateHTMLShareNameLine -ShareName $PermissionDiff.Share ) -Force } Add-Content -Path $Path -Value ( CreateHTMLPermissionDiffLine -PermissionDiff $PermissionDiff ) -Force } # Foreach } # Process end { Add-Content -Path $Path -Value ( CreateHTMLReportFooter ) -Force } # End } # Function Export-ACLPermissionDiffHTML #################################################################################################### # Hidden Support CmdLets #################################################################################################### <# .SYNOPSIS This function creates the a support module containing classes and enums via reflection. It also checks for and loads the File System Security PowerShell Module Module (https://gallery.technet.microsoft.com/scriptcenter/1abd77a5-9c0b-4a2b-acef-90dbb2b84e85) .DESCRIPTION This function creates a .net dynamic module via reflection and adds classes and enums to it that are then used by other functions in this module. #> function Initialize-Module { [CmdLetBinding()] param ( [String]$ModuleName = 'ACLReportTools' ) # Param # Do we need to install the NTFSSecurity Module? $SupportInstall = (@(Get-Command -Name Install-Module -ErrorAction SilentlyContinue).Count -gt 0) $NTFSSecurityModules = @(Get-Module -Name NTFSSecurity -ListAvailable) if ( @($NTFSSecurityModules | Where-Object -Property Version -gt 4.0.0).Count -eq 0) { try { Write-Verbose -Message 'NTFSSecrity Module needs to be installed.' Get-PackageProvider -Name NuGet -ForceBootstrap -Force Install-Module -Name NTFSSecurity -MinimumVersion 4.0.0 -Force Write-Verbose -Message 'NTFSSecrity Module was installed.' } catch { Throw 'NTFSSecurity Module v4.0.0 or greater is not available and could not be installed automatically. Please download it from https://gallery.technet.microsoft.com/scriptcenter/1abd77a5-9c0b-4a2b-acef-90dbb2b84e85' } } # If Import-Module -Name NTFSSecurity -MinimumVersion 4.0.0 $Domain = [AppDomain]::CurrentDomain if (($Domain.GetAssemblies() | Where-Object -FilterScript { $_.FullName -eq "$ModuleName, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" } | Measure-Object).Count-eq 0) { # Define the module $DynAssembly = New-Object Reflection.AssemblyName($ModuleName) $AssemblyBuilder = $Domain.DefineDynamicAssembly($DynAssembly, 'Run') $ModuleBuilder = $AssemblyBuilder.DefineDynamicModule($ModuleName, $False) # Define Permission Difference Enumeration $EnumBuilder = $ModuleBuilder.DefineEnum('ACLReportTools.PermissionTypeEnum', 'Public', [Int]) # Define values of the enum $EnumBuilder.DefineLiteral('Not Applicable', [Int]0) $EnumBuilder.DefineLiteral('Share', [Int]1) $EnumBuilder.DefineLiteral('Folder', [Int]2) $EnumBuilder.DefineLiteral('File', [Int]3) $PermissionTypeEnumType = $EnumBuilder.CreateType() # Define the ACLReportTools.Permission Class $Attributes = 'AutoLayout, AnsiClass, Class, Public' $TypeBuilder = $ModuleBuilder.DefineType('ACLReportTools.Permission',$Attributes,[System.Object]) $TypeBuilder.DefineField('ComputerName', [string], 'Public') | Out-Null $TypeBuilder.DefineField('Type', $PermissionTypeEnumType, 'Public') | Out-Null $TypeBuilder.DefineField('Share', [string], 'Public') | Out-Null $TypeBuilder.DefineField('Path', [string], 'Public') | Out-Null $TypeBuilder.DefineField('Owner', [string], 'Public') | Out-Null $TypeBuilder.DefineField('Access', [Security2.FileSystemAccessRule2], 'Public') | Out-Null $TypeBuilder.DefineField('Inherited', [string], 'Public') | Out-Null $TypeBuilder.CreateType() | Out-Null # Define the ACLReportTools.Share Class $Attributes = 'AutoLayout, AnsiClass, Class, Public' $TypeBuilder = $ModuleBuilder.DefineType('ACLReportTools.Share',$Attributes,[System.Object]) $TypeBuilder.DefineField('ComputerName', [string], 'Public') | Out-Null $TypeBuilder.DefineField('Name', [string], 'Public') | Out-Null $TypeBuilder.CreateType() | Out-Null # Define Permission Difference Enumeration $EnumBuilder = $ModuleBuilder.DefineEnum('ACLReportTools.PermissionDiffEnum', 'Public', [Int]) # Define values of the enum $EnumBuilder.DefineLiteral('No Change', [Int]0) $EnumBuilder.DefineLiteral('Computer Added', [Int]1) $EnumBuilder.DefineLiteral('Computer Removed', [Int]2) $EnumBuilder.DefineLiteral('Share Removed', [Int]3) $EnumBuilder.DefineLiteral('Share Added', [Int]4) $EnumBuilder.DefineLiteral('Permission Removed', [Int]5) $EnumBuilder.DefineLiteral('Permission Added', [Int]6) $EnumBuilder.DefineLiteral('Permission Rights Changed', [Int]7) $EnumBuilder.DefineLiteral('Permission Access Control Changed', [Int]8) $EnumBuilder.DefineLiteral('Owner Changed', [Int]9) $EnumBuilder.DefineLiteral('Inheritance Changed', [Int]10) $PermissionDiffEnumType = $EnumBuilder.CreateType() # Define the ACLReportTools.PermissionDiff Class $Attributes = 'AutoLayout, AnsiClass, Class, Public' $TypeBuilder = $ModuleBuilder.DefineType('ACLReportTools.PermissionDiff',$Attributes,[System.Object]) $TypeBuilder.DefineField('ComputerName', [string], 'Public') | Out-Null $TypeBuilder.DefineField('Type', $PermissionTypeEnumType, 'Public') | Out-Null $TypeBuilder.DefineField('Share', [string], 'Public') | Out-Null $TypeBuilder.DefineField('Path', [string], 'Public') | Out-Null $TypeBuilder.DefineField('DiffType', $PermissionDiffEnumType, 'Public') | Out-Null $TypeBuilder.DefineField('Difference', [String], 'Public') | Out-Null $TypeBuilder.CreateType() | Out-Null } # If } # Function Initialize-Module <# .SYNOPSIS This function creates an ACLReportTools.Share object and populates it. .DESCRIPTION This function creates an ACLReportTools.Share object from the class definition in the dynamic module ACLREportsModule and assigns the function parameters to the field values of the object. #> function New-ShareObject { [CmdLetBinding()] param ( [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [String]$ComputerName, [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [String]$ShareName ) # Param $share_object = New-Object -TypeName 'ACLReportTools.Share' $share_object.ComputerName = $ComputerName $share_object.Name = $ShareName return $share_object } # function New-ShareObject <# .SYNOPSIS This function creates an ACLReportTools.Permission object and populates it. .DESCRIPTION This function creates an ACLReportTools.Permission object from the class definition in the dynamic module ACLREportsModule and assigns the function parameters to the field values of the object. #> function New-PermissionObject { [CmdLetBinding()] param ( [Parameter(Mandatory=$true)] [ACLReportTools.PermissionTypeEnum]$Type, [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [String]$ComputerName, [String]$Path='', [String]$Share='', [String]$Owner='', [Parameter(Mandatory=$true)] [ValidateNotNull()] [Security2.FileSystemAccessRule2]$Access, [String]$Inherited='' ) # Param # Need to correct the $Access objects to ensure the FileSystemRights values correctly converted to string # When the "Generic Rights" bits are set: http://msdn.microsoft.com/en-us/library/aa374896%28v=vs.85%29.aspx $permission_object = New-Object -TypeName 'ACLReportTools.Permission' $permission_object.Type = $Type $permission_object.ComputerName = $ComputerName $permission_object.Path = $Path $permission_object.Share = $Share $permission_object.Owner = $Owner $permission_object.Access = $Access $permission_object.Inherited = $Inherited return $permission_object } # function New-PermissionObject <# .SYNOPSIS This function creates an ACLReportTools.PermissionDiff object and populates it. .DESCRIPTION This function creates an ACLReportTools.PermissionDiff object from the class definition in the dynamic module ACLREportsModule and assigns the function parameters to the field values of the object. #> function New-PermissionDiffObject { [CmdLetBinding()] param ( [ACLReportTools.PermissionTypeEnum]$Type=([ACLReportTools.PermissionTypeEnum]::'Not Applicable'), [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [String]$ComputerName, [String]$Path='', [String]$Share='', [ACLReportTools.PermissionDiffEnum]$DiffType=([ACLReportTools.PermissionDiffEnum]::'No Change'), [String]$Difference='' ) # Param # Need to correct the $Access objects to ensure the FileSystemRights values correctly converted to string # When the "Generic Rights" bits are set: http://msdn.microsoft.com/en-us/library/aa374896%28v=vs.85%29.aspx $permissiondiff_object = New-Object -TypeName 'ACLReportTools.PermissionDiff' $permissiondiff_object.Type = $Type $permissiondiff_object.ComputerName = $ComputerName $permissiondiff_object.Path = $Path $permissiondiff_object.Share = $Share $permissiondiff_object.DiffType = $DiffType $permissiondiff_object.Difference = $Difference return $permissiondiff_object } # function New-PermissionDiffObject <# .SYNOPSIS .DESCRIPTION #> function Convert-FileSystemAppliesToString { [CmdLetBinding()] param ( [Parameter(Mandatory=$true)] [String]$InheritanceFlags, [Parameter(Mandatory=$true)] [String]$PropagationFlags ) # Param if ($PropagationFlags -eq 'None') { switch ($InheritanceFlags) { 'None' { return 'This folder only'; break } 'ContainerInherit, ObjectInherit' { return 'This folder, subfolders and files'; break } 'ContainerInherit' { return 'This folder and subfolders'; break } 'ObjectInherit' { return 'This folder and files'; break } } # Switch } else { switch ($InheritanceFlags) { 'ContainerInherit, ObjectInherit' { return 'Subfolders and files only'; break } 'ContainerInherit' { return 'Subfolders only'; break } 'ObjectInherit' { return 'Files only'; break } } # Switch } # If return 'Unknown' } # function Convert-FileSystemAppliesToString <# .SYNOPSIS .DESCRIPTION #> function Convert-AccessToString { [CmdLetBinding()] param ( [Parameter(Mandatory=$true)] [Object]$Access ) # Param [string]$rights=$Access.AccessRights [string]$controltype=$Access.AccessControlType [string]$IdentityReference=$Access.IdentityReference [string]$IsInherited=$Access.IsInherited [string]$AppliesTo=Convert-FileSystemAppliesToString -InheritanceFlags $Access.InheritanceFlags -PropagationFlags $Access.PropagationFlags Return "AccessRights : $rights`nAccessControlType : $controltype`nIdentityReference : $IdentityReference`nIsInherited : $IsInherited`nAppliesTo : $AppliesTo`n" } # function Convert-AccessToString <# .SYNOPSIS .DESCRIPTION #> function Convert-ACEToString { [CmdLetBinding()] param ( [Parameter(Mandatory=$true)] [Object]$ACE ) # Param [string]$path=$ACE.path [string]$owner=$ACE.owner [string]$acccessstring=Convert-ACEToString($ACE.access) Return "Path : $path`nOwner : $owner`n$acccessstring" } # function Convert-ACEToString Function CreateHTMLReportHeader { param ( [Parameter(Mandatory=$true)] [String]$Title ) # Param return $Script:Html_Header -f $Title } # Function CreateHTMLReportHeader Function CreateHTMLReportFooter { return $Script:Html_Footer } # Function CreateHTMLReportFooter Function CreateHTMLComputerNameLine { param ( [Parameter(Mandatory=$true)] [String]$ComputerName ) # Param return $Script:Html_ComputerName -f $ComputerName } # Function CreateHTMLComputerNameLine Function CreateHTMLShareNameLine { param ( [Parameter(Mandatory=$true)] [String]$ShareName ) # Param return $Script:Html_ShareName -f $ShareName } # Function CreateHTMLShareNameLine Function CreateHTMLPermissionDiffLine { param ( [Parameter(Mandatory=$true)] [ACLReportTools.PermissionDiff]$PermissionDiff ) # Param # This function takes a Permission Diff object and formats it as HTML for a report. [string]$label = $PermissionDiff.Type.ToString() [string]$class = $PermissionDiff.DiffType.ToString().ToLower() -replace ' ','' [string]$html = $PermissionDiff.Difference return $Script:Html_DifferenceLine -f $Label,$Class,$Html } # Function CreateHTMLPermissionDiffLine # Ensure all the custom classes are loaded in available Initialize-Module # Export the Module Cmdlets Export-ModuleMember -Function ` New-ACLShareReport,` New-ACLPathFileReport, ` Import-ACLReport,` Export-ACLReport,` Import-ACLDiffReport,` Export-ACLDiffReport,` Compare-ACLReports,` Get-ACLShare,` Get-ACLShareACL,` Get-ACLPathFileACL,` Get-ACLShareFileACL,` Import-ACLPermission,` Export-ACLPermission,` Import-ACLPermissionDiff,` Export-ACLPermissionDiff,` Export-ACLPermissionDiffHTML |