Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

xComputer: Fix for Error When Joining a Domain and Renaming a Computer when JoinOU is specified #222

Merged
merged 6 commits into from
Jun 27, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## Unreleased

- xComputer:
- Fix for 'directory service is busy' error when joining a domain and renaming
a computer when JoinOU is specified - Fixes [Issue #221](https://github.com/PowerShell/ComputerManagementDsc/issues/221).

## 6.4.0.0

- ScheduledTask:
Expand Down
29 changes: 28 additions & 1 deletion DSCResources/MSFT_Computer/MSFT_Computer.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ $script:localizedData = Get-LocalizedData `
-ResourceName 'MSFT_Computer' `
-ResourcePath (Split-Path -Parent $Script:MyInvocation.MyCommand.Path)

$FailToRenameAfterJoinDomainErrorId = 'FailToRenameAfterJoinDomain,Microsoft.PowerShell.Commands.AddComputerCommand'

<#
.SYNOPSIS
Gets the current state of the computer.
Expand Down Expand Up @@ -251,7 +253,32 @@ function Set-TargetResource
}

# Rename the computer, and join it to the domain.
Add-Computer @addComputerParameters
try
{
Add-Computer @addComputerParameters
}
catch [System.InvalidOperationException]
{
<#
If the rename failed during the domain join, re-try the rename.
References to this issue:
https://social.technet.microsoft.com/Forums/windowsserver/en-US/81105b18-b1ff-4fcc-ae5c-2c1a7cf7bf3d/addcomputer-to-domain-with-new-name-returns-error
https://powershell.org/forums/topic/the-directory-service-is-busy/
#>
if ($_.FullyQualifiedErrorId -eq $failToRenameAfterJoinDomainErrorId)
{
Write-Verbose -Message $script:localizedData.FailToRenameAfterJoinDomainMessage
Rename-Computer -NewName $Name -DomainCredential $Credential
}
else
{
New-InvalidOperationException -ErrorRecord $_
}
}
catch
{
throw $_
}

if ($rename)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ ConvertFrom-StringData @'
RenamedComputerMessage = Renamed computer to '{0}'.
RenamedComputerAndJoinedDomainMessage = Renamed computer to '{0}' and added to the domain '{1}'.
JoinedDomainMessage = Added computer to domain '{0}'.
FailToRenameAfterJoinDomainMessage = Failed to rename the computer during the domain join. Re-trying the rename.
RenamedComputerAndJoinedWorkgroupMessage = Renamed computer to '{0}' and addded to workgroup '{1}'.
JoinedWorkgroupMessage = Added computer to workgroup '{0}'.
CredentialsNotSpecifiedError = Must to specify credentials with domain.
Expand Down
137 changes: 136 additions & 1 deletion Tests/Unit/MSFT_Computer.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -481,13 +481,14 @@ try

Context "$($script:dscResourceName)\Set-TargetResource" {
Mock -CommandName Rename-Computer
Mock -CommandName Add-Computer
Mock -CommandName Set-CimInstance

It 'Throws if both DomainName and WorkGroupName are specified' {
$errorRecord = Get-InvalidOperationRecord `
-Message ($LocalizedData.DomainNameAndWorkgroupNameError)

Mock -CommandName Add-Computer

{
Set-TargetResource `
-Name $env:COMPUTERNAME `
Expand All @@ -505,6 +506,8 @@ try
-Message ($LocalizedData.CredentialsNotSpecifiedError) `
-ArgumentName 'Credentials'

Mock -CommandName Add-Computer

{
Set-TargetResource `
-Name $env:COMPUTERNAME `
Expand All @@ -529,6 +532,8 @@ try
'contoso.com'
}

Mock -CommandName Add-Computer

Set-TargetResource `
-Name $notComputerName `
-DomainName 'adventure-works.com' `
Expand All @@ -554,6 +559,8 @@ try
'contoso.com'
}

Mock -CommandName Add-Computer

Set-TargetResource `
-Name $notComputerName `
-DomainName 'adventure-works.com' `
Expand All @@ -580,6 +587,8 @@ try
'contoso.com'
}

Mock -CommandName Add-Computer

Set-TargetResource `
-Name $notComputerName `
-WorkGroupName 'contoso' `
Expand All @@ -604,6 +613,8 @@ try
''
}

Mock -CommandName Add-Computer

Set-TargetResource `
-Name $notComputerName `
-DomainName 'Contoso.com' `
Expand All @@ -628,6 +639,8 @@ try
''
}

Mock -CommandName Add-Computer

Set-TargetResource `
-Name $notComputerName `
-DomainName 'Contoso.com' `
Expand All @@ -653,6 +666,8 @@ try
''
}

Mock -CommandName Add-Computer

Set-TargetResource `
-Name $notComputerName `
-DomainName 'Contoso.com' `
Expand All @@ -665,6 +680,110 @@ try
Assert-MockCalled -CommandName Add-Computer -Exactly -Times 0 -Scope It -ParameterFilter { $WorkGroupName }
}

