#!powershell # Copyright: (c) 2017, Noah Sparks # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) #Requires -Module Ansible.ModuleUtils.Legacy #Requires -Module Ansible.ModuleUtils.SID $params = Parse-Args -arguments $args -supports_check_mode $true $check_mode = Get-AnsibleParam -obj $params -name "_ansible_check_mode" -type "bool" -default $false # module parameters $path = Get-AnsibleParam -obj $params -name "path" -type "path" -failifempty $true -aliases "destination","dest" $user = Get-AnsibleParam -obj $params -name "user" -type "str" -failifempty $true $rights = Get-AnsibleParam -obj $params -name "rights" -type "list" $inheritance_flags = Get-AnsibleParam -obj $params -name "inheritance_flags" -type "list" -default 'ContainerInherit','ObjectInherit' $propagation_flags = Get-AnsibleParam -obj $params -name "propagation_flags" -type "str" -default "none" -ValidateSet 'InheritOnly','None','NoPropagateInherit' $audit_flags = Get-AnsibleParam -obj $params -name "audit_flags" -type "list" -default 'success' $state = Get-AnsibleParam -obj $params -name "state" -type "str" -default "present" -validateset 'present','absent' #Make sure target path is valid If (-not (Test-Path -Path $path) ) { Fail-Json -obj $result -message "defined path ($path) is not found/invalid" } #function get current audit rules and convert to hashtable Function Get-CurrentAuditRules ($path) { Try { $ACL = Get-Acl $path -Audit } Catch { Return "Unable to retrieve the ACL on $Path" } $HT = Foreach ($Obj in $ACL.Audit) { @{ user = $Obj.IdentityReference.ToString() rights = ($Obj | Select-Object -expand "*rights").ToString() audit_flags = $Obj.AuditFlags.ToString() is_inherited = $Obj.IsInherited.ToString() inheritance_flags = $Obj.InheritanceFlags.ToString() propagation_flags = $Obj.PropagationFlags.ToString() } } If (-Not $HT) { "No audit rules defined on $path" } Else {$HT} } $result = @{ changed = $false current_audit_rules = Get-CurrentAuditRules $path } #Make sure identity is valid and can be looked up Try { $SID = Convert-ToSid $user } Catch { Fail-Json -obj $result -message "Failed to lookup the identity ($user) - $($_.exception.message)" } #get the path type $ItemType = (Get-Item $path).GetType() switch ($ItemType) { ([Microsoft.Win32.RegistryKey]) {$registry = $true; $result.path_type = 'registry'} ([System.IO.FileInfo]) {$file = $true; $result.path_type = 'file'} ([System.IO.DirectoryInfo]) {$result.path_type = 'directory'} } #Get current acl/audit rules on the target Try { $ACL = Get-Acl $path -Audit } Catch { Fail-Json -obj $result -message "Unable to retrieve the ACL on $Path - $($_.Exception.Message)" } #configure acl object to remove the specified user If ($state -eq 'absent') { #Try and find an identity on the object that matches user #We skip inherited items since we can't remove those $ToRemove = ($ACL.Audit | Where-Object {$_.IdentityReference.Translate([System.Security.Principal.SecurityIdentifier]) -eq $SID -and $_.IsInherited -eq $false}).IdentityReference #Exit with changed false if no identity is found If (-Not $ToRemove) { $result.current_audit_rules = Get-CurrentAuditRules $path Exit-Json -obj $result } #update the ACL object if identity found Try { $ToRemove | ForEach-Object { $ACL.PurgeAuditRules($_) } } Catch { $result.current_audit_rules = Get-CurrentAuditRules $path Fail-Json -obj $result -message "Failed to remove audit rule: $($_.Exception.Message)" } } Else { If ($registry) { $PossibleRights = [System.Enum]::GetNames([System.Security.AccessControl.RegistryRights]) Foreach ($right in $rights) { if ($right -notin $PossibleRights) { Fail-Json -obj $result -message "$right does not seem to be a valid REGISTRY right" } } $NewAccessRule = New-Object System.Security.AccessControl.RegistryAuditRule($user,$rights,$inheritance_flags,$propagation_flags,$audit_flags) } Else { $PossibleRights = [System.Enum]::GetNames([System.Security.AccessControl.FileSystemRights]) Foreach ($right in $rights) { if ($right -notin $PossibleRights) { Fail-Json -obj $result -message "$right does not seem to be a valid FILE SYSTEM right" } } If ($file -and $inheritance_flags -ne 'none') { Fail-Json -obj $result -message "The target type is a file. inheritance_flags must be changed to 'none'" } $NewAccessRule = New-Object System.Security.AccessControl.FileSystemAuditRule($user,$rights,$inheritance_flags,$propagation_flags,$audit_flags) } #exit here if any existing rule matches defined rule since no change is needed #if we need to ignore inherited rules in the future, this would be where to do it #Just filter out inherited rules from $ACL.Audit Foreach ($group in $ACL.Audit | Where-Object {$_.IsInherited -eq $false}) { If ( ($group | Select-Object -expand "*Rights") -eq ($NewAccessRule | Select-Object -expand "*Rights") -and $group.AuditFlags -eq $NewAccessRule.AuditFlags -and $group.IdentityReference.Translate([System.Security.Principal.SecurityIdentifier]) -eq $SID -and $group.InheritanceFlags -eq $NewAccessRule.InheritanceFlags -and $group.PropagationFlags -eq $NewAccessRule.PropagationFlags ) { $result.current_audit_rules = Get-CurrentAuditRules $path Exit-Json -obj $result } } #try and set the acl object. AddAuditRule allows for multiple entries to exist under the same #identity...so if someone wanted success: write and failure: delete for example, that setup would be #possible. The alternative is SetAuditRule which would instead modify an existing rule and not allow #for setting the above example. Try { $ACL.AddAuditRule($NewAccessRule) } Catch { Fail-Json -obj $result -message "Failed to set the audit rule: $($_.Exception.Message)" } } #finally set the permissions Try { Set-Acl -Path $path -ACLObject $ACL -WhatIf:$check_mode } Catch { $result.current_audit_rules = Get-CurrentAuditRules $path Fail-Json -obj $result -message "Failed to apply audit change: $($_.Exception.Message)" } #exit here after a change is applied $result.current_audit_rules = Get-CurrentAuditRules $path $result.changed = $true Exit-Json -obj $result