From 35a7c41e370ce47af87392e0a84c5d02c8a218ee Mon Sep 17 00:00:00 2001 From: Nick Germany Date: Tue, 17 May 2022 15:43:57 -0400 Subject: [PATCH 01/39] First stab at computer object deletion if it exists already --- .../DSC_Computer/DSC_Computer.psm1 | 41 ++++++++++++++++--- 1 file changed, 35 insertions(+), 6 deletions(-) diff --git a/source/DSCResources/DSC_Computer/DSC_Computer.psm1 b/source/DSCResources/DSC_Computer/DSC_Computer.psm1 index 94b246ff..4cd54216 100644 --- a/source/DSCResources/DSC_Computer/DSC_Computer.psm1 +++ b/source/DSCResources/DSC_Computer/DSC_Computer.psm1 @@ -92,18 +92,18 @@ function Get-TargetResource $convertToCimCredential = New-CimInstance ` -ClassName DSC_Credential ` -Property @{ - Username = [System.String] $Credential.UserName - Password = [System.String] $null - } ` + Username = [System.String] $Credential.UserName + Password = [System.String] $null + } ` -Namespace root/microsoft/windows/desiredstateconfiguration ` -ClientOnly $convertToCimUnjoinCredential = New-CimInstance ` -ClassName DSC_Credential ` -Property @{ - Username = [System.String] $UnjoinCredential.UserName - Password = [System.String] $null - } ` + Username = [System.String] $UnjoinCredential.UserName + Password = [System.String] $null + } ` -Namespace root/microsoft/windows/desiredstateconfiguration ` -ClientOnly @@ -247,6 +247,35 @@ function Set-TargetResource $addComputerParameters.Add("Server", $Server) } + # Check for existing computer objecst using ADSI without ActiveDirectory module + try + { + $searcher = New-Object -TypeName System.DirectoryServices.DirectorySearcher ` + -ErrorAction Stop + $searcher.Filter = "(&(objectCategory=Computer)(name=$Name))" + if ( $DomainDN -notlike "LDAP://*") + { + $DomainDN = "LDAP://$DomainDN" + } + $searcher.SearchRoot = $DomainDN + + $directoryEntry = New-Object -TypeName System.DirectoryServices.DirectoryEntry ` + -ArgumentList $DomainDN, $($Credential.UserName), $($Credential.GetNetworkCredential().password) ` + -ErrorAction Stop + $searcher.SearchRoot = $directoryEntry + + $computerObj = $searcher.FindOne() + if ($computerObj) + { + $objectPath = [adsi]$computerObj.Path + $objectPath.psbase.DeleteTree() + } + } + catch + { + + } + # Rename the computer, and join it to the domain. try { From f37afb288619a709fa171b119a3fc7003d46d212 Mon Sep 17 00:00:00 2001 From: Nick Germany Date: Tue, 17 May 2022 20:55:21 -0400 Subject: [PATCH 02/39] Using functions and adding some verbose output --- .../DSC_Computer/DSC_Computer.psm1 | 121 ++++++++++++++---- .../en-US/DSC_Computer.strings.psd1 | 2 + 2 files changed, 99 insertions(+), 24 deletions(-) diff --git a/source/DSCResources/DSC_Computer/DSC_Computer.psm1 b/source/DSCResources/DSC_Computer/DSC_Computer.psm1 index 4cd54216..380d49d6 100644 --- a/source/DSCResources/DSC_Computer/DSC_Computer.psm1 +++ b/source/DSCResources/DSC_Computer/DSC_Computer.psm1 @@ -248,32 +248,12 @@ function Set-TargetResource } # Check for existing computer objecst using ADSI without ActiveDirectory module - try - { - $searcher = New-Object -TypeName System.DirectoryServices.DirectorySearcher ` - -ErrorAction Stop - $searcher.Filter = "(&(objectCategory=Computer)(name=$Name))" - if ( $DomainDN -notlike "LDAP://*") - { - $DomainDN = "LDAP://$DomainDN" - } - $searcher.SearchRoot = $DomainDN - - $directoryEntry = New-Object -TypeName System.DirectoryServices.DirectoryEntry ` - -ArgumentList $DomainDN, $($Credential.UserName), $($Credential.GetNetworkCredential().password) ` - -ErrorAction Stop - $searcher.SearchRoot = $directoryEntry + $computerObject = Get-ADSIComputer -Name $Name -DomainName $DomainName -Credential $Credential - $computerObj = $searcher.FindOne() - if ($computerObj) - { - $objectPath = [adsi]$computerObj.Path - $objectPath.psbase.DeleteTree() - } - } - catch + if ($computerObject) { - + Delete-ADSIObject -Name $computerObject.Path -Credential $Credential + Write-Verbose -Message ($script:localizedData.DeletedExistingComputerObject -f @($Name, $computerObject.Path)) } # Rename the computer, and join it to the domain. @@ -676,4 +656,97 @@ function Get-LogonServer return $logonserver } +<# + .SYNOPSIS + Returns an ADSI Computer Object. + + .PARAMETER Name + Name of the computer to search for in the given domain + + .PARAMETER Domain + Domain to search + + .PARAMETER Credential + Credential to search domain with +#> +function Get-ADSIComputer +{ + [CmdletBinding()] + [OutputType([System.DirectoryServices.SearchResult])] + param + ( + [Parameter(Mandatory = $true)] + [ValidateLength(1, 15)] + [ValidateScript( { $_ -inotmatch '[\/\\:*?"<>|]' })] + [System.String] + $Name, + + [Parameter(Mandatory = $true)] + [System.String] + $DomainName, + + [Parameter(Mandatory = $true)] + [System.Management.Automation.PSCredential] + $Credential + ) + + $searcher = ([adsisearcher]"(&(objectCategory=computer)(objectClass=computer)(cn=$Name))") + if ( $DomainName -notlike "LDAP://*") + { + $DomainName = "LDAP://$DomainName" + } + + try + { + $searchRoot = New-Object -TypeName System.DirectoryServices.DirectoryEntry ` + -ArgumentList $DomainName, $($Credential.UserName), $($Credential.GetNetworkCredential().password) ` + -ErrorAction Stop + } + catch + { + New-InvalidOperationException -Message $_.Exception.Message -ErrorRecord $_ + } + $searcher.SearchRoot = $searchRoot + + return $searcher.FindOne() +} + +<# + .SYNOPSIS + Deletes an ADSI DirectoryEntry Object. + + .PARAMETER Path + Path to Object to delete + + .PARAMETER Credential + Credential to authenticate to the domain +#> +function Delete-ADSIObject +{ + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [System.DirectoryServices.DirectoryEntry] + $Path, + + [Parameter(Mandatory = $true)] + [System.Management.Automation.PSCredential] + $Credential + ) + + try + { + $adsiObj = New-Object -TypeName System.DirectoryServices.DirectoryEntry ` + -ArgumentList $computerObj.Path, $($Credential.UserName), $($Credential.GetNetworkCredential().password) ` + -ErrorAction Stop + + $adsiObj.psbase.DeleteTree() + } + catch + { + New-InvalidOperationException -Message $_.Exception.Message -ErrorRecord $_ + } +} + Export-ModuleMember -Function *-TargetResource diff --git a/source/DSCResources/DSC_Computer/en-US/DSC_Computer.strings.psd1 b/source/DSCResources/DSC_Computer/en-US/DSC_Computer.strings.psd1 index 011bdec9..c4a69840 100644 --- a/source/DSCResources/DSC_Computer/en-US/DSC_Computer.strings.psd1 +++ b/source/DSCResources/DSC_Computer/en-US/DSC_Computer.strings.psd1 @@ -16,4 +16,6 @@ ConvertFrom-StringData @' CheckingWorkgroupMemberMessage = Checking if the machine is a member of workgroup '{0}'. DomainNameAndWorkgroupNameError = Only DomainName or WorkGroupName can be specified at once. ComputerNotInDomainMessage = This machine is not a domain member. + DeletedExistingComputerObject = Deleted existing computer object with name + '{0}' at path '{1}'. '@ From 9b87213ceeb3ed4b422ebac1a0cf3989ebe09a2e Mon Sep 17 00:00:00 2001 From: Nick Germany Date: Tue, 17 May 2022 21:41:11 -0400 Subject: [PATCH 03/39] Updating changelog and fixing formatting --- CHANGELOG.md | 2 ++ source/DSCResources/DSC_Computer/DSC_Computer.psm1 | 12 ++++++------ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8295ad34..4e8ea2da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ The format is based on and uses the types of changes according to [Keep a Change and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +- Computer + - When joining a computer to a domain, existing AD computer objects will be deleted. ## [8.5.0] - 2021-09-13 diff --git a/source/DSCResources/DSC_Computer/DSC_Computer.psm1 b/source/DSCResources/DSC_Computer/DSC_Computer.psm1 index 380d49d6..0cce0547 100644 --- a/source/DSCResources/DSC_Computer/DSC_Computer.psm1 +++ b/source/DSCResources/DSC_Computer/DSC_Computer.psm1 @@ -92,18 +92,18 @@ function Get-TargetResource $convertToCimCredential = New-CimInstance ` -ClassName DSC_Credential ` -Property @{ - Username = [System.String] $Credential.UserName - Password = [System.String] $null - } ` + Username = [System.String] $Credential.UserName + Password = [System.String] $null + } ` -Namespace root/microsoft/windows/desiredstateconfiguration ` -ClientOnly $convertToCimUnjoinCredential = New-CimInstance ` -ClassName DSC_Credential ` -Property @{ - Username = [System.String] $UnjoinCredential.UserName - Password = [System.String] $null - } ` + Username = [System.String] $UnjoinCredential.UserName + Password = [System.String] $null + } ` -Namespace root/microsoft/windows/desiredstateconfiguration ` -ClientOnly From 27852f707f91675d4ec45370a45584a0d7a01f46 Mon Sep 17 00:00:00 2001 From: Nick Germany Date: Tue, 17 May 2022 22:38:26 -0400 Subject: [PATCH 04/39] Fixing up functions --- source/DSCResources/DSC_Computer/DSC_Computer.psm1 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/source/DSCResources/DSC_Computer/DSC_Computer.psm1 b/source/DSCResources/DSC_Computer/DSC_Computer.psm1 index 0cce0547..359f1810 100644 --- a/source/DSCResources/DSC_Computer/DSC_Computer.psm1 +++ b/source/DSCResources/DSC_Computer/DSC_Computer.psm1 @@ -252,7 +252,7 @@ function Set-TargetResource if ($computerObject) { - Delete-ADSIObject -Name $computerObject.Path -Credential $Credential + Delete-ADSIObject -Path $computerObject.Path -Credential $Credential Write-Verbose -Message ($script:localizedData.DeletedExistingComputerObject -f @($Name, $computerObject.Path)) } @@ -727,7 +727,7 @@ function Delete-ADSIObject param ( [Parameter(Mandatory = $true)] - [System.DirectoryServices.DirectoryEntry] + [System.String] $Path, [Parameter(Mandatory = $true)] @@ -738,7 +738,7 @@ function Delete-ADSIObject try { $adsiObj = New-Object -TypeName System.DirectoryServices.DirectoryEntry ` - -ArgumentList $computerObj.Path, $($Credential.UserName), $($Credential.GetNetworkCredential().password) ` + -ArgumentList $Path, $($Credential.UserName), $($Credential.GetNetworkCredential().password) ` -ErrorAction Stop $adsiObj.psbase.DeleteTree() From 77423d3c033060dd2aaa3610bf9a1e5b397a1db0 Mon Sep 17 00:00:00 2001 From: Nick Germany Date: Wed, 18 May 2022 12:49:04 -0400 Subject: [PATCH 05/39] fixing verbose message --- source/DSCResources/DSC_Computer/DSC_Computer.psm1 | 2 +- .../DSCResources/DSC_Computer/en-US/DSC_Computer.strings.psd1 | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/source/DSCResources/DSC_Computer/DSC_Computer.psm1 b/source/DSCResources/DSC_Computer/DSC_Computer.psm1 index 359f1810..c86d4ac2 100644 --- a/source/DSCResources/DSC_Computer/DSC_Computer.psm1 +++ b/source/DSCResources/DSC_Computer/DSC_Computer.psm1 @@ -253,7 +253,7 @@ function Set-TargetResource if ($computerObject) { Delete-ADSIObject -Path $computerObject.Path -Credential $Credential - Write-Verbose -Message ($script:localizedData.DeletedExistingComputerObject -f @($Name, $computerObject.Path)) + Write-Verbose -Message ($script:localizedData.DeletedExistingComputerObject -f $Name, $computerObject.Path) } # Rename the computer, and join it to the domain. diff --git a/source/DSCResources/DSC_Computer/en-US/DSC_Computer.strings.psd1 b/source/DSCResources/DSC_Computer/en-US/DSC_Computer.strings.psd1 index c4a69840..54220d4b 100644 --- a/source/DSCResources/DSC_Computer/en-US/DSC_Computer.strings.psd1 +++ b/source/DSCResources/DSC_Computer/en-US/DSC_Computer.strings.psd1 @@ -16,6 +16,5 @@ ConvertFrom-StringData @' CheckingWorkgroupMemberMessage = Checking if the machine is a member of workgroup '{0}'. DomainNameAndWorkgroupNameError = Only DomainName or WorkGroupName can be specified at once. ComputerNotInDomainMessage = This machine is not a domain member. - DeletedExistingComputerObject = Deleted existing computer object with name - '{0}' at path '{1}'. + DeletedExistingComputerObject = Deleted existing computer object with name '{0}' at path '{1}'. '@ From 0675006dbc2b91761f7be5c77c1cc24f40418358 Mon Sep 17 00:00:00 2001 From: Nick Germany Date: Wed, 18 May 2022 17:57:16 -0400 Subject: [PATCH 06/39] adding tests --- tests/Unit/DSC_Computer.Tests.ps1 | 64 +++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/tests/Unit/DSC_Computer.Tests.ps1 b/tests/Unit/DSC_Computer.Tests.ps1 index eeb6339d..07b6350b 100644 --- a/tests/Unit/DSC_Computer.Tests.ps1 +++ b/tests/Unit/DSC_Computer.Tests.ps1 @@ -485,6 +485,8 @@ try Context 'DSC_Computer\Set-TargetResource' { Mock -CommandName Rename-Computer Mock -CommandName Set-CimInstance + Mock -CommandName Get-ADSIComputer + Mock -CommandName Delete-ADSIObject It 'Throws if both DomainName and WorkGroupName are specified' { $errorRecord = Get-InvalidOperationRecord ` @@ -531,6 +533,12 @@ try } } + Mock -CommandName Get-ADSIComputer -MockWith { + [PSCustomObject] @{ + Path = 'LDAP://Contoso.com/CN=mocked-comp,OU=Computers,DC=Contoso,DC=com'; + } + } + Mock -CommandName Get-ComputerDomain -MockWith { 'contoso.com' } @@ -547,6 +555,8 @@ try 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 } + Assert-MockCalled -CommandName Get-ADSIComputer -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Delete-ADSIObject -Exactly -Times 1 -Scope It } It 'Changes ComputerName and changes Domain to new Domain with specified OU' { @@ -562,6 +572,12 @@ try 'contoso.com' } + Mock -CommandName Get-ADSIComputer -MockWith { + [PSCustomObject] @{ + Path = 'LDAP://Contoso.com/CN=mocked-comp,OU=Computers,DC=Contoso,DC=com'; + } + } + Mock -CommandName Add-Computer Set-TargetResource ` @@ -575,6 +591,8 @@ try 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 } + Assert-MockCalled -CommandName Get-ADSIComputer -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Delete-ADSIObject -Exactly -Times 1 -Scope It } It 'Changes ComputerName and changes Domain to Workgroup' { @@ -601,6 +619,8 @@ try Assert-MockCalled -CommandName Rename-Computer -Exactly -Times 0 -Scope It Assert-MockCalled -CommandName Add-Computer -Exactly -Times 1 -Scope It -ParameterFilter { $WorkGroupName -and $NewName -and $credential } Assert-MockCalled -CommandName Add-Computer -Exactly -Times 0 -Scope It -ParameterFilter { $DomainName -or $UnjoinCredential } + Assert-MockCalled -CommandName Get-ADSIComputer -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Delete-ADSIObject -Exactly -Times 1 -Scope It } It 'Changes ComputerName and changes Workgroup to Domain' { @@ -612,6 +632,12 @@ try } } + Mock -CommandName Get-ADSIComputer -MockWith { + [PSCustomObject] @{ + Path = 'LDAP://Contoso.com/CN=mocked-comp,OU=Computers,DC=Contoso,DC=com'; + } + } + Mock -CommandName Get-ComputerDomain -MockWith { '' } @@ -627,6 +653,8 @@ try 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 } + Assert-MockCalled -CommandName Get-ADSIComputer -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Delete-ADSIObject -Exactly -Times 1 -Scope It } It 'Changes ComputerName and changes Workgroup to Domain with specified Domain Controller' { @@ -638,6 +666,12 @@ try } } + Mock -CommandName Get-ADSIComputer -MockWith { + [PSCustomObject] @{ + Path = 'LDAP://Contoso.com/CN=mocked-comp,OU=Computers,DC=Contoso,DC=com'; + } + } + Mock -CommandName Get-ComputerDomain -MockWith { '' } @@ -654,6 +688,8 @@ try Assert-MockCalled -CommandName Rename-Computer -Exactly -Times 0 -Scope It Assert-MockCalled -CommandName Add-Computer -Exactly -Times 1 -Scope It -ParameterFilter { $DomainName -and $NewName -and $Server } Assert-MockCalled -CommandName Add-Computer -Exactly -Times 0 -Scope It -ParameterFilter { $WorkGroupName } + Assert-MockCalled -CommandName Get-ADSIComputer -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Delete-ADSIObject -Exactly -Times 1 -Scope It } It 'Changes ComputerName and changes Workgroup to Domain with specified OU' { @@ -665,6 +701,12 @@ try } } + Mock -CommandName Get-ADSIComputer -MockWith { + [PSCustomObject] @{ + Path = 'LDAP://Contoso.com/CN=mocked-comp,OU=Computers,DC=Contoso,DC=com'; + } + } + Mock -CommandName Get-ComputerDomain -MockWith { '' } @@ -681,6 +723,8 @@ try 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 } + Assert-MockCalled -CommandName Get-ADSIComputer -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Delete-ADSIObject -Exactly -Times 1 -Scope It } It 'Should try a separate rename if ''FailToRenameAfterJoinDomain'' occured during domain join' { @@ -698,6 +742,10 @@ try } } + Mock -CommandName Get-ADSIComputer -MockWith { + $null + } + Mock -CommandName Get-ComputerDomain -MockWith { '' } @@ -715,6 +763,8 @@ try 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 } + Assert-MockCalled -CommandName Get-ADSIComputer -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Delete-ADSIObject -Exactly -Times 0 -Scope It } It 'Should Throw the correct error if Add-Computer errors with an unknown InvalidOperationException' { @@ -785,6 +835,8 @@ try 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 } + Assert-MockCalled -CommandName Get-ADSIComputer -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Delete-ADSIObject -Exactly -Times 0 -Scope It } It 'Changes ComputerName and changes Workgroup to new Workgroup' { @@ -810,6 +862,8 @@ try Assert-MockCalled -CommandName Rename-Computer -Exactly -Times 0 -Scope It Assert-MockCalled -CommandName Add-Computer -Exactly -Times 1 -Scope It -ParameterFilter { $WorkGroupName -and $NewName } Assert-MockCalled -CommandName Add-Computer -Exactly -Times 0 -Scope It -ParameterFilter { $DomainName } + Assert-MockCalled -CommandName Get-ADSIComputer -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Delete-ADSIObject -Exactly -Times 0 -Scope It } It 'Changes only the Domain to new Domain' { @@ -838,6 +892,8 @@ try Assert-MockCalled -CommandName Add-Computer -Exactly -Times 1 -Scope It -ParameterFilter { $DomainName } Assert-MockCalled -CommandName Add-Computer -Exactly -Times 0 -Scope It -ParameterFilter { $NewName } Assert-MockCalled -CommandName Add-Computer -Exactly -Times 0 -Scope It -ParameterFilter { $WorkGroupName } + Assert-MockCalled -CommandName Get-ADSIComputer -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Delete-ADSIObject -Exactly -Times 0 -Scope It } It 'Changes only the Domain to new Domain when name is [localhost]' { @@ -866,6 +922,8 @@ try Assert-MockCalled -CommandName Add-Computer -Exactly -Times 1 -Scope It -ParameterFilter { $DomainName } Assert-MockCalled -CommandName Add-Computer -Exactly -Times 0 -Scope It -ParameterFilter { $NewName } Assert-MockCalled -CommandName Add-Computer -Exactly -Times 0 -Scope It -ParameterFilter { $WorkGroupName } + Assert-MockCalled -CommandName Get-ADSIComputer -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Delete-ADSIObject -Exactly -Times 0 -Scope It } It 'Changes only the Domain to new Domain with specified OU' { @@ -895,6 +953,8 @@ try Assert-MockCalled -CommandName Add-Computer -Exactly -Times 1 -Scope It -ParameterFilter { $DomainName } Assert-MockCalled -CommandName Add-Computer -Exactly -Times 0 -Scope It -ParameterFilter { $NewName } Assert-MockCalled -CommandName Add-Computer -Exactly -Times 0 -Scope It -ParameterFilter { $WorkGroupName } + Assert-MockCalled -CommandName Get-ADSIComputer -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Delete-ADSIObject -Exactly -Times 0 -Scope It } It 'Changes only the Domain to new Domain with specified OU when Name is [localhost]' { @@ -924,6 +984,8 @@ try Assert-MockCalled -CommandName Add-Computer -Exactly -Times 1 -Scope It -ParameterFilter { $DomainName } Assert-MockCalled -CommandName Add-Computer -Exactly -Times 0 -Scope It -ParameterFilter { $NewName } Assert-MockCalled -CommandName Add-Computer -Exactly -Times 0 -Scope It -ParameterFilter { $WorkGroupName } + Assert-MockCalled -CommandName Get-ADSIComputer -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Delete-ADSIObject -Exactly -Times 0 -Scope It } It 'Changes only Domain to Workgroup' { @@ -951,6 +1013,8 @@ try Assert-MockCalled -CommandName Add-Computer -Exactly -Times 0 -Scope It -ParameterFilter { $NewName } Assert-MockCalled -CommandName Add-Computer -Exactly -Times 1 -Scope It -ParameterFilter { $WorkGroupName } Assert-MockCalled -CommandName Add-Computer -Exactly -Times 0 -Scope It -ParameterFilter { $DomainName } + Assert-MockCalled -CommandName Get-ADSIComputer -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Delete-ADSIObject -Exactly -Times 0 -Scope It } It 'Changes only Domain to Workgroup when Name is [localhost]' { From 342891b2abf0b07e70f32a9c01bf5759bbdece69 Mon Sep 17 00:00:00 2001 From: Nick Germany Date: Wed, 18 May 2022 18:06:07 -0400 Subject: [PATCH 07/39] Fixing mocks for adsi funcs in workgroup join --- tests/Unit/DSC_Computer.Tests.ps1 | 6 ------ 1 file changed, 6 deletions(-) diff --git a/tests/Unit/DSC_Computer.Tests.ps1 b/tests/Unit/DSC_Computer.Tests.ps1 index 07b6350b..85335080 100644 --- a/tests/Unit/DSC_Computer.Tests.ps1 +++ b/tests/Unit/DSC_Computer.Tests.ps1 @@ -619,8 +619,6 @@ try Assert-MockCalled -CommandName Rename-Computer -Exactly -Times 0 -Scope It Assert-MockCalled -CommandName Add-Computer -Exactly -Times 1 -Scope It -ParameterFilter { $WorkGroupName -and $NewName -and $credential } Assert-MockCalled -CommandName Add-Computer -Exactly -Times 0 -Scope It -ParameterFilter { $DomainName -or $UnjoinCredential } - Assert-MockCalled -CommandName Get-ADSIComputer -Exactly -Times 1 -Scope It - Assert-MockCalled -CommandName Delete-ADSIObject -Exactly -Times 1 -Scope It } It 'Changes ComputerName and changes Workgroup to Domain' { @@ -862,8 +860,6 @@ try Assert-MockCalled -CommandName Rename-Computer -Exactly -Times 0 -Scope It Assert-MockCalled -CommandName Add-Computer -Exactly -Times 1 -Scope It -ParameterFilter { $WorkGroupName -and $NewName } Assert-MockCalled -CommandName Add-Computer -Exactly -Times 0 -Scope It -ParameterFilter { $DomainName } - Assert-MockCalled -CommandName Get-ADSIComputer -Exactly -Times 1 -Scope It - Assert-MockCalled -CommandName Delete-ADSIObject -Exactly -Times 0 -Scope It } It 'Changes only the Domain to new Domain' { @@ -1013,8 +1009,6 @@ try Assert-MockCalled -CommandName Add-Computer -Exactly -Times 0 -Scope It -ParameterFilter { $NewName } Assert-MockCalled -CommandName Add-Computer -Exactly -Times 1 -Scope It -ParameterFilter { $WorkGroupName } Assert-MockCalled -CommandName Add-Computer -Exactly -Times 0 -Scope It -ParameterFilter { $DomainName } - Assert-MockCalled -CommandName Get-ADSIComputer -Exactly -Times 1 -Scope It - Assert-MockCalled -CommandName Delete-ADSIObject -Exactly -Times 0 -Scope It } It 'Changes only Domain to Workgroup when Name is [localhost]' { From b520ab14cba03569374e3fa905b4bb8189f16d32 Mon Sep 17 00:00:00 2001 From: Nick Germany Date: Thu, 19 May 2022 12:47:59 -0400 Subject: [PATCH 08/39] initial getadsicomputer tests --- tests/Unit/DSC_Computer.Tests.ps1 | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tests/Unit/DSC_Computer.Tests.ps1 b/tests/Unit/DSC_Computer.Tests.ps1 index 85335080..e4bca874 100644 --- a/tests/Unit/DSC_Computer.Tests.ps1 +++ b/tests/Unit/DSC_Computer.Tests.ps1 @@ -1237,6 +1237,28 @@ try Get-LogonServer | Should -Not -BeNullOrEmpty } } + + Context 'DSC_Computer\Get-ADSIComputer' { + It 'Throws if name is to long' { + { + Get-ADSIComputer ` + -Name 'ThisNameIsTooLong' ` + -Domain 'Contoso.com' ` + -Credential $credential ` + -Verbose + } | Should -Throw + } + + It 'Throws if name contains illegal characters' { + { + Get-ADSIComputer ` + -Name 'IllegalName[<' ` + -Domain 'Contoso.com' ` + -Credential $credential ` + -Verbose + } | Should -Throw + } + } } } } From a50330efc42330d95a4beebfee6c6f6b5c7029c8 Mon Sep 17 00:00:00 2001 From: Nick Germany Date: Thu, 19 May 2022 15:23:19 -0400 Subject: [PATCH 09/39] Add test with mocks of dot net objects --- .../DSC_Computer/DSC_Computer.psm1 | 3 +- tests/Unit/DSC_Computer.Tests.ps1 | 37 +++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/source/DSCResources/DSC_Computer/DSC_Computer.psm1 b/source/DSCResources/DSC_Computer/DSC_Computer.psm1 index c86d4ac2..eb596a4d 100644 --- a/source/DSCResources/DSC_Computer/DSC_Computer.psm1 +++ b/source/DSCResources/DSC_Computer/DSC_Computer.psm1 @@ -690,7 +690,8 @@ function Get-ADSIComputer $Credential ) - $searcher = ([adsisearcher]"(&(objectCategory=computer)(objectClass=computer)(cn=$Name))") + $searcher = New-Object -TypeName System.DirectoryServices.DirectorySearcher + $searcher.Filter = "(&(objectCategory=computer)(objectClass=computer)(cn=$Name))" if ( $DomainName -notlike "LDAP://*") { $DomainName = "LDAP://$DomainName" diff --git a/tests/Unit/DSC_Computer.Tests.ps1 b/tests/Unit/DSC_Computer.Tests.ps1 index e4bca874..9e8a54fc 100644 --- a/tests/Unit/DSC_Computer.Tests.ps1 +++ b/tests/Unit/DSC_Computer.Tests.ps1 @@ -1258,6 +1258,43 @@ try -Verbose } | Should -Throw } + + It 'Return ADSI object' { + class fake_adsi_directoryentry { + [string] $Domain + [PSCredential] $Credential + } + + class fake_adsi_searcher { + [string] $SearchRoot + [string] $Filter + [hashtable] FindOne( ){ + return @{ + path = 'LDAP://contoso.com/CN=fake-computer,OU=Computers,DC=contoso,DC=com' + } + } + } + + Mock 'New-Object' { New-Object 'fake_adsi_directoryentry' } ` + -ParameterFilter { + $TypeName -and + $TypeName -eq 'System.DirectoryServices.DirectoryEntry' + } + + Mock 'New-Object' { New-Object 'fake_adsi_searcher' } ` + -ParameterFilter { + $TypeName -and + $TypeName -eq 'System.DirectoryServices.DirectorySearcher' + } + + { + Get-ADSIComputer ` + -Name 'IllegalName[<' ` + -Domain 'Contoso.com' ` + -Credential $credential ` + -Verbose + } | Should -Not -BeNullOrEmpty + } } } } From a92bdb147ed210ba424d1d074cda50ed22942500 Mon Sep 17 00:00:00 2001 From: Nick Germany Date: Fri, 20 May 2022 10:57:54 -0400 Subject: [PATCH 10/39] Initial delete-adsiobject test --- tests/Unit/DSC_Computer.Tests.ps1 | 86 +++++++++++++++++++++++-------- 1 file changed, 64 insertions(+), 22 deletions(-) diff --git a/tests/Unit/DSC_Computer.Tests.ps1 b/tests/Unit/DSC_Computer.Tests.ps1 index 9e8a54fc..a9cd086f 100644 --- a/tests/Unit/DSC_Computer.Tests.ps1 +++ b/tests/Unit/DSC_Computer.Tests.ps1 @@ -1239,6 +1239,34 @@ try } Context 'DSC_Computer\Get-ADSIComputer' { + class fake_adsi_directoryentry { + [string] $Domain + [string] $Username + [string] $password + } + + class fake_adsi_searcher { + [string] $SearchRoot + [string] $Filter + [hashtable] FindOne( ){ + return @{ + path = 'LDAP://contoso.com/CN=fake-computer,OU=Computers,DC=contoso,DC=com' + } + } + } + + Mock 'New-Object' { New-Object 'fake_adsi_directoryentry' } ` + -ParameterFilter { + $TypeName -and + $TypeName -eq 'System.DirectoryServices.DirectoryEntry' + } + + Mock 'New-Object' { New-Object 'fake_adsi_searcher' } ` + -ParameterFilter { + $TypeName -and + $TypeName -eq 'System.DirectoryServices.DirectorySearcher' + } + It 'Throws if name is to long' { { Get-ADSIComputer ` @@ -1259,20 +1287,41 @@ try } | Should -Throw } - It 'Return ADSI object' { - class fake_adsi_directoryentry { - [string] $Domain - [PSCredential] $Credential + It 'Returns ADSI object with ADSI path ' { + + { + Get-ADSIComputer ` + -Name 'IllegalName[<' ` + -Domain 'LDAP://Contoso.com' ` + -Credential $credential ` + -Verbose + } | Should -Not -BeNullOrEmpty + } + + It 'Returns ADSI object with domain name' { + + { + Get-ADSIComputer ` + -Name 'IllegalName[<' ` + -Domain 'Contoso.com' ` + -Credential $credential ` + -Verbose + } | Should -Not -BeNullOrEmpty + } + } + + Context 'DSC_Computer\Delete-ADSIObject' { + + It 'Deletes ADSI Object' { + class fake_psbase_object { + [void] DeleteTree(){ } } - class fake_adsi_searcher { - [string] $SearchRoot - [string] $Filter - [hashtable] FindOne( ){ - return @{ - path = 'LDAP://contoso.com/CN=fake-computer,OU=Computers,DC=contoso,DC=com' - } - } + class fake_adsi_directoryentry { + [string] $Domain + [string] $Username + [string] $password + [fake_psbase_object] $psbase } Mock 'New-Object' { New-Object 'fake_adsi_directoryentry' } ` @@ -1281,19 +1330,12 @@ try $TypeName -eq 'System.DirectoryServices.DirectoryEntry' } - Mock 'New-Object' { New-Object 'fake_adsi_searcher' } ` - -ParameterFilter { - $TypeName -and - $TypeName -eq 'System.DirectoryServices.DirectorySearcher' - } - { - Get-ADSIComputer ` - -Name 'IllegalName[<' ` - -Domain 'Contoso.com' ` + Delete-ADSIObject ` + -Path 'LDAP://contoso.com/CN=fake-computer,OU=Computers,DC=contoso,DC=com' ` -Credential $credential ` -Verbose - } | Should -Not -BeNullOrEmpty + } | Should -BeNullOrEmpty } } } From fd7a2c630ca83907c017528ba64e3776dd37b9d6 Mon Sep 17 00:00:00 2001 From: Nick Germany Date: Fri, 20 May 2022 11:10:35 -0400 Subject: [PATCH 11/39] Updating tests for adsi stuff --- tests/Unit/DSC_Computer.Tests.ps1 | 39 +++++++++++++++++-------------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/tests/Unit/DSC_Computer.Tests.ps1 b/tests/Unit/DSC_Computer.Tests.ps1 index a9cd086f..c2f3df40 100644 --- a/tests/Unit/DSC_Computer.Tests.ps1 +++ b/tests/Unit/DSC_Computer.Tests.ps1 @@ -1291,51 +1291,54 @@ try { Get-ADSIComputer ` - -Name 'IllegalName[<' ` + -Name 'LegalName' ` -Domain 'LDAP://Contoso.com' ` -Credential $credential ` -Verbose } | Should -Not -BeNullOrEmpty + Assert-MockCalled -CommandName New-Object -Exactly -Times 2 -Scope It } It 'Returns ADSI object with domain name' { { Get-ADSIComputer ` - -Name 'IllegalName[<' ` + -Name 'LegalName' ` -Domain 'Contoso.com' ` -Credential $credential ` -Verbose } | Should -Not -BeNullOrEmpty + Assert-MockCalled -CommandName New-Object -Exactly -Times 2 -Scope It } } Context 'DSC_Computer\Delete-ADSIObject' { - It 'Deletes ADSI Object' { - class fake_psbase_object { - [void] DeleteTree(){ } - } + class fake_psbase_object { + [void] DeleteTree(){ } + } - class fake_adsi_directoryentry { - [string] $Domain - [string] $Username - [string] $password - [fake_psbase_object] $psbase - } + class fake_adsi_directoryentry { + [string] $Domain + [string] $Username + [string] $password + [fake_psbase_object] $psbase + } - Mock 'New-Object' { New-Object 'fake_adsi_directoryentry' } ` - -ParameterFilter { - $TypeName -and - $TypeName -eq 'System.DirectoryServices.DirectoryEntry' - } + Mock 'New-Object' { New-Object 'fake_adsi_directoryentry' } ` + -ParameterFilter { + $TypeName -and + $TypeName -eq 'System.DirectoryServices.DirectoryEntry' + } + It 'Deletes ADSI Object' { { Delete-ADSIObject ` -Path 'LDAP://contoso.com/CN=fake-computer,OU=Computers,DC=contoso,DC=com' ` -Credential $credential ` -Verbose - } | Should -BeNullOrEmpty + } | Should -Not -Throw + Assert-MockCalled -CommandName New-Object -Exactly -Times 1 -Scope It } } } From e0d919d18ff098969ec31a7d5360891c0213c650 Mon Sep 17 00:00:00 2001 From: Nick Germany Date: Fri, 20 May 2022 11:16:37 -0400 Subject: [PATCH 12/39] Updating tests --- tests/Unit/DSC_Computer.Tests.ps1 | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/tests/Unit/DSC_Computer.Tests.ps1 b/tests/Unit/DSC_Computer.Tests.ps1 index c2f3df40..35ed6cbc 100644 --- a/tests/Unit/DSC_Computer.Tests.ps1 +++ b/tests/Unit/DSC_Computer.Tests.ps1 @@ -1289,25 +1289,23 @@ try It 'Returns ADSI object with ADSI path ' { - { - Get-ADSIComputer ` - -Name 'LegalName' ` - -Domain 'LDAP://Contoso.com' ` - -Credential $credential ` - -Verbose - } | Should -Not -BeNullOrEmpty + $obj = Get-ADSIComputer ` + -Name 'LegalName' ` + -Domain 'LDAP://Contoso.com' ` + -Credential $credential ` + -Verbose + $obj.path | Should -Be 'LDAP://contoso.com/CN=fake-computer,OU=Computers,DC=contoso,DC=com' Assert-MockCalled -CommandName New-Object -Exactly -Times 2 -Scope It } It 'Returns ADSI object with domain name' { - { - Get-ADSIComputer ` + $obj = Get-ADSIComputer ` -Name 'LegalName' ` -Domain 'Contoso.com' ` -Credential $credential ` -Verbose - } | Should -Not -BeNullOrEmpty + $obj.Path | Should -Be 'LDAP://contoso.com/CN=fake-computer,OU=Computers,DC=contoso,DC=com' Assert-MockCalled -CommandName New-Object -Exactly -Times 2 -Scope It } } From fc0889a988b028afa5db5b99876e0e9ec9546e44 Mon Sep 17 00:00:00 2001 From: Nick Germany Date: Fri, 20 May 2022 11:33:28 -0400 Subject: [PATCH 13/39] mock the psautomation type better --- tests/Unit/DSC_Computer.Tests.ps1 | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/Unit/DSC_Computer.Tests.ps1 b/tests/Unit/DSC_Computer.Tests.ps1 index 35ed6cbc..ce660b60 100644 --- a/tests/Unit/DSC_Computer.Tests.ps1 +++ b/tests/Unit/DSC_Computer.Tests.ps1 @@ -1321,6 +1321,11 @@ try [string] $Username [string] $password [fake_psbase_object] $psbase + + fake_adsi_directoryentry() { + $this.psbase = ` + New-Object 'fake_psbase_object' + } } Mock 'New-Object' { New-Object 'fake_adsi_directoryentry' } ` From b2c131a3eae655d72237982064c2e916d63933cd Mon Sep 17 00:00:00 2001 From: Nick Germany Date: Fri, 20 May 2022 11:43:37 -0400 Subject: [PATCH 14/39] mock the psbase obj type with property --- tests/Unit/DSC_Computer.Tests.ps1 | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/Unit/DSC_Computer.Tests.ps1 b/tests/Unit/DSC_Computer.Tests.ps1 index ce660b60..c0d56d1f 100644 --- a/tests/Unit/DSC_Computer.Tests.ps1 +++ b/tests/Unit/DSC_Computer.Tests.ps1 @@ -1313,6 +1313,7 @@ try Context 'DSC_Computer\Delete-ADSIObject' { class fake_psbase_object { + [string] $name [void] DeleteTree(){ } } From 987fc1b9890deaa5fbaa2f40ac609226ac0c6e43 Mon Sep 17 00:00:00 2001 From: Nick Germany Date: Fri, 20 May 2022 12:05:47 -0400 Subject: [PATCH 15/39] Removing unnecessary property of fake_psbase_object --- tests/Unit/DSC_Computer.Tests.ps1 | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/Unit/DSC_Computer.Tests.ps1 b/tests/Unit/DSC_Computer.Tests.ps1 index c0d56d1f..ce660b60 100644 --- a/tests/Unit/DSC_Computer.Tests.ps1 +++ b/tests/Unit/DSC_Computer.Tests.ps1 @@ -1313,7 +1313,6 @@ try Context 'DSC_Computer\Delete-ADSIObject' { class fake_psbase_object { - [string] $name [void] DeleteTree(){ } } From b807a0292c4281d5453218ecf33fcb6568da0591 Mon Sep 17 00:00:00 2001 From: Nick Germany Date: Fri, 20 May 2022 13:09:32 -0400 Subject: [PATCH 16/39] moving scope --- tests/Unit/DSC_Computer.Tests.ps1 | 33 ++++++++++++++++--------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/tests/Unit/DSC_Computer.Tests.ps1 b/tests/Unit/DSC_Computer.Tests.ps1 index ce660b60..56333dc8 100644 --- a/tests/Unit/DSC_Computer.Tests.ps1 +++ b/tests/Unit/DSC_Computer.Tests.ps1 @@ -45,6 +45,23 @@ try 'name' } + # These classes are used in Delete-ADSIObject + class fake_psbase_object { + [void] DeleteTree(){ } + } + + class fake_adsi_directoryentry { + [string] $Domain + [string] $Username + [string] $password + [fake_psbase_object] $psbase + + fake_adsi_directoryentry() { + $this.psbase = ` + New-Object 'fake_psbase_object' + } + } + Context 'DSC_Computer\Test-TargetResource' { Mock -CommandName Get-WMIObject -MockWith { [PSCustomObject] @{ @@ -1312,22 +1329,6 @@ try Context 'DSC_Computer\Delete-ADSIObject' { - class fake_psbase_object { - [void] DeleteTree(){ } - } - - class fake_adsi_directoryentry { - [string] $Domain - [string] $Username - [string] $password - [fake_psbase_object] $psbase - - fake_adsi_directoryentry() { - $this.psbase = ` - New-Object 'fake_psbase_object' - } - } - Mock 'New-Object' { New-Object 'fake_adsi_directoryentry' } ` -ParameterFilter { $TypeName -and From 7fb77f72fc57e33f7e6a99682095757d8b9dfed0 Mon Sep 17 00:00:00 2001 From: Nick Germany Date: Fri, 20 May 2022 13:28:54 -0400 Subject: [PATCH 17/39] does this work --- tests/Unit/DSC_Computer.Tests.ps1 | 32 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/tests/Unit/DSC_Computer.Tests.ps1 b/tests/Unit/DSC_Computer.Tests.ps1 index 56333dc8..35a7b305 100644 --- a/tests/Unit/DSC_Computer.Tests.ps1 +++ b/tests/Unit/DSC_Computer.Tests.ps1 @@ -45,23 +45,6 @@ try 'name' } - # These classes are used in Delete-ADSIObject - class fake_psbase_object { - [void] DeleteTree(){ } - } - - class fake_adsi_directoryentry { - [string] $Domain - [string] $Username - [string] $password - [fake_psbase_object] $psbase - - fake_adsi_directoryentry() { - $this.psbase = ` - New-Object 'fake_psbase_object' - } - } - Context 'DSC_Computer\Test-TargetResource' { Mock -CommandName Get-WMIObject -MockWith { [PSCustomObject] @{ @@ -1329,6 +1312,21 @@ try Context 'DSC_Computer\Delete-ADSIObject' { + + + class fake_adsi_directoryentry { + [string] $Domain + [string] $Username + [string] $password + $psbase + + fake_adsi_directoryentry() { + $this.psbase = class fake_psbase { + [void] DeleteTree(){ } + } + } + } + Mock 'New-Object' { New-Object 'fake_adsi_directoryentry' } ` -ParameterFilter { $TypeName -and From 3d658b632c13629011a0de1155865a32cd503c1d Mon Sep 17 00:00:00 2001 From: Nick Germany Date: Fri, 20 May 2022 13:47:58 -0400 Subject: [PATCH 18/39] no quotes --- tests/Unit/DSC_Computer.Tests.ps1 | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/tests/Unit/DSC_Computer.Tests.ps1 b/tests/Unit/DSC_Computer.Tests.ps1 index 35a7b305..2e6e7117 100644 --- a/tests/Unit/DSC_Computer.Tests.ps1 +++ b/tests/Unit/DSC_Computer.Tests.ps1 @@ -1312,18 +1312,19 @@ try Context 'DSC_Computer\Delete-ADSIObject' { - + class fake_psbase { + [void] DeleteTree(){ } + } class fake_adsi_directoryentry { [string] $Domain [string] $Username [string] $password - $psbase + [fake_psbase] $psbase fake_adsi_directoryentry() { - $this.psbase = class fake_psbase { - [void] DeleteTree(){ } - } + $this.psbase = ` + New-Object -TypeName fake_psbase } } From 498639d3e48df61ae2eb06facb8e4ac1616b0343 Mon Sep 17 00:00:00 2001 From: Nick Germany Date: Fri, 20 May 2022 14:26:16 -0400 Subject: [PATCH 19/39] Simplify --- source/DSCResources/DSC_Computer/DSC_Computer.psm1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/DSCResources/DSC_Computer/DSC_Computer.psm1 b/source/DSCResources/DSC_Computer/DSC_Computer.psm1 index eb596a4d..6097b4b8 100644 --- a/source/DSCResources/DSC_Computer/DSC_Computer.psm1 +++ b/source/DSCResources/DSC_Computer/DSC_Computer.psm1 @@ -742,7 +742,7 @@ function Delete-ADSIObject -ArgumentList $Path, $($Credential.UserName), $($Credential.GetNetworkCredential().password) ` -ErrorAction Stop - $adsiObj.psbase.DeleteTree() + $adsiObj.DeleteTree() } catch { From 5680604cd352c5bfe6409b48ef7909316d4fc109 Mon Sep 17 00:00:00 2001 From: Nick Germany Date: Fri, 20 May 2022 14:34:28 -0400 Subject: [PATCH 20/39] no need for nested class --- tests/Unit/DSC_Computer.Tests.ps1 | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/tests/Unit/DSC_Computer.Tests.ps1 b/tests/Unit/DSC_Computer.Tests.ps1 index 2e6e7117..fe02a824 100644 --- a/tests/Unit/DSC_Computer.Tests.ps1 +++ b/tests/Unit/DSC_Computer.Tests.ps1 @@ -1312,20 +1312,11 @@ try Context 'DSC_Computer\Delete-ADSIObject' { - class fake_psbase { - [void] DeleteTree(){ } - } - class fake_adsi_directoryentry { [string] $Domain [string] $Username [string] $password - [fake_psbase] $psbase - - fake_adsi_directoryentry() { - $this.psbase = ` - New-Object -TypeName fake_psbase - } + [void] DeleteTree(){ } } Mock 'New-Object' { New-Object 'fake_adsi_directoryentry' } ` From 3c0722354f8fc055769af5df83b3aa2404eddf9b Mon Sep 17 00:00:00 2001 From: Nick Germany Date: Fri, 20 May 2022 15:32:02 -0400 Subject: [PATCH 21/39] Adding mocks for exceptions --- tests/Unit/DSC_Computer.Tests.ps1 | 33 +++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/tests/Unit/DSC_Computer.Tests.ps1 b/tests/Unit/DSC_Computer.Tests.ps1 index fe02a824..74765c50 100644 --- a/tests/Unit/DSC_Computer.Tests.ps1 +++ b/tests/Unit/DSC_Computer.Tests.ps1 @@ -1308,6 +1308,22 @@ try $obj.Path | Should -Be 'LDAP://contoso.com/CN=fake-computer,OU=Computers,DC=contoso,DC=com' Assert-MockCalled -CommandName New-Object -Exactly -Times 2 -Scope It } + + It 'Should throw if Credential is incorrect' { + Mock 'New-Object' { throw } ` + -ParameterFilter { + $TypeName -and + $TypeName -eq 'System.DirectoryServices.DirectoryEntry' + } + + { + Get-ADSIComputer ` + -Name 'LegalName' ` + -Domain 'Contoso.com' ` + -Credential $credential` + -Verbose + } | Should -Throw + Assert-MockCalled -CommandName New-Object -Exactly -Times 2 -Scope It } Context 'DSC_Computer\Delete-ADSIObject' { @@ -1334,6 +1350,23 @@ try } | Should -Not -Throw Assert-MockCalled -CommandName New-Object -Exactly -Times 1 -Scope It } + + It 'Should throw if Credential is incorrect' { + Mock 'New-Object' { throw } ` + -ParameterFilter { + $TypeName -and + $TypeName -eq 'System.DirectoryServices.DirectoryEntry' + } + + { + Delete-ADSIObject ` + -Path 'LDAP://contoso.com/CN=fake-computer,OU=Computers,DC=contoso,DC=com' ` + -Domain 'Contoso.com' ` + -Credential $credential` + -Verbose + } | Should -Throw + Assert-MockCalled -CommandName New-Object -Exactly -Times 1 -Scope It + } } } } From cb6c1d08bff58abbb894efc497271e1537757ad7 Mon Sep 17 00:00:00 2001 From: Nick Germany Date: Fri, 20 May 2022 15:34:15 -0400 Subject: [PATCH 22/39] Adding validation for path in delete-adsiobject --- source/DSCResources/DSC_Computer/DSC_Computer.psm1 | 1 + tests/Unit/DSC_Computer.Tests.ps1 | 11 ++++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/source/DSCResources/DSC_Computer/DSC_Computer.psm1 b/source/DSCResources/DSC_Computer/DSC_Computer.psm1 index 6097b4b8..f0ccb39a 100644 --- a/source/DSCResources/DSC_Computer/DSC_Computer.psm1 +++ b/source/DSCResources/DSC_Computer/DSC_Computer.psm1 @@ -728,6 +728,7 @@ function Delete-ADSIObject param ( [Parameter(Mandatory = $true)] + [ValidateScript( { $_ -imatch "LDAP://*" })] [System.String] $Path, diff --git a/tests/Unit/DSC_Computer.Tests.ps1 b/tests/Unit/DSC_Computer.Tests.ps1 index 74765c50..3308482d 100644 --- a/tests/Unit/DSC_Computer.Tests.ps1 +++ b/tests/Unit/DSC_Computer.Tests.ps1 @@ -1361,13 +1361,22 @@ try { Delete-ADSIObject ` -Path 'LDAP://contoso.com/CN=fake-computer,OU=Computers,DC=contoso,DC=com' ` - -Domain 'Contoso.com' ` -Credential $credential` -Verbose } | Should -Throw Assert-MockCalled -CommandName New-Object -Exactly -Times 1 -Scope It } + + It 'Should throw if path does not begin with LDAP://' { + { + Delete-ADSIObject ` + -Path 'contoso.com/CN=fake-computer,OU=Computers,DC=contoso,DC=com' ` + -Credential $credential` + -Verbose + } | Should -Throw + } } + } } } From 729e72ad3ee0d6616d8d40577507b82653e54139 Mon Sep 17 00:00:00 2001 From: Nick Germany Date: Fri, 20 May 2022 15:50:08 -0400 Subject: [PATCH 23/39] updating tests --- tests/Unit/DSC_Computer.Tests.ps1 | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/Unit/DSC_Computer.Tests.ps1 b/tests/Unit/DSC_Computer.Tests.ps1 index 3308482d..3c8677d7 100644 --- a/tests/Unit/DSC_Computer.Tests.ps1 +++ b/tests/Unit/DSC_Computer.Tests.ps1 @@ -1324,6 +1324,7 @@ try -Verbose } | Should -Throw Assert-MockCalled -CommandName New-Object -Exactly -Times 2 -Scope It + } } Context 'DSC_Computer\Delete-ADSIObject' { @@ -1351,6 +1352,15 @@ try Assert-MockCalled -CommandName New-Object -Exactly -Times 1 -Scope It } + It 'Should throw if path does not begin with LDAP://' { + { + Delete-ADSIObject ` + -Path 'contoso.com/CN=fake-computer,OU=Computers,DC=contoso,DC=com' ` + -Credential $credential` + -Verbose + } | Should -Throw + } + It 'Should throw if Credential is incorrect' { Mock 'New-Object' { throw } ` -ParameterFilter { @@ -1365,18 +1375,8 @@ try -Verbose } | Should -Throw Assert-MockCalled -CommandName New-Object -Exactly -Times 1 -Scope It - } - - It 'Should throw if path does not begin with LDAP://' { - { - Delete-ADSIObject ` - -Path 'contoso.com/CN=fake-computer,OU=Computers,DC=contoso,DC=com' ` - -Credential $credential` - -Verbose - } | Should -Throw } } - } } } From 8164665ec2d54bfa793f67aeb4d2281120bf0135 Mon Sep 17 00:00:00 2001 From: Nick Germany Date: Fri, 20 May 2022 16:07:53 -0400 Subject: [PATCH 24/39] rescope new-object --- tests/Unit/DSC_Computer.Tests.ps1 | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/tests/Unit/DSC_Computer.Tests.ps1 b/tests/Unit/DSC_Computer.Tests.ps1 index 3c8677d7..948c914c 100644 --- a/tests/Unit/DSC_Computer.Tests.ps1 +++ b/tests/Unit/DSC_Computer.Tests.ps1 @@ -1255,12 +1255,6 @@ try } } - Mock 'New-Object' { New-Object 'fake_adsi_directoryentry' } ` - -ParameterFilter { - $TypeName -and - $TypeName -eq 'System.DirectoryServices.DirectoryEntry' - } - Mock 'New-Object' { New-Object 'fake_adsi_searcher' } ` -ParameterFilter { $TypeName -and @@ -1289,6 +1283,12 @@ try It 'Returns ADSI object with ADSI path ' { + Mock 'New-Object' { New-Object 'fake_adsi_directoryentry' } ` + -ParameterFilter { + $TypeName -and + $TypeName -eq 'System.DirectoryServices.DirectoryEntry' + } + $obj = Get-ADSIComputer ` -Name 'LegalName' ` -Domain 'LDAP://Contoso.com' ` @@ -1300,6 +1300,12 @@ try It 'Returns ADSI object with domain name' { + Mock 'New-Object' { New-Object 'fake_adsi_directoryentry' } ` + -ParameterFilter { + $TypeName -and + $TypeName -eq 'System.DirectoryServices.DirectoryEntry' + } + $obj = Get-ADSIComputer ` -Name 'LegalName' ` -Domain 'Contoso.com' ` @@ -1336,13 +1342,13 @@ try [void] DeleteTree(){ } } - Mock 'New-Object' { New-Object 'fake_adsi_directoryentry' } ` + It 'Deletes ADSI Object' { + Mock 'New-Object' { New-Object 'fake_adsi_directoryentry' } ` -ParameterFilter { $TypeName -and $TypeName -eq 'System.DirectoryServices.DirectoryEntry' } - It 'Deletes ADSI Object' { { Delete-ADSIObject ` -Path 'LDAP://contoso.com/CN=fake-computer,OU=Computers,DC=contoso,DC=com' ` From 77255ee49e83fc98c9840392f700e767f0466b4a Mon Sep 17 00:00:00 2001 From: Nick Germany Date: Fri, 20 May 2022 16:15:53 -0400 Subject: [PATCH 25/39] remove throw for write-error --- tests/Unit/DSC_Computer.Tests.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Unit/DSC_Computer.Tests.ps1 b/tests/Unit/DSC_Computer.Tests.ps1 index 948c914c..e08f1f4c 100644 --- a/tests/Unit/DSC_Computer.Tests.ps1 +++ b/tests/Unit/DSC_Computer.Tests.ps1 @@ -1316,7 +1316,7 @@ try } It 'Should throw if Credential is incorrect' { - Mock 'New-Object' { throw } ` + Mock 'New-Object' { Write-Error -message "error" } ` -ParameterFilter { $TypeName -and $TypeName -eq 'System.DirectoryServices.DirectoryEntry' @@ -1368,7 +1368,7 @@ try } It 'Should throw if Credential is incorrect' { - Mock 'New-Object' { throw } ` + Mock 'New-Object' { Write-Error -message "error" } ` -ParameterFilter { $TypeName -and $TypeName -eq 'System.DirectoryServices.DirectoryEntry' From 8e9af1eeb21a3aad4efe61a800716f5fc4577287 Mon Sep 17 00:00:00 2001 From: Nick Germany Date: Fri, 20 May 2022 16:25:53 -0400 Subject: [PATCH 26/39] add space between credential and backtick --- tests/Unit/DSC_Computer.Tests.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Unit/DSC_Computer.Tests.ps1 b/tests/Unit/DSC_Computer.Tests.ps1 index e08f1f4c..7a155dfa 100644 --- a/tests/Unit/DSC_Computer.Tests.ps1 +++ b/tests/Unit/DSC_Computer.Tests.ps1 @@ -1326,7 +1326,7 @@ try Get-ADSIComputer ` -Name 'LegalName' ` -Domain 'Contoso.com' ` - -Credential $credential` + -Credential $credential ` -Verbose } | Should -Throw Assert-MockCalled -CommandName New-Object -Exactly -Times 2 -Scope It From f14498f592076a2dd27424c1e9f409547f3d68b9 Mon Sep 17 00:00:00 2001 From: Nick Germany Date: Fri, 20 May 2022 16:33:25 -0400 Subject: [PATCH 27/39] add space between credential and backtick --- tests/Unit/DSC_Computer.Tests.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Unit/DSC_Computer.Tests.ps1 b/tests/Unit/DSC_Computer.Tests.ps1 index 7a155dfa..d3495e10 100644 --- a/tests/Unit/DSC_Computer.Tests.ps1 +++ b/tests/Unit/DSC_Computer.Tests.ps1 @@ -1377,7 +1377,7 @@ try { Delete-ADSIObject ` -Path 'LDAP://contoso.com/CN=fake-computer,OU=Computers,DC=contoso,DC=com' ` - -Credential $credential` + -Credential $credential ` -Verbose } | Should -Throw Assert-MockCalled -CommandName New-Object -Exactly -Times 1 -Scope It From a9a79b11ad4aa3634987b0fd2369fe27ce011814 Mon Sep 17 00:00:00 2001 From: Nick Germany Date: Sat, 21 May 2022 09:08:30 -0400 Subject: [PATCH 28/39] use splat rather than backtick --- .../DSC_Computer/DSC_Computer.psm1 | 26 ++++++++++++++----- 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/source/DSCResources/DSC_Computer/DSC_Computer.psm1 b/source/DSCResources/DSC_Computer/DSC_Computer.psm1 index f0ccb39a..30fcb447 100644 --- a/source/DSCResources/DSC_Computer/DSC_Computer.psm1 +++ b/source/DSCResources/DSC_Computer/DSC_Computer.psm1 @@ -699,9 +699,16 @@ function Get-ADSIComputer try { - $searchRoot = New-Object -TypeName System.DirectoryServices.DirectoryEntry ` - -ArgumentList $DomainName, $($Credential.UserName), $($Credential.GetNetworkCredential().password) ` - -ErrorAction Stop + $params = @{ + TypeName = 'System.DirectoryServices.DirectoryEntry' + ArgumentList = @( + $DomainName, + $($Credential.UserName), + $($Credential.GetNetworkCredential().password) + ) + ErrorAction = 'Stop' + } + $searchRoot = New-Object @params } catch { @@ -739,9 +746,16 @@ function Delete-ADSIObject try { - $adsiObj = New-Object -TypeName System.DirectoryServices.DirectoryEntry ` - -ArgumentList $Path, $($Credential.UserName), $($Credential.GetNetworkCredential().password) ` - -ErrorAction Stop + $params = @{ + TypeName = 'System.DirectoryServices.DirectoryEntry' + ArgumentList = @( + $DomainName, + $($Credential.UserName), + $($Credential.GetNetworkCredential().password) + ) + ErrorAction = 'Stop' + } + $adsiObj = New-Object @params $adsiObj.DeleteTree() } From 9f7919fd13f600a4a962a4c1a8ab59419184f991 Mon Sep 17 00:00:00 2001 From: Nick Germany Date: Mon, 23 May 2022 11:04:13 -0400 Subject: [PATCH 29/39] fixing blocks from merge conflict --- tests/Unit/DSC_Computer.Tests.ps1 | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/Unit/DSC_Computer.Tests.ps1 b/tests/Unit/DSC_Computer.Tests.ps1 index d63cdad3..87e4cf05 100644 --- a/tests/Unit/DSC_Computer.Tests.ps1 +++ b/tests/Unit/DSC_Computer.Tests.ps1 @@ -1409,6 +1409,9 @@ try -Verbose } | Should -Throw Assert-MockCalled -CommandName New-Object -Exactly -Times 1 -Scope It + } + } + Context 'DSC_Computer\Assert-ResourceProperty' { It 'Should throw if PasswordPass and UnsecuredJoin is present but credential username is not null' { $errorRecord = Get-InvalidArgumentRecord ` From 1c593528711704e788a24084c254cfd462343011 Mon Sep 17 00:00:00 2001 From: Nick Germany Date: Mon, 30 May 2022 20:32:49 -0400 Subject: [PATCH 30/39] Rework Get-AdsiComputer to throw correctly --- .../DSC_Computer/DSC_Computer.psm1 | 36 +++++++++---------- .../en-US/DSC_Computer.strings.psd1 | 1 + 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/source/DSCResources/DSC_Computer/DSC_Computer.psm1 b/source/DSCResources/DSC_Computer/DSC_Computer.psm1 index d8c5be80..74dfcc5f 100644 --- a/source/DSCResources/DSC_Computer/DSC_Computer.psm1 +++ b/source/DSCResources/DSC_Computer/DSC_Computer.psm1 @@ -694,13 +694,13 @@ function Get-LogonServer Returns an ADSI Computer Object. .PARAMETER Name - Name of the computer to search for in the given domain + Name of the computer to search for in the given domain. .PARAMETER Domain - Domain to search + Domain to search. .PARAMETER Credential - Credential to search domain with + Credential to search domain with. #> function Get-ADSIComputer { @@ -725,31 +725,31 @@ function Get-ADSIComputer $searcher = New-Object -TypeName System.DirectoryServices.DirectorySearcher $searcher.Filter = "(&(objectCategory=computer)(objectClass=computer)(cn=$Name))" - if ( $DomainName -notlike "LDAP://*") + if ($DomainName -notlike "LDAP://*") { $DomainName = "LDAP://$DomainName" } + $params = @{ + TypeName = 'System.DirectoryServices.DirectoryEntry' + ArgumentList = @( + $DomainName, + $Credential.UserName, + $Credential.GetNetworkCredential().password + ) + ErrorAction = 'Stop' + } + $searchRoot = New-Object @params + $searcher.SearchRoot = $searchRoot + try { - $params = @{ - TypeName = 'System.DirectoryServices.DirectoryEntry' - ArgumentList = @( - $DomainName, - $($Credential.UserName), - $($Credential.GetNetworkCredential().password) - ) - ErrorAction = 'Stop' - } - $searchRoot = New-Object @params + return $searcher.FindOne() } catch { - New-InvalidOperationException -Message $_.Exception.Message -ErrorRecord $_ + New-InvalidOperationException -Message ($script:localizedData.InvalidUserNameorPassword -f $Credential.Username) } - $searcher.SearchRoot = $searchRoot - - return $searcher.FindOne() } <# diff --git a/source/DSCResources/DSC_Computer/en-US/DSC_Computer.strings.psd1 b/source/DSCResources/DSC_Computer/en-US/DSC_Computer.strings.psd1 index 22903a87..944ca25a 100644 --- a/source/DSCResources/DSC_Computer/en-US/DSC_Computer.strings.psd1 +++ b/source/DSCResources/DSC_Computer/en-US/DSC_Computer.strings.psd1 @@ -19,4 +19,5 @@ ConvertFrom-StringData @' DeletedExistingComputerObject = Deleted existing computer object with name '{0}' at path '{1}'. InvalidOptionPasswordPassUnsecuredJoin = Domain Join option 'PasswordPass' may not be specified if 'UnsecuredJoin' is specified. InvalidOptionCredentialUnsecuredJoinNullUsername = 'Credential' username must be null if 'UnsecuredJoin' is specified. + InvalidUserNameorPassword = Password specified for UserName {0} is invalid. '@ From 2e236ee35ead1ab557e1fc02391b817993a68849 Mon Sep 17 00:00:00 2001 From: Nick Germany Date: Mon, 30 May 2022 20:54:49 -0400 Subject: [PATCH 31/39] resolving comments from PR --- .../DSC_Computer/DSC_Computer.psm1 | 10 +-- tests/Unit/DSC_Computer.Tests.ps1 | 62 ++++++++++--------- 2 files changed, 39 insertions(+), 33 deletions(-) diff --git a/source/DSCResources/DSC_Computer/DSC_Computer.psm1 b/source/DSCResources/DSC_Computer/DSC_Computer.psm1 index 74dfcc5f..092bbcf8 100644 --- a/source/DSCResources/DSC_Computer/DSC_Computer.psm1 +++ b/source/DSCResources/DSC_Computer/DSC_Computer.psm1 @@ -757,10 +757,10 @@ function Get-ADSIComputer Deletes an ADSI DirectoryEntry Object. .PARAMETER Path - Path to Object to delete + Path to Object to delete. .PARAMETER Credential - Credential to authenticate to the domain + Credential to authenticate to the domain. #> function Delete-ADSIObject { @@ -783,9 +783,9 @@ function Delete-ADSIObject TypeName = 'System.DirectoryServices.DirectoryEntry' ArgumentList = @( $DomainName, - $($Credential.UserName), - $($Credential.GetNetworkCredential().password) - ) + $Credential.UserName + $Credential.GetNetworkCredential().password + ) ErrorAction = 'Stop' } $adsiObj = New-Object @params diff --git a/tests/Unit/DSC_Computer.Tests.ps1 b/tests/Unit/DSC_Computer.Tests.ps1 index 87e4cf05..f213dd50 100644 --- a/tests/Unit/DSC_Computer.Tests.ps1 +++ b/tests/Unit/DSC_Computer.Tests.ps1 @@ -1283,39 +1283,43 @@ try } } - Mock 'New-Object' { New-Object 'fake_adsi_searcher' } ` + Mock -CommandName New-Object -MockWith { + New-Object -TypeName 'fake_adsi_directoryentry' + } ` -ParameterFilter { $TypeName -and - $TypeName -eq 'System.DirectoryServices.DirectorySearcher' + $TypeName -eq 'System.DirectoryServices.DirectoryEntry' } - It 'Throws if name is to long' { + It 'Should throw the expected exception if the name is to long' { { - Get-ADSIComputer ` + $error = Get-ADSIComputer ` -Name 'ThisNameIsTooLong' ` -Domain 'Contoso.com' ` -Credential $credential ` -Verbose - } | Should -Throw + $error + } | Should -Throw "Test-ParamValidator: Cannot validate argument on parameter 'Name'. The character length of the 17 argument is too long. Shorten the character length of the argument so it is fewer than or equal to `"15`" characters, and then try the command again." } - It 'Throws if name contains illegal characters' { + It 'Should throws if the expected exception if the name contains illegal characters' { { Get-ADSIComputer ` -Name 'IllegalName[<' ` -Domain 'Contoso.com' ` -Credential $credential ` -Verbose - } | Should -Throw + } | Should -Throw "Test-ParamValidator: Cannot validate argument on parameter 'Name'. The `" $_ -inotmatch '[\/\\:*?`"<>|]' `" validation script for the argument with value `"IllegalName[<`" did not return a result of True. Determine why the validation script failed, and then try the command again." } It 'Returns ADSI object with ADSI path ' { - - Mock 'New-Object' { New-Object 'fake_adsi_directoryentry' } ` - -ParameterFilter { - $TypeName -and - $TypeName -eq 'System.DirectoryServices.DirectoryEntry' - } + Mock -CommandName New-Object -MockWith { + New-Object -TypeName 'fake_adsi_directoryentry' + } ` + -ParameterFilter { + $TypeName -and + $TypeName -eq 'System.DirectoryServices.DirectoryEntry' + } $obj = Get-ADSIComputer ` -Name 'LegalName' ` @@ -1328,11 +1332,13 @@ try It 'Returns ADSI object with domain name' { - Mock 'New-Object' { New-Object 'fake_adsi_directoryentry' } ` - -ParameterFilter { - $TypeName -and - $TypeName -eq 'System.DirectoryServices.DirectoryEntry' - } + Mock -CommandName New-Object -MockWith { + New-Object -TypeName 'fake_adsi_directoryentry' + } ` + -ParameterFilter { + $TypeName -and + $TypeName -eq 'System.DirectoryServices.DirectoryEntry' + } $obj = Get-ADSIComputer ` -Name 'LegalName' ` @@ -1343,8 +1349,8 @@ try Assert-MockCalled -CommandName New-Object -Exactly -Times 2 -Scope It } - It 'Should throw if Credential is incorrect' { - Mock 'New-Object' { Write-Error -message "error" } ` + It 'Should throw the expected exception if Credential is incorrect' { + Mock 'New-Object' { Write-Error -message "Invalid Credentials" } ` -ParameterFilter { $TypeName -and $TypeName -eq 'System.DirectoryServices.DirectoryEntry' @@ -1356,7 +1362,7 @@ try -Domain 'Contoso.com' ` -Credential $credential ` -Verbose - } | Should -Throw + } | Should -Throw "Invalid Credentials" Assert-MockCalled -CommandName New-Object -Exactly -Times 2 -Scope It } } @@ -1370,7 +1376,7 @@ try [void] DeleteTree(){ } } - It 'Deletes ADSI Object' { + It 'Should delete the ADSI Object' { Mock 'New-Object' { New-Object 'fake_adsi_directoryentry' } ` -ParameterFilter { $TypeName -and @@ -1389,14 +1395,14 @@ try It 'Should throw if path does not begin with LDAP://' { { Delete-ADSIObject ` - -Path 'contoso.com/CN=fake-computer,OU=Computers,DC=contoso,DC=com' ` - -Credential $credential` - -Verbose - } | Should -Throw + -Path 'contoso.com/CN=fake-computer,OU=Computers,DC=contoso,DC=com' ` + -Credential $credential` + -Verbose + } | Should -Throw "Test-ParamValidator: Cannot validate argument on parameter 'Path'. The `" $_ -imatch `"LDAP://*`" `" validation script for the argument with value `"contoso.com/CN=fake-computer,OU=Computers,DC=contoso,DC=com`" did not return a result of True. Determine why the validation script failed, and then try the command again." } It 'Should throw if Credential is incorrect' { - Mock 'New-Object' { Write-Error -message "error" } ` + Mock 'New-Object' { Write-Error -message "Invalid Credential" } ` -ParameterFilter { $TypeName -and $TypeName -eq 'System.DirectoryServices.DirectoryEntry' @@ -1407,7 +1413,7 @@ try -Path 'LDAP://contoso.com/CN=fake-computer,OU=Computers,DC=contoso,DC=com' ` -Credential $credential ` -Verbose - } | Should -Throw + } | Should -Throw "Invalid Credential" Assert-MockCalled -CommandName New-Object -Exactly -Times 1 -Scope It } } From 7ee2284fbc8f39915785e044b8affacd87d4c7e3 Mon Sep 17 00:00:00 2001 From: Nick Germany Date: Mon, 30 May 2022 21:10:31 -0400 Subject: [PATCH 32/39] Fixing tests --- tests/Unit/DSC_Computer.Tests.ps1 | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/tests/Unit/DSC_Computer.Tests.ps1 b/tests/Unit/DSC_Computer.Tests.ps1 index f213dd50..e9a97fe6 100644 --- a/tests/Unit/DSC_Computer.Tests.ps1 +++ b/tests/Unit/DSC_Computer.Tests.ps1 @@ -1284,13 +1284,14 @@ try } Mock -CommandName New-Object -MockWith { - New-Object -TypeName 'fake_adsi_directoryentry' + New-Object -TypeName 'fake_adsi_searcher' } ` -ParameterFilter { $TypeName -and - $TypeName -eq 'System.DirectoryServices.DirectoryEntry' + $TypeName -eq 'System.DirectoryServices.DirectorySearcher' } + $message = "Cannot validate argument on parameter 'Name'. The character length of the 17 argument is too long. Shorten the character length of the argument so it is fewer than or equal to "15" characters, and then try the command again." It 'Should throw the expected exception if the name is to long' { { $error = Get-ADSIComputer ` @@ -1299,7 +1300,7 @@ try -Credential $credential ` -Verbose $error - } | Should -Throw "Test-ParamValidator: Cannot validate argument on parameter 'Name'. The character length of the 17 argument is too long. Shorten the character length of the argument so it is fewer than or equal to `"15`" characters, and then try the command again." + } | Should -Throw $message } It 'Should throws if the expected exception if the name contains illegal characters' { @@ -1309,7 +1310,7 @@ try -Domain 'Contoso.com' ` -Credential $credential ` -Verbose - } | Should -Throw "Test-ParamValidator: Cannot validate argument on parameter 'Name'. The `" $_ -inotmatch '[\/\\:*?`"<>|]' `" validation script for the argument with value `"IllegalName[<`" did not return a result of True. Determine why the validation script failed, and then try the command again." + } | Should -Throw "Cannot validate argument on parameter 'Name'. The `" $_ -inotmatch '[\/\\:*?`"<>|]' `" validation script for the argument with value `"IllegalName[<`" did not return a result of True. Determine why the validation script failed, and then try the command again." } It 'Returns ADSI object with ADSI path ' { @@ -1333,11 +1334,11 @@ try It 'Returns ADSI object with domain name' { Mock -CommandName New-Object -MockWith { - New-Object -TypeName 'fake_adsi_directoryentry' + New-Object -TypeName 'fake_adsi_searcher' } ` -ParameterFilter { $TypeName -and - $TypeName -eq 'System.DirectoryServices.DirectoryEntry' + $TypeName -eq 'System.DirectoryServices.DirectorySearcher' } $obj = Get-ADSIComputer ` @@ -1398,7 +1399,7 @@ try -Path 'contoso.com/CN=fake-computer,OU=Computers,DC=contoso,DC=com' ` -Credential $credential` -Verbose - } | Should -Throw "Test-ParamValidator: Cannot validate argument on parameter 'Path'. The `" $_ -imatch `"LDAP://*`" `" validation script for the argument with value `"contoso.com/CN=fake-computer,OU=Computers,DC=contoso,DC=com`" did not return a result of True. Determine why the validation script failed, and then try the command again." + } | Should -Throw "Cannot validate argument on parameter 'Path'. The `" $_ -imatch `"LDAP://*`" `" validation script for the argument with value `"contoso.com/CN=fake-computer,OU=Computers,DC=contoso,DC=com`" did not return a result of True. Determine why the validation script failed, and then try the command again." } It 'Should throw if Credential is incorrect' { From 1f68c52e45808c2a8a5acae9e4f96e113f96cd98 Mon Sep 17 00:00:00 2001 From: Nick Germany Date: Mon, 30 May 2022 21:18:23 -0400 Subject: [PATCH 33/39] Fixing error message quoting --- tests/Unit/DSC_Computer.Tests.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Unit/DSC_Computer.Tests.ps1 b/tests/Unit/DSC_Computer.Tests.ps1 index e9a97fe6..c0193713 100644 --- a/tests/Unit/DSC_Computer.Tests.ps1 +++ b/tests/Unit/DSC_Computer.Tests.ps1 @@ -1291,7 +1291,7 @@ try $TypeName -eq 'System.DirectoryServices.DirectorySearcher' } - $message = "Cannot validate argument on parameter 'Name'. The character length of the 17 argument is too long. Shorten the character length of the argument so it is fewer than or equal to "15" characters, and then try the command again." + $message = "Cannot validate argument on parameter 'Name'. The character length of the 17 argument is too long. Shorten the character length of the argument so it is fewer than or equal to `"15`" characters, and then try the command again." It 'Should throw the expected exception if the name is to long' { { $error = Get-ADSIComputer ` From 9125fd71511310c423b4b5454698c54a855bc51d Mon Sep 17 00:00:00 2001 From: Nick Germany Date: Mon, 30 May 2022 21:53:57 -0400 Subject: [PATCH 34/39] Fixing error messages --- tests/Unit/DSC_Computer.Tests.ps1 | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/Unit/DSC_Computer.Tests.ps1 b/tests/Unit/DSC_Computer.Tests.ps1 index c0193713..ea140095 100644 --- a/tests/Unit/DSC_Computer.Tests.ps1 +++ b/tests/Unit/DSC_Computer.Tests.ps1 @@ -1303,6 +1303,7 @@ try } | Should -Throw $message } + $message = "Cannot validate argument on parameter 'Name'. The `" $_ -inotmatch '[\/\\:*?`"<>|]' `" validation script for the argument with value `"IllegalName[<`" did not return a result of True. Determine why the validation script failed, and then try the command again." It 'Should throws if the expected exception if the name contains illegal characters' { { Get-ADSIComputer ` @@ -1310,7 +1311,7 @@ try -Domain 'Contoso.com' ` -Credential $credential ` -Verbose - } | Should -Throw "Cannot validate argument on parameter 'Name'. The `" $_ -inotmatch '[\/\\:*?`"<>|]' `" validation script for the argument with value `"IllegalName[<`" did not return a result of True. Determine why the validation script failed, and then try the command again." + } | Should -Throw $message } It 'Returns ADSI object with ADSI path ' { @@ -1393,13 +1394,14 @@ try Assert-MockCalled -CommandName New-Object -Exactly -Times 1 -Scope It } + $message = "Cannot validate argument on parameter 'Path'. The `" $_ -imatch `"LDAP://*`" `" validation script for the argument with value `"contoso.com/CN=fake-computer,OU=Computers,DC=contoso,DC=com`" did not return a result of True. Determine why the validation script failed, and then try the command again." It 'Should throw if path does not begin with LDAP://' { { Delete-ADSIObject ` -Path 'contoso.com/CN=fake-computer,OU=Computers,DC=contoso,DC=com' ` -Credential $credential` -Verbose - } | Should -Throw "Cannot validate argument on parameter 'Path'. The `" $_ -imatch `"LDAP://*`" `" validation script for the argument with value `"contoso.com/CN=fake-computer,OU=Computers,DC=contoso,DC=com`" did not return a result of True. Determine why the validation script failed, and then try the command again." + } | Should -Throw $message } It 'Should throw if Credential is incorrect' { From 5257d52209850acea12d1dceb19a2613745262c9 Mon Sep 17 00:00:00 2001 From: Nick Germany Date: Mon, 30 May 2022 22:11:42 -0400 Subject: [PATCH 35/39] Fixing validation error message --- tests/Unit/DSC_Computer.Tests.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Unit/DSC_Computer.Tests.ps1 b/tests/Unit/DSC_Computer.Tests.ps1 index ea140095..74fec6d1 100644 --- a/tests/Unit/DSC_Computer.Tests.ps1 +++ b/tests/Unit/DSC_Computer.Tests.ps1 @@ -1303,7 +1303,7 @@ try } | Should -Throw $message } - $message = "Cannot validate argument on parameter 'Name'. The `" $_ -inotmatch '[\/\\:*?`"<>|]' `" validation script for the argument with value `"IllegalName[<`" did not return a result of True. Determine why the validation script failed, and then try the command again." + $message = "Cannot validate argument on parameter 'Name'. The `" `$_ -inotmatch '[\/\\:*?`"<>|]' `" validation script for the argument with value `"IllegalName[<`" did not return a result of True. Determine why the validation script failed, and then try the command again." It 'Should throws if the expected exception if the name contains illegal characters' { { Get-ADSIComputer ` @@ -1394,7 +1394,7 @@ try Assert-MockCalled -CommandName New-Object -Exactly -Times 1 -Scope It } - $message = "Cannot validate argument on parameter 'Path'. The `" $_ -imatch `"LDAP://*`" `" validation script for the argument with value `"contoso.com/CN=fake-computer,OU=Computers,DC=contoso,DC=com`" did not return a result of True. Determine why the validation script failed, and then try the command again." + $message = "Cannot validate argument on parameter 'Path'. The `" `$_ -imatch `"LDAP://*`" `" validation script for the argument with value `"contoso.com/CN=fake-computer,OU=Computers,DC=contoso,DC=com`" did not return a result of True. Determine why the validation script failed, and then try the command again." It 'Should throw if path does not begin with LDAP://' { { Delete-ADSIObject ` From a51166621f202a63876f1a317c5716083f25d7c6 Mon Sep 17 00:00:00 2001 From: Nick Germany Date: Tue, 31 May 2022 10:08:05 -0400 Subject: [PATCH 36/39] Updating mock commands and removing try catches to let dotnet error expose itself --- .../DSC_Computer/DSC_Computer.psm1 | 43 ++++++------------- tests/Unit/DSC_Computer.Tests.ps1 | 27 +++++++----- 2 files changed, 30 insertions(+), 40 deletions(-) diff --git a/source/DSCResources/DSC_Computer/DSC_Computer.psm1 b/source/DSCResources/DSC_Computer/DSC_Computer.psm1 index 092bbcf8..141a91e6 100644 --- a/source/DSCResources/DSC_Computer/DSC_Computer.psm1 +++ b/source/DSCResources/DSC_Computer/DSC_Computer.psm1 @@ -742,14 +742,7 @@ function Get-ADSIComputer $searchRoot = New-Object @params $searcher.SearchRoot = $searchRoot - try - { - return $searcher.FindOne() - } - catch - { - New-InvalidOperationException -Message ($script:localizedData.InvalidUserNameorPassword -f $Credential.Username) - } + return $searcher.FindOne() } <# @@ -777,28 +770,19 @@ function Delete-ADSIObject $Credential ) - try - { - $params = @{ - TypeName = 'System.DirectoryServices.DirectoryEntry' - ArgumentList = @( - $DomainName, - $Credential.UserName - $Credential.GetNetworkCredential().password - ) - ErrorAction = 'Stop' - } - $adsiObj = New-Object @params - - $adsiObj.DeleteTree() - } - catch - { - New-InvalidOperationException -Message $_.Exception.Message -ErrorRecord $_ + $params = @{ + TypeName = 'System.DirectoryServices.DirectoryEntry' + ArgumentList = @( + $DomainName, + $Credential.UserName + $Credential.GetNetworkCredential().password + ) + ErrorAction = 'Stop' } -} + $adsiObj = New-Object @params -Export-ModuleMember -Function *-TargetResource + $adsiObj.DeleteTree() +} <# .SYNOPSIS @@ -892,5 +876,6 @@ function Assert-ResourceProperty -Message $script:localizedData.InvalidOptionCredentialUnsecuredJoinNullUsername ` -ArgumentName 'Credential' } - } + +Export-ModuleMember -Function *-TargetResource diff --git a/tests/Unit/DSC_Computer.Tests.ps1 b/tests/Unit/DSC_Computer.Tests.ps1 index 74fec6d1..fe7dc7a5 100644 --- a/tests/Unit/DSC_Computer.Tests.ps1 +++ b/tests/Unit/DSC_Computer.Tests.ps1 @@ -1352,7 +1352,9 @@ try } It 'Should throw the expected exception if Credential is incorrect' { - Mock 'New-Object' { Write-Error -message "Invalid Credentials" } ` + Mock -CommandName New-Object -MockWith { + Write-Error -message "Invalid Credentials" + } ` -ParameterFilter { $TypeName -and $TypeName -eq 'System.DirectoryServices.DirectoryEntry' @@ -1364,13 +1366,12 @@ try -Domain 'Contoso.com' ` -Credential $credential ` -Verbose - } | Should -Throw "Invalid Credentials" + } | Should -Throw 'Invalid Credentials' Assert-MockCalled -CommandName New-Object -Exactly -Times 2 -Scope It } } Context 'DSC_Computer\Delete-ADSIObject' { - class fake_adsi_directoryentry { [string] $Domain [string] $Username @@ -1379,11 +1380,13 @@ try } It 'Should delete the ADSI Object' { - Mock 'New-Object' { New-Object 'fake_adsi_directoryentry' } ` - -ParameterFilter { - $TypeName -and - $TypeName -eq 'System.DirectoryServices.DirectoryEntry' - } + Mock -CommandName New-Object -MockWith { + New-Object 'fake_adsi_directoryentry' + } ` + -ParameterFilter { + $TypeName -and + $TypeName -eq 'System.DirectoryServices.DirectoryEntry' + } { Delete-ADSIObject ` @@ -1404,8 +1407,10 @@ try } | Should -Throw $message } - It 'Should throw if Credential is incorrect' { - Mock 'New-Object' { Write-Error -message "Invalid Credential" } ` + It 'Should throw the expected exception if Credential is incorrect' { + Mock -CommandName New-Object -MockWith { + Write-Error -message 'Invalid Credential' + } ` -ParameterFilter { $TypeName -and $TypeName -eq 'System.DirectoryServices.DirectoryEntry' @@ -1416,7 +1421,7 @@ try -Path 'LDAP://contoso.com/CN=fake-computer,OU=Computers,DC=contoso,DC=com' ` -Credential $credential ` -Verbose - } | Should -Throw "Invalid Credential" + } | Should -Throw 'Invalid Credential' Assert-MockCalled -CommandName New-Object -Exactly -Times 1 -Scope It } } From 20df9ddf4aa731dc2e112a2f456dd1242eee84cf Mon Sep 17 00:00:00 2001 From: Nick Germany Date: Tue, 31 May 2022 10:15:57 -0400 Subject: [PATCH 37/39] Removing unused string --- source/DSCResources/DSC_Computer/en-US/DSC_Computer.strings.psd1 | 1 - 1 file changed, 1 deletion(-) diff --git a/source/DSCResources/DSC_Computer/en-US/DSC_Computer.strings.psd1 b/source/DSCResources/DSC_Computer/en-US/DSC_Computer.strings.psd1 index 944ca25a..22903a87 100644 --- a/source/DSCResources/DSC_Computer/en-US/DSC_Computer.strings.psd1 +++ b/source/DSCResources/DSC_Computer/en-US/DSC_Computer.strings.psd1 @@ -19,5 +19,4 @@ ConvertFrom-StringData @' DeletedExistingComputerObject = Deleted existing computer object with name '{0}' at path '{1}'. InvalidOptionPasswordPassUnsecuredJoin = Domain Join option 'PasswordPass' may not be specified if 'UnsecuredJoin' is specified. InvalidOptionCredentialUnsecuredJoinNullUsername = 'Credential' username must be null if 'UnsecuredJoin' is specified. - InvalidUserNameorPassword = Password specified for UserName {0} is invalid. '@ From 91154482e16d1b6694e52fbd56c6d4b0fa825823 Mon Sep 17 00:00:00 2001 From: Nick Germany Date: Wed, 1 Jun 2022 09:25:37 -0400 Subject: [PATCH 38/39] update changelog and single quote string --- CHANGELOG.md | 4 ++-- tests/Unit/DSC_Computer.Tests.ps1 | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 31ca7f3c..73154ce3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,12 +4,12 @@ The format is based on and uses the types of changes according to [Keep a Change and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] -- Computer - - When joining a computer to a domain, existing AD computer objects will be deleted. ### Added + - Computer - Support Options Parameter for domain join - Fixes [Issue #234](https://github.com/dsccommunity/ComputerManagementDsc/issues/234). + - When joining a computer to a domain, existing AD computer objects will be deleted. [Issue #55](https://github.com/dsccommunity/ComputerManagementDsc/issues/55), [Issue #58](https://github.com/dsccommunity/ComputerManagementDsc/issues/58) ## [8.5.0] - 2021-09-13 diff --git a/tests/Unit/DSC_Computer.Tests.ps1 b/tests/Unit/DSC_Computer.Tests.ps1 index fe7dc7a5..76f9862b 100644 --- a/tests/Unit/DSC_Computer.Tests.ps1 +++ b/tests/Unit/DSC_Computer.Tests.ps1 @@ -1353,7 +1353,7 @@ try It 'Should throw the expected exception if Credential is incorrect' { Mock -CommandName New-Object -MockWith { - Write-Error -message "Invalid Credentials" + Write-Error -message 'Invalid Credentials' } ` -ParameterFilter { $TypeName -and From f59de8f7bee51765e86332fdeaf20fc8fbe58005 Mon Sep 17 00:00:00 2001 From: Nick Germany Date: Wed, 1 Jun 2022 09:26:23 -0400 Subject: [PATCH 39/39] changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 73154ce3..5f4b0414 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Computer - Support Options Parameter for domain join - Fixes [Issue #234](https://github.com/dsccommunity/ComputerManagementDsc/issues/234). - - When joining a computer to a domain, existing AD computer objects will be deleted. [Issue #55](https://github.com/dsccommunity/ComputerManagementDsc/issues/55), [Issue #58](https://github.com/dsccommunity/ComputerManagementDsc/issues/58) + - When joining a computer to a domain, existing AD computer objects will be deleted - Fixes [Issue #55](https://github.com/dsccommunity/ComputerManagementDsc/issues/55), [Issue #58](https://github.com/dsccommunity/ComputerManagementDsc/issues/58). ## [8.5.0] - 2021-09-13