From ee2adb45a848e47c6aecc98512de99ee9c44e047 Mon Sep 17 00:00:00 2001 From: Daniel-Sanchez-Fabregas <33929811+Daniel-Sanchez-Fabregas@users.noreply.github.com> Date: Thu, 17 May 2018 05:06:01 +0200 Subject: [PATCH] Adding module win_domain_computer (#35954) * Adding module win_domain_computer This module is a wrapper of powershell *-ADComputer commands. The main use case is to add non-windows computers to Active Directory through a bridge windows computer. * Replace `Set-Attr` * Fix case insensitive comparisons * Add omitted parameters in cmdlets * Enhance module documentation * Simplify `state` as case insensitive. * Simplify try catch * Fix indentation * Make message errors more descriptive. * Specify type in boolean parameters * Keep parameter ingestion together * Delete superfluous resultobj * Workaround failing Erroraction * Add target info in error * Cosmetic changes * Fix up Fail-Json to use correct message param --- .../modules/windows/win_domain_computer.ps1 | 186 ++++++++++++++++++ .../modules/windows/win_domain_computer.py | 93 +++++++++ 2 files changed, 279 insertions(+) create mode 100644 lib/ansible/modules/windows/win_domain_computer.ps1 create mode 100644 lib/ansible/modules/windows/win_domain_computer.py diff --git a/lib/ansible/modules/windows/win_domain_computer.ps1 b/lib/ansible/modules/windows/win_domain_computer.ps1 new file mode 100644 index 0000000000..d7413bfc45 --- /dev/null +++ b/lib/ansible/modules/windows/win_domain_computer.ps1 @@ -0,0 +1,186 @@ +#!powershell +# +# Copyright: (c) 2017, AMTEGA - Xunta de Galicia +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +#Requires -Module Ansible.ModuleUtils.Legacy + + +# ------------------------------------------------------------------------------ +$ErrorActionPreference = "Stop" + +# Preparing result +$result = @{} +$result.changed = $false + +# Parameter ingestion +$params = Parse-Args $args -supports_check_mode $true + +$check_mode = Get-AnsibleParam -obj $params -name "_ansible_check_mode" -type "bool" -default $false +$diff_support = Get-AnsibleParam -obj $params -name "_ansible_diff" -type "bool" -default $false + +$name = Get-AnsibleParam -obj $params -name "name" -failifempty $true -resultobj $result +$sam_account_name = Get-AnsibleParam -obj $params -name "sam_account_name" -default "$name$" +If (-not $sam_account_name.EndsWith("$")) { + Fail-Json -obj $result -message "sam_account_name must end in $" +} +$enabled = Get-AnsibleParam -obj $params -name "enabled" -type "bool" -default $true +$description = Get-AnsibleParam -obj $params -name "description" -default "" +$state = Get-AnsibleParam -obj $params -name "state" -ValidateSet "present","absent" -default "present" +If ($state -eq "present") { + $dns_hostname = Get-AnsibleParam -obj $params -name "dns_hostname" -failifempty $true -resultobj $result + $ou = Get-AnsibleParam -obj $params -name "ou" -failifempty $true -resultobj $result + $distinguished_name = "CN=$name,$ou" + + $desired_state = @{ + name = $name + sam_account_name = $sam_account_name + dns_hostname = $dns_hostname + ou = $ou + distinguished_name = $distinguished_name + description = $description + enabled = $enabled + state = $state + } +} Else { + $desired_state = @{ + name = $name + state = $state + } +} + +# ------------------------------------------------------------------------------ +Function Get-InitialState($desired_state) { + # Test computer exists + $computer = Try { + Get-ADComputer ` + -Identity $desired_state.name ` + -Properties DistinguishedName,DNSHostName,Enabled,Name,SamAccountName,Description,ObjectClass + } Catch { $null } + If ($computer) { + $initial_state = @{ + name = $computer.Name + sam_account_name = $computer.SamAccountName + dns_hostname = $computer.DNSHostName + # Get OU from regexp that removes all characters to the first "," + ou = $computer.DistinguishedName -creplace "^[^,]*,","" + distinguished_name = $computer.DistinguishedName + description = $computer.Description + enabled = $computer.Enabled + state = "present" + } + } Else { + $initial_state = @{ + name = $desired_state.name + state = "absent" + } + } + + return $initial_state +} + +# ------------------------------------------------------------------------------ +Function Set-ConstructedState($initial_state, $desired_state) { + Try { + Set-ADComputer ` + -Identity $desired_state.name ` + -SamAccountName $desired_state.name ` + -DNSHostName $desired_state.dns_hostname ` + -Enabled $desired_state.enabled ` + -Description $desired_state.description ` + -WhatIf:$check_mode + } Catch { + Fail-Json -obj $result -message "Failed to set the AD object $($desired_state.name): $($_.Exception.Message)" + } + + If ($initial_state.distinguished_name -cne $desired_state.distinguished_name) { + # Move computer to OU + Try { + Get-ADComputer -Identity $desired_state.name | + Move-ADObject ` + -TargetPath $desired_state.ou ` + -Confirm:$False ` + -WhatIf:$check_mode + } Catch { + Fail-Json -obj $result -message "Failed to move the AD object $($desired_state.name) to $($desired_state.ou) OU: $($_.Exception.Message)" + } + } + $result.changed = $true +} + +# ------------------------------------------------------------------------------ +Function Add-ConstructedState($desired_state) { + Try { + New-ADComputer ` + -Name $desired_state.name ` + -SamAccountName $desired_state.sam_account_name ` + -DNSHostName $desired_state.dns_hostname ` + -Path $desired_state.ou ` + -Enabled $desired_state.enabled ` + -Description $desired_state.description ` + -WhatIf:$check_mode + } Catch { + Fail-Json -obj $result -message "Failed to create the AD object $($desired_state.name): $($_.Exception.Message)" + } + + $result.changed = $true +} + +# ------------------------------------------------------------------------------ +Function Remove-ConstructedState($initial_state) { + Try { + Remove-ADComputer ` + -Identity $initial_state.name ` + -Confirm:$False ` + -WhatIf:$check_mode + } Catch { + Fail-Json -obj $result -message "Failed to remove the AD object $($desired_state.name): $($_.Exception.Message)" + } + + $result.changed = $true +} + +# ------------------------------------------------------------------------------ +Function are_hashtables_equal($x, $y) { + # Compare not nested HashTables + Foreach ($key in $x.Keys) { + If (($y.Keys -notcontains $key) -or ($x[$key] -cne $y[$key])) { + Return $false + } + } + foreach ($key in $y.Keys) { + if (($x.Keys -notcontains $key) -or ($x[$key] -cne $y[$key])) { + Return $false + } + } + Return $true +} + +# ------------------------------------------------------------------------------ +$initial_state = Get-InitialState($desired_state) + +If ($desired_state.state -eq "present") { + If ($initial_state.state -eq "present") { + $in_desired_state = are_hashtables_equal $initial_state $desired_state + + If (-not $in_desired_state) { + Set-ConstructedState $initial_state $desired_state + } + } Else { # $desired_state.state = "Present" & $initial_state.state = "Absent" + Add-ConstructedState($desired_state) + } + } Else { # $desired_state.state = "Absent" + If ($initial_state.state -eq "present") { + Remove-ConstructedState($initial_state) + } + } + +If ($diff_support) { + $diff = @{ + before = $initial_state + after = $desired_state + } + $result.diff = $diff +} + +Exit-Json -obj $result diff --git a/lib/ansible/modules/windows/win_domain_computer.py b/lib/ansible/modules/windows/win_domain_computer.py new file mode 100644 index 0000000000..32b21af715 --- /dev/null +++ b/lib/ansible/modules/windows/win_domain_computer.py @@ -0,0 +1,93 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2017, AMTEGA - Xunta de Galicia +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + + +DOCUMENTATION = ''' +--- +module: win_domain_computer +short_description: Manage computers in Active Directory +description: + - Create, read, update and delete computers in Active Directory using a + windows brigde computer to launch New-ADComputer, Get-ADComputer, + Set-ADComputer, Remove-ADComputer and Move-ADObject powershell commands. +options: + name: + description: + - Specifies the name of the object. This parameter sets the Name property + of the Active Directory object. The LDAP display name (ldapDisplayName) + of this property is name. + required: true + sam_account_name: + description: + - Specifies the Security Account Manager (SAM) account name of the + computer. It maximum is 256 characters, 15 is advised for older + operating systems compatibility. The LDAP display name + (ldapDisplayName) for this property is sAMAccountName. If ommitted the + value is the same as C(name). + Note. All computer SAMAccountNames needs to end with a $. + enabled: + description: + - Specifies if an account is enabled. An enabled account requires a + password. This parameter sets the Enabled property for an account + object. This parameter also sets the ADS_UF_ACCOUNTDISABLE flag of the + Active Directory User Account Control (UAC) attribute. + type: bool + default: 'yes' + ou: + description: + - Specifies the X.500 path of the Organizational Unit (OU) or container + where the new object is created. Required when I(state=present). + description: + description: + - Specifies a description of the object. This parameter sets the value + of the Description property for the object. The LDAP display name + (ldapDisplayName) for this property is description. + default: '' + dns_hostname: + description: + - Specifies the fully qualified domain name (FQDN) of the computer. This + parameter sets the DNSHostName property for a computer object. The LDAP + display name for this property is dNSHostName. Required when + I(state=present). + state: + description: + - Specified whether the computer should be C(present) or C(absent) in + Active Directory. + choices: + - present + - absent + default: present +notes: +version_added: 2.6 +author: Daniel Sánchez Fábregas (@Daniel-Sanchez-Fabregas) +''' + +EXAMPLES = ''' + - name: Add linux computer to Active Directory OU using a windows machine + win_domain_computer: + name: one_linux_server.my_org.local + sam_account_name: linux_server + dns_hostname: one_linux_server.my_org.local + ou: "OU=servers,DC=my_org,DC=local" + description: Example of linux server + enabled: yes + state: present + delegate_to: my_windows_bridge.my_org.local + + - name: Remove linux computer from Active Directory using a windows machine + win_domain_computer: + name: one_linux_server.my_org.local + state: absent + delegate_to: my_windows_bridge.my_org.local +''' + +RETURN = ''' +'''