It 'Should try a separate rename if ''FailToRenameAfterJoinDomain'' occured during domain join' {
$message = "Computer '' was successfully joined to the new domain '', but renaming it to '' failed with the following error message: The directory service is busy."
$exception = [System.InvalidOperationException]::new($message)
$errorID = $failToRenameAfterJoinDomainErrorId
$errorCategory = [Management.Automation.ErrorCategory]::InvalidOperation
$errorRecord = [System.Management.Automation.ErrorRecord]::new($exception, $errorID, $errorCategory, $null)

Mock -CommandName Get-WMIObject -MockWith {
[PSCustomObject] @{
Domain = 'Contoso'
Workgroup = 'Contoso'
PartOfDomain = $false
}
}

Mock -CommandName Get-ComputerDomain -MockWith {
''
}

Mock -CommandName Add-Computer -MockWith {
Throw $errorRecord
}

Set-TargetResource `
-Name $notComputerName `
-DomainName 'Contoso.com' `
-JoinOU 'OU=Computers,DC=contoso,DC=com' `
-Credential $credential | Should -BeNullOrEmpty

Assert-MockCalled -CommandName Rename-Computer -Exactly -Times 1 -Scope It
Assert-MockCalled -CommandName Add-Computer -Exactly -Times 1 -Scope It -ParameterFilter { $DomainName -and $NewName }
Assert-MockCalled -CommandName Add-Computer -Exactly -Times 0 -Scope It -ParameterFilter { $WorkGroupName }
}

It 'Should Throw the correct error if Add-Computer errors with an unknown InvalidOperationException' {
$error = 'Unknown Error'
$errorRecord = [System.InvalidOperationException]::new($error)

Mock -CommandName Get-WMIObject -MockWith {
[PSCustomObject] @{
Domain = 'Contoso'
Workgroup = 'Contoso'
PartOfDomain = $false
}
}

Mock -CommandName Get-ComputerDomain -MockWith {
''
}

Mock -CommandName Add-Computer -MockWith {
Throw $errorRecord
}

Mock -CommandName New-InvalidOperationException -MockWith {
Throw $errorRecord
}

{ Set-TargetResource `
-Name $notComputerName `
-DomainName 'Contoso.com' `
-JoinOU 'OU=Computers,DC=contoso,DC=com' `
-Credential $credential
} | Should -Throw $error

Assert-MockCalled -CommandName Rename-Computer -Exactly -Times 0 -Scope It
Assert-MockCalled -CommandName Add-Computer -Exactly -Times 1 -Scope It -ParameterFilter { $DomainName -and $NewName }
Assert-MockCalled -CommandName Add-Computer -Exactly -Times 0 -Scope It -ParameterFilter { $WorkGroupName }
}

It 'Should Throw the correct error if Add-Computer errors with an unknown error' {
$errorRecord = 'Unknown Error'
Mock -CommandName Get-WMIObject -MockWith {
[PSCustomObject] @{
Domain = 'Contoso'
Workgroup = 'Contoso'
PartOfDomain = $false
}
}

Mock -CommandName Get-ComputerDomain -MockWith {
''
}

Mock -CommandName Add-Computer -MockWith {
Throw $errorRecord
}

Mock -CommandName New-InvalidOperationException -MockWith {
Throw $errorRecord
}

{ Set-TargetResource `
-Name $notComputerName `
-DomainName 'Contoso.com' `
-JoinOU 'OU=Computers,DC=contoso,DC=com' `
-Credential $credential
} | Should -Throw $errorRecord

Assert-MockCalled -CommandName Rename-Computer -Exactly -Times 0 -Scope It
Assert-MockCalled -CommandName Add-Computer -Exactly -Times 1 -Scope It -ParameterFilter { $DomainName -and $NewName }
Assert-MockCalled -CommandName Add-Computer -Exactly -Times 0 -Scope It -ParameterFilter { $WorkGroupName }
}

It 'Changes ComputerName and changes Workgroup to new Workgroup' {
Mock -CommandName Get-WMIObject -MockWith {
[PSCustomObject] @{
Expand All @@ -678,6 +797,8 @@ try
''
}

Mock -CommandName Add-Computer

Set-TargetResource `
-Name $notComputerName `
-WorkGroupName 'adventure-works' `
Expand All @@ -701,6 +822,8 @@ try
'contoso.com'
}

Mock -CommandName Add-Computer

Set-TargetResource `
-Name $env:COMPUTERNAME `
-DomainName 'adventure-works.com' `
Expand All @@ -727,6 +850,8 @@ try
'contoso.com'
}

Mock -CommandName Add-Computer

Set-TargetResource `
-Name 'localhost' `
-DomainName 'adventure-works.com' `
Expand All @@ -753,6 +878,8 @@ try
'contoso.com'
}

Mock -CommandName Add-Computer

Set-TargetResource `
-Name $env:COMPUTERNAME `
-DomainName 'adventure-works.com' `
Expand Down Expand Up @@ -780,6 +907,8 @@ try
'contoso.com'
}

Mock -CommandName Add-Computer

Set-TargetResource `
-Name 'localhost' `
-DomainName 'adventure-works.com' `
Expand Down Expand Up @@ -807,6 +936,8 @@ try
''
}

Mock -CommandName Add-Computer

Set-TargetResource `
-Name $env:COMPUTERNAME `
-WorkGroupName 'Contoso' `
Expand All @@ -832,6 +963,8 @@ try
''
}

Mock -CommandName Add-Computer

Set-TargetResource `
-Name 'localhost' `
-WorkGroupName 'Contoso' `
Expand All @@ -857,6 +990,8 @@ try
'contoso.com'
}

Mock -CommandName Add-Computer

Set-TargetResource `
-Name $notComputerName `
-Credential $credential `
Expand Down