From d488787c3db58350bc3bd0a8d335fabd268328de Mon Sep 17 00:00:00 2001 From: Mikael Olofsson Date: Sun, 24 Sep 2023 14:09:57 +0000 Subject: [PATCH 1/2] Add module to modify ACL's on Active Directory objects --- plugins/modules/acl.ps1 | 130 ++++++++++++++++++++++++++++++++++++++++ plugins/modules/acl.py | 57 ++++++++++++++++++ 2 files changed, 187 insertions(+) create mode 100644 plugins/modules/acl.ps1 create mode 100644 plugins/modules/acl.py diff --git a/plugins/modules/acl.ps1 b/plugins/modules/acl.ps1 new file mode 100644 index 0000000..0307776 --- /dev/null +++ b/plugins/modules/acl.ps1 @@ -0,0 +1,130 @@ +#!powershell + +# Copyright: (c) 2023, Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +#AnsibleRequires -CSharpUtil Ansible.Basic +#Requires -Module Ansible.ModuleUtils.Legacy +#Requires -Module Ansible.ModuleUtils.SID + +$spec = @{ + options = @{ + object = @{ type = "str"; required = $true; aliases = "path" } + principal = @{ type = "str"; required = $true; aliases = "user" } + rights = @{ type = "str"; required = $true } + rights_attr = @{ type = "str" } + type = @{ type = "str"; required = $true; choices = "allow", "deny" } + inherit = @{ type = "str"; default = "None" } + state = @{ type = "str"; default = "present"; choices = "absent", "present" } + } +} + +$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec) +$module.Result.changed = $false + +Try { + Import-Module ActiveDirectory +} +Catch { + $module.FailJson("Error importing module ActiveDirectory") +} + +$object = $module.Params.object +$principal = $module.Params.principal +$state = $module.Params.state +$type = $module.Params.type +$rights = $module.Params.rights +$rights_attr = $module.Params.rights_attr +$inherit = $module.Params.inherit + +$user_sid = Convert-ToSID -account_name $principal + +$guidmap = @{} +Get-ADObject -SearchBase ((Get-ADRootDSE).SchemaNamingContext) -LDAPFilter "(schemaidguid=*)" -Properties lDAPDisplayName, schemaIDGUID | + ForEach-Object { $guidmap[$_.lDAPDisplayName] = [System.GUID]$_.schemaIDGUID } + +if ($rights_attr) { + if ($guidmap.Contains($rights_attr)) { + $objGUID = $guidmap[$rights_attr] + } + Else { + $module.FailJson("LDAP attribute $rights_attr does not exist") + } +} +Else { + $objGUID = [guid]::empty +} + +Try { + $objRights = [System.DirectoryServices.ActiveDirectoryRights]$rights + $InheritanceFlag = [System.DirectoryServices.ActiveDirectorySecurityInheritance]$inherit + + If ($type -eq "allow") { + $objType = [System.Security.AccessControl.AccessControlType]::Allow + } + Else { + $objType = [System.Security.AccessControl.AccessControlType]::Deny + } + + $objUser = New-Object System.Security.Principal.SecurityIdentifier($user_sid) + $objACE = New-Object System.DirectoryServices.ActiveDirectoryAccessRule($objUser, $objRights, $objType, $objGUID, $InheritanceFlag, [guid]::empty) + $objACL = Get-ACL -Path "AD:\$($object)" + + $match = $false + ForEach ($rule in $objACL.GetAccessRules($true, $true, [System.Security.Principal.SecurityIdentifier])) { + If ( + ($rule.ActiveDirectoryRights -eq $objACE.ActiveDirectoryRights) -And + ($rule.InheritanceType -eq $objACE.InheritanceType) -And + ($rule.ObjectType -eq $objACE.ObjectType) -And + ($rule.InheritedObjectType -eq $objACE.InheritedObjectType) -And + ($rule.ObjectFlags -eq $objACE.ObjectFlags) -And + ($rule.AccessControlType -eq $objACE.AccessControlType) -And + ($rule.IdentityReference -eq $objACE.IdentityReference) -And + ($rule.IsInherited -eq $objACE.IsInherited) -And + ($rule.InheritanceFlags -eq $objACE.InheritanceFlags) -And + ($rule.PropagationFlags -eq $objACE.PropagationFlags) + ) { + $match = $true + Break + } + } + + If ($state -eq "present" -And $match -eq $false) { + Try { + $objACL.AddAccessRule($objACE) + Set-ACL -Path "AD:\$($object)" -AclObject $objACL + $module.Result.changed = $true + } + Catch { + $module.FailJson("an exception occurred when adding the specified rule - $($_.Exception.Message)") + } + } + ElseIf ($state -eq "absent" -And $match -eq $true) { + Try { + $objACL.RemoveAccessRule($objACE) + Set-ACL -Path "AD:\$($object)" -AclObject $objACL + $module.Result.changed = $true + } + Catch { + $module.FailJson("an exception occurred when removing the specified rule - $($_.Exception.Message)") + } + } + Else { + # A rule was attempting to be added but already exists + If ($match -eq $true) { + $module.Result.msg = "the specified rule already exists" + $module.ExitJson() + } + # A rule didn't exist that was trying to be removed + Else { + $module.Result.msg = "the specified rule does not exist" + $module.ExitJson() + } + } + +} +Catch { + $module.FailJson("an error occurred when attempting to $type $rights permission(s) on $object for $principal - $($_.Exception.Message)") +} + +$module.ExitJson() diff --git a/plugins/modules/acl.py b/plugins/modules/acl.py new file mode 100644 index 0000000..3c39de2 --- /dev/null +++ b/plugins/modules/acl.py @@ -0,0 +1,57 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2023, Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +DOCUMENTATION = r''' +--- +module: acl +short_description: Used to set ACL's on objects in an Active Directory. +description: + - Used to set ACL's on objects in an Active Directory. +options: + object: + description: The Distinguished Name of object to modify. + type: str + required: yes + aliases: [ path ] + principal: + description: User or Group to add specified rights on the object. + type: str + required: yes + aliases: [ user ] + rights: + description: The rights/permissions that are to be allowed/denied for the object. + type: str + required: yes + rights_attr: + description: The attribute that the rights are to be allowd/denied for. + type: str + type: + description: Specify whether to allow or deny the rights specified. + type: str + choices: [ allow, deny ] + required: yes + inherit: + description: Inherit flags on the ACL rules. + type: str + default: None + state: + description: Specify whether to add C(present) or remove C(absent) the specified access rule. + type: str + choices: [ absent, present ] + default: present +author: + - Mikael Olofsson (@quiphius) +''' + +EXAMPLES = r''' +- name: Set the C(Manager can update membership list) in the C(Managed By) tab + win_domain_acl: + object: "CN=System Administrators,OU=MyDomain,DC=domain,DC=test" + principal: System Administrators + rights: WriteProperty + rights_attr: member + type: allow +''' From ad53bfe61e0e171552e87a8b7133e8796b972ef1 Mon Sep 17 00:00:00 2001 From: Mikael Olofsson Date: Thu, 12 Oct 2023 17:20:06 +0000 Subject: [PATCH 2/2] Some minor updates ti acl module --- plugins/modules/acl.ps1 | 24 +++++++++++++++++++----- plugins/modules/acl.py | 41 +++++++++++++++++++++++++++++++++++------ 2 files changed, 54 insertions(+), 11 deletions(-) diff --git a/plugins/modules/acl.ps1 b/plugins/modules/acl.ps1 index 0307776..37934d2 100644 --- a/plugins/modules/acl.ps1 +++ b/plugins/modules/acl.ps1 @@ -12,9 +12,10 @@ $spec = @{ object = @{ type = "str"; required = $true; aliases = "path" } principal = @{ type = "str"; required = $true; aliases = "user" } rights = @{ type = "str"; required = $true } - rights_attr = @{ type = "str" } + object_type = @{ type = "str"; aliases = "rights_attr" } type = @{ type = "str"; required = $true; choices = "allow", "deny" } inherit = @{ type = "str"; default = "None" } + inherited_object_type = @{ type = "str" } state = @{ type = "str"; default = "present"; choices = "absent", "present" } } } @@ -34,8 +35,9 @@ $principal = $module.Params.principal $state = $module.Params.state $type = $module.Params.type $rights = $module.Params.rights -$rights_attr = $module.Params.rights_attr +$object_type = $module.Params.object_type $inherit = $module.Params.inherit +$inherited_object_type = $module.Params.inherited_object_type $user_sid = Convert-ToSID -account_name $principal @@ -44,8 +46,8 @@ Get-ADObject -SearchBase ((Get-ADRootDSE).SchemaNamingContext) -LDAPFilter "(sch ForEach-Object { $guidmap[$_.lDAPDisplayName] = [System.GUID]$_.schemaIDGUID } if ($rights_attr) { - if ($guidmap.Contains($rights_attr)) { - $objGUID = $guidmap[$rights_attr] + if ($guidmap.Contains($object_type)) { + $objGUID = $guidmap[$object_type] } Else { $module.FailJson("LDAP attribute $rights_attr does not exist") @@ -55,6 +57,18 @@ Else { $objGUID = [guid]::empty } +if ($inherited_object_type) { + if ($guidmap.Contains($inherited_object_type)) { + $inheritGUID = $guidmap[$inherited_object_type] + } + Else { + $module.FailJson("LDAP attribute $inherited_object_type does not exist") + } +} +Else { + $inheritGUID = [guid]::empty +} + Try { $objRights = [System.DirectoryServices.ActiveDirectoryRights]$rights $InheritanceFlag = [System.DirectoryServices.ActiveDirectorySecurityInheritance]$inherit @@ -67,7 +81,7 @@ Try { } $objUser = New-Object System.Security.Principal.SecurityIdentifier($user_sid) - $objACE = New-Object System.DirectoryServices.ActiveDirectoryAccessRule($objUser, $objRights, $objType, $objGUID, $InheritanceFlag, [guid]::empty) + $objACE = New-Object System.DirectoryServices.ActiveDirectoryAccessRule($objUser, $objRights, $objType, $objGUID, $InheritanceFlag, $inheritGUID) $objACL = Get-ACL -Path "AD:\$($object)" $match = $false diff --git a/plugins/modules/acl.py b/plugins/modules/acl.py index 3c39de2..f9d91d7 100644 --- a/plugins/modules/acl.py +++ b/plugins/modules/acl.py @@ -22,21 +22,33 @@ required: yes aliases: [ user ] rights: - description: The rights/permissions that are to be allowed/denied for the object. + description: + - The rights/permissions that are to be allowed/denied for the object. + - The rights can be any right under Microsoft Learn ActiveDirectoryRights + U(https://learn.microsoft.com/en-us/dotnet/api/system.directoryservices.activedirectoryrights). type: str required: yes - rights_attr: - description: The attribute that the rights are to be allowd/denied for. + object_type: + description: + - The attribute or object type that the rights are to be allowd/denied for. + - This can be any LDAP attribute or object type. type: str + aliases: [ rights_attr ] type: description: Specify whether to allow or deny the rights specified. type: str choices: [ allow, deny ] required: yes inherit: - description: Inherit flags on the ACL rules. + description: + - Inherit flags on the ACL rules. + - For more information on the choices see Microsoft Learn ActiveDirectorySecurityInheritance + U(https://learn.microsoft.com/en-us/dotnet/api/system.directoryservices.activedirectorysecurityinheritance). type: str default: None + inherited_object_type: + description: The inherited attribute or object type the access rule applies on + type: str state: description: Specify whether to add C(present) or remove C(absent) the specified access rule. type: str @@ -47,9 +59,26 @@ ''' EXAMPLES = r''' +- name: Let System Adminstrators create/delete users in the MyAdmins OU + microsoft.ad.acl: + path: "OU=MyAdmins,DC=domain,DC=test" + user: System Administrators + rights: CreateChild,DeleteChild + rights_attr: user + type: allow + +- name: Let System Adminstrators manage users in the MyAdmins OU + microsoft.ad.acl: + path: "CN=System Administrators,OU=MyAdmins,DC=domain,DC=test" + user: System Administrators + rights: GenericAll + inherited_object_type: user + inherit: Children + type: allow + - name: Set the C(Manager can update membership list) in the C(Managed By) tab - win_domain_acl: - object: "CN=System Administrators,OU=MyDomain,DC=domain,DC=test" + microsoft.ad.acl: + object: "CN=System Administrators,OU=MyAdmins,DC=domain,DC=test" principal: System Administrators rights: WriteProperty rights_attr: member