diff --git a/changelogs/fragments/win_domain_membership-replace.yaml b/changelogs/fragments/win_domain_membership-replace.yaml new file mode 100644 index 00000000000..73cadd249b7 --- /dev/null +++ b/changelogs/fragments/win_domain_membership-replace.yaml @@ -0,0 +1,2 @@ +minor_changes: +- win_domain_membership - will now fail if an existing AD object for the host exists and ``allow_existing_computer_account=no`` - https://github.com/ansible/ansible/pull/53542 diff --git a/docs/docsite/rst/porting_guides/porting_guide_2.8.rst b/docs/docsite/rst/porting_guides/porting_guide_2.8.rst index 969c5fad793..3512f19fa1a 100644 --- a/docs/docsite/rst/porting_guides/porting_guide_2.8.rst +++ b/docs/docsite/rst/porting_guides/porting_guide_2.8.rst @@ -315,6 +315,10 @@ Noteworthy module changes * The ``win_dsc`` module will now validate the input options for a DSC resource. In previous versions invalid options would be ignored but are now not. +* The ``win_domain_membership`` module will no longer automatically join a host in a domain that already has an account + with the same name. Set ``allow_existing_computer_account=yes`` to override this check and go back to the original + behaviour. + Plugins ======= diff --git a/lib/ansible/modules/windows/win_domain_membership.ps1 b/lib/ansible/modules/windows/win_domain_membership.ps1 index e08e17c1585..32b5f3eb0e3 100644 --- a/lib/ansible/modules/windows/win_domain_membership.ps1 +++ b/lib/ansible/modules/windows/win_domain_membership.ps1 @@ -47,11 +47,11 @@ Function Get-DomainMembershipMatch { } catch [System.Security.Authentication.AuthenticationException] { Write-DebugLog "Failed to get computer domain. Attempting a different method." - Add-Type -AssemblyName System.DirectoryServices.AccountManagement + Add-Type -AssemblyName System.DirectoryServices.AccountManagement $user_principal = [System.DirectoryServices.AccountManagement.UserPrincipal]::Current If ($user_principal.ContextType -eq "Machine") { $current_dns_domain = (Get-CimInstance -ClassName Win32_ComputerSystem -Property Domain).Domain - + $domain_match = $current_dns_domain -eq $dns_domain_name Write-DebugLog ("current domain {0} matches {1}: {2}" -f $current_dns_domain, $dns_domain_name, $domain_match) @@ -102,7 +102,8 @@ Function Join-Domain { [string] $new_hostname, [string] $domain_admin_user, [string] $domain_admin_password, - [string] $domain_ou_path + [string] $domain_ou_path, + [bool] $allow_existing_computer_account ) Write-DebugLog ("Creating credential for user {0}" -f $domain_admin_user) @@ -118,17 +119,24 @@ Function Join-Domain { Write-DebugLog "adding hostname set arg to Add-Computer args" If($new_hostname) { $add_args["NewName"] = $new_hostname + $hostname_in_domain = Get-ADObject -LDAPFilter "(&(CN=$new_hostname)(ObjectClass=Computer))" + } else { + $hostname_in_domain = Get-ADObject -LDAPFilter "(&(CN=$env:COMPUTERNAME)(ObjectClass=Computer))" } - if($domain_ou_path){ Write-DebugLog "adding OU destination arg to Add-Computer args" $add_args["OUPath"] = $domain_ou_path } + $argstr = $add_args | Out-String Write-DebugLog "calling Add-Computer with args: $argstr" try { - $add_result = Add-Computer @add_args + if($null -eq $hostname_in_domain -or ($null -ne $hostname_in_domain -and $allow_existing_computer_account)) { + $add_result = Add-Computer @add_args + } else { + Fail-Json -obj $result -message "failed to join domain: hostname already exists in AD and allow_existing_computer_account=no" + } } catch { Fail-Json -obj $result -message "failed to join domain: $($_.Exception.Message)" } @@ -154,7 +162,7 @@ Function Set-Workgroup { if ($swg_result.ReturnValue -ne 0) { Fail-Json -obj $result -message "failed to set workgroup through WMI, return value: $($swg_result.ReturnValue)" - + return $swg_result} } @@ -198,6 +206,7 @@ $workgroup_name = Get-AnsibleParam $params "workgroup_name" $domain_admin_user = Get-AnsibleParam $params "domain_admin_user" -failifempty $result $domain_admin_password = Get-AnsibleParam $params "domain_admin_password" -failifempty $result $domain_ou_path = Get-AnsibleParam $params "domain_ou_path" +$allow_existing_computer_account = Get-AnsibleParam $params "allow_existing_computer_account" -type "bool" -default $false $log_path = Get-AnsibleParam $params "log_path" $_ansible_check_mode = Get-AnsibleParam $params "_ansible_check_mode" -default $false @@ -239,6 +248,7 @@ Try { dns_domain_name = $dns_domain_name domain_admin_user = $domain_admin_user domain_admin_password = $domain_admin_password + allow_existing_computer_account = $allow_existing_computer_account } Write-DebugLog "not a domain member, joining..." diff --git a/lib/ansible/modules/windows/win_domain_membership.py b/lib/ansible/modules/windows/win_domain_membership.py index e74a0d8c01c..7d405267510 100644 --- a/lib/ansible/modules/windows/win_domain_membership.py +++ b/lib/ansible/modules/windows/win_domain_membership.py @@ -48,6 +48,13 @@ options: description: - When C(state) is C(workgroup), the name of the workgroup that the Windows host should be in. type: str + allow_existing_computer_account: + description: + - If a host with the same hostname is already in the AD, replace it. + type: bool + choices: [ true, false ] + default: false + version_added: "2.8" seealso: - module: win_domain - module: win_domain_controller diff --git a/test/sanity/pslint/ignore.txt b/test/sanity/pslint/ignore.txt index 544a3cde1b3..b68890576c3 100644 --- a/test/sanity/pslint/ignore.txt +++ b/test/sanity/pslint/ignore.txt @@ -57,7 +57,6 @@ lib/ansible/modules/windows/win_domain_controller.ps1 PSUseApprovedVerbs lib/ansible/modules/windows/win_domain_controller.ps1 PSUseDeclaredVarsMoreThanAssignments lib/ansible/modules/windows/win_domain_group.ps1 PSAvoidTrailingWhitespace lib/ansible/modules/windows/win_domain_membership.ps1 PSAvoidGlobalVars -lib/ansible/modules/windows/win_domain_membership.ps1 PSAvoidTrailingWhitespace lib/ansible/modules/windows/win_domain_membership.ps1 PSAvoidUsingWMICmdlet lib/ansible/modules/windows/win_domain_membership.ps1 PSCustomUseLiteralPath lib/ansible/modules/windows/win_domain_membership.ps1 PSUseApprovedVerbs