From 2cd797af10e285c1a293e25f8f4a200d8c805df5 Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Wed, 22 May 2019 14:31:11 +0200 Subject: [PATCH 1/8] Add new resource xADObjectEnabledState --- CHANGELOG.md | 4 + .../MSFT_xADObjectEnabledState.psm1 | 378 ++++++++++++++ .../MSFT_xADObjectEnabledState.schema.mof | 9 + .../MSFT_xADObjectEnabledState.strings.psd1 | 13 + .../1-EnabledComputerAccount_Config.ps1 | 46 ++ .../2-CreateClusterComputerAccount_Config.ps1 | 65 +++ ...PrestagedClusterComputerAccount_Config.ps1 | 58 +++ ...xADObjectEnableState.Integration.Tests.ps1 | 197 ++++++++ .../MSFT_xADObjectEnabledState.config.ps1 | 101 ++++ .../Unit/MSFT_xADObjectEnabledState.Tests.ps1 | 477 ++++++++++++++++++ 10 files changed, 1348 insertions(+) create mode 100644 DSCResources/MSFT_xADObjectEnabledState/MSFT_xADObjectEnabledState.psm1 create mode 100644 DSCResources/MSFT_xADObjectEnabledState/MSFT_xADObjectEnabledState.schema.mof create mode 100644 DSCResources/MSFT_xADObjectEnabledState/en-US/MSFT_xADObjectEnabledState.strings.psd1 create mode 100644 Examples/Resources/xADObjectEnabledState/1-EnabledComputerAccount_Config.ps1 create mode 100644 Examples/Resources/xADObjectEnabledState/2-CreateClusterComputerAccount_Config.ps1 create mode 100644 Examples/Resources/xADObjectEnabledState/3-EnabledPrestagedClusterComputerAccount_Config.ps1 create mode 100644 Tests/Integration/MSFT_xADObjectEnableState.Integration.Tests.ps1 create mode 100644 Tests/Integration/MSFT_xADObjectEnabledState.config.ps1 create mode 100644 Tests/Unit/MSFT_xADObjectEnabledState.Tests.ps1 diff --git a/CHANGELOG.md b/CHANGELOG.md index b9a4870c3..6561c30a2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,10 @@ are not in desired state when verbose output is enabled. - Update all unit tests to latest unit test template. - Deleted the obsolete xActiveDirectory_TechNetDocumentation.html file + - Added new resource xADObjectEnabledState. This resource should be + used to enforce the `Enabled` property of computer accounts. This + resource replaces the deprecated `Enabled` property in the resource + xADComputer. - Changes to xADComputer - Refactored the resource and the unit tests. - BREAKING CHANGE: The `Enabled` property is **DEPRECATED** and is no diff --git a/DSCResources/MSFT_xADObjectEnabledState/MSFT_xADObjectEnabledState.psm1 b/DSCResources/MSFT_xADObjectEnabledState/MSFT_xADObjectEnabledState.psm1 new file mode 100644 index 000000000..304790717 --- /dev/null +++ b/DSCResources/MSFT_xADObjectEnabledState/MSFT_xADObjectEnabledState.psm1 @@ -0,0 +1,378 @@ +$script:resourceModulePath = Split-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -Parent +$script:modulesFolderPath = Join-Path -Path $script:resourceModulePath -ChildPath 'Modules' + +$script:localizationModulePath = Join-Path -Path $script:modulesFolderPath -ChildPath 'xActiveDirectory.Common' +Import-Module -Name (Join-Path -Path $script:localizationModulePath -ChildPath 'xActiveDirectory.Common.psm1') + +$script:dscResourcePath = Split-Path -Path $PSScriptRoot -Parent +Import-Module -Name (Join-Path -Path $script:dscResourcePath -ChildPath '\MSFT_xADCommon\MSFT_xADCommon.psm1') + +$script:localizedData = Get-LocalizedData -ResourceName 'MSFT_xADObjectEnabledState' + +<# + .SYNOPSIS + Returns the current state of the property Enabled of an Active Directory + object. + + .PARAMETER Identity + Specifies the identity of an object that has the object class specified + in the parameter ObjectClass. When ObjectClass is set to 'Computer' then + this property can be set to either distinguished name, GUID (objectGUID), + security identifier (objectSid), or security Accounts Manager account + name (sAMAccountName). + + .PARAMETER ObjectClass + Specifies the object class. + + .PARAMETER Enabled + Specifies the value of the Enabled property. + + Not used in Get-TargetResource. + + .PARAMETER DomainController + Specifies the Active Directory Domain Services instance to connect to perform the task. + + Used by Get-ADCommonParameters and is returned as a common parameter. + + .PARAMETER Credential + Specifies the user account credentials to use to perform the task. + + Used by Get-ADCommonParameters and is returned as a common parameter. +#> +function Get-TargetResource +{ + [CmdletBinding()] + [OutputType([System.Collections.Hashtable])] + param + ( + [Parameter(Mandatory = $true)] + [System.String] + $Identity, + + [Parameter(Mandatory = $true)] + [ValidateSet('Computer')] + [System.String] + $ObjectClass, + + [Parameter(Mandatory = $true)] + [ValidateNotNull()] + [System.Boolean] + $Enabled, + + [Parameter()] + [ValidateNotNull()] + [System.String] + $DomainController, + + [Parameter()] + [ValidateNotNull()] + [System.Management.Automation.PSCredential] + [System.Management.Automation.CredentialAttribute()] + $Credential + ) + + Assert-Module -ModuleName 'ActiveDirectory' -ImportModule + + <# + These are properties that have no corresponding property in a + Computer account object. + #> + $getTargetResourceReturnValue = @{ + Identity = $Identity + ObjectClass = $ObjectClass + Enabled = $false + DomainController = $DomainController + Credential = $Credential + } + + switch ($ObjectClass) + { + 'Computer' + { + $getADComputerResult = $null + + try + { + Write-Verbose -Message ($script:localizedData.RetrievingComputerAccount -f $Identity) + + $getADComputerParameters = Get-ADCommonParameters @PSBoundParameters + $getADComputerParameters['Properties'] = 'Enabled' + + # If the computer account is not found Get-ADComputer will throw an error. + $getADComputerResult = Get-ADComputer @getADComputerParameters + + $getTargetResourceReturnValue['Enabled'] = $getADComputerResult.Enabled + + if ($getADComputerResult.Enabled) + { + Write-Verbose -Message $script:localizedData.ComputerAccountEnabled + } + else + { + Write-Verbose -Message $script:localizedData.ComputerAccountDisabled + } + } + catch + { + $errorMessage = $script:localizedData.FailedToRetrieveComputerAccount -f $Identity + New-InvalidOperationException -Message $errorMessage -ErrorRecord $_ + } + } + } + + return $getTargetResourceReturnValue +} + +<# + .SYNOPSIS + Determines if the property Enabled of the Active Directory object is in + the desired state. + + .PARAMETER Identity + Specifies the identity of an object that has the object class specified + in the parameter ObjectClass. When ObjectClass is set to 'Computer' then + this property can be set to either distinguished name, GUID (objectGUID), + security identifier (objectSid), or security Accounts Manager account + name (sAMAccountName). + + .PARAMETER ObjectClass + Specifies the object class. + + .PARAMETER Enabled + Specifies the value of the Enabled property. + + .PARAMETER DomainController + Specifies the Active Directory Domain Services instance to connect to + perform the task. + + .PARAMETER Credential + Specifies the user account credentials to use to perform the task. +#> +function Test-TargetResource +{ + [CmdletBinding()] + [OutputType([System.Boolean])] + param + ( + [Parameter(Mandatory = $true)] + [System.String] + $Identity, + + [Parameter(Mandatory = $true)] + [ValidateSet('Computer')] + [System.String] + $ObjectClass, + + [Parameter(Mandatory = $true)] + [ValidateNotNull()] + [System.Boolean] + $Enabled, + + [Parameter()] + [ValidateNotNull()] + [System.String] + $DomainController, + + [Parameter()] + [ValidateNotNull()] + [System.Management.Automation.PSCredential] + [System.Management.Automation.CredentialAttribute()] + $Credential + ) + + Write-Verbose -Message ( + $script:localizedData.TestConfiguration -f $Identity, $ObjectClass + ) + + $getTargetResourceParameters = @{ + Identity = $Identity + ObjectClass = $ObjectClass + Enabled = $Enabled + DomainController = $DomainController + Credential = $Credential + } + + # Need the @() around this to get a new array to enumerate. + @($getTargetResourceParameters.Keys) | ForEach-Object { + if (-not $PSBoundParameters.ContainsKey($_)) + { + $getTargetResourceParameters.Remove($_) + } + } + + $getTargetResourceResult = Get-TargetResource @getTargetResourceParameters + + $compareTargetResourceStateParameters = @{ + CurrentValues = $getTargetResourceResult + DesiredValues = $PSBoundParameters + Properties = @('Enabled') + } + + $compareTargetResourceStateResult = Compare-ResourcePropertyState @compareTargetResourceStateParameters + + if ($false -in $compareTargetResourceStateResult.InDesiredState) + { + $testTargetResourceReturnValue = $false + } + else + { + $testTargetResourceReturnValue = $true + } + + switch ($ObjectClass) + { + 'Computer' + { + if ($testTargetResourceReturnValue) + { + Write-Verbose -Message ($script:localizedData.ComputerAccountInDesiredState -f $Identity) + } + else + { + Write-Verbose -Message ($script:localizedData.ComputerAccountNotInDesiredState -f $Identity) + } + } + } + + return $testTargetResourceReturnValue +} + +<# + .SYNOPSIS + Sets the property Enabled of the Active Directory object. + + .PARAMETER Identity + Specifies the identity of an object that has the object class specified + in the parameter ObjectClass. When ObjectClass is set to 'Computer' then + this property can be set to either distinguished name, GUID (objectGUID), + security identifier (objectSid), or security Accounts Manager account + name (sAMAccountName). + + .PARAMETER ObjectClass + Specifies the object class. + + .PARAMETER Enabled + Specifies the value of the Enabled property. + + .PARAMETER DomainController + Specifies the Active Directory Domain Services instance to connect to + perform the task. + + .PARAMETER Credential + Specifies the user account credentials to use to perform the task. +#> +function Set-TargetResource +{ + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [System.String] + $Identity, + + [Parameter(Mandatory = $true)] + [ValidateSet('Computer')] + [System.String] + $ObjectClass, + + [Parameter(Mandatory = $true)] + [ValidateNotNull()] + [System.Boolean] + $Enabled, + + [Parameter()] + [ValidateNotNull()] + [System.String] + $DomainController, + + [Parameter()] + [ValidateNotNull()] + [System.Management.Automation.PSCredential] + [System.Management.Automation.CredentialAttribute()] + $Credential + ) + + $getTargetResourceParameters = @{ + Identity = $Identity + ObjectClass = $ObjectClass + Enabled = $Enabled + DomainController = $DomainController + Credential = $Credential + } + + # Need the @() around this to get a new array to enumerate. + @($getTargetResourceParameters.Keys) | ForEach-Object { + if (-not $PSBoundParameters.ContainsKey($_)) + { + $getTargetResourceParameters.Remove($_) + } + } + + $getTargetResourceResult = Get-TargetResource @getTargetResourceParameters + + $compareTargetResourceStateParameters = @{ + CurrentValues = $getTargetResourceResult + DesiredValues = $PSBoundParameters + Properties = @('Enabled') + } + + $compareTargetResourceStateResult = Compare-ResourcePropertyState @compareTargetResourceStateParameters + + # Get all properties that are not in desired state. + $propertiesNotInDesiredState = $compareTargetResourceStateResult | Where-Object -FilterScript { + -not $_.InDesiredState + } + + if ($propertiesNotInDesiredState.Where( { $_.ParameterName -eq 'Enabled' })) + { + $commonParameters = Get-ADCommonParameters @PSBoundParameters + + switch ($ObjectClass) + { + 'Computer' + { + $setADComputerParameters = $commonParameters.Clone() + $setADComputerParameters['Enabled'] = $Enabled + + Set-DscADComputer -Parameters $setADComputerParameters + + if ($Enabled) + { + Write-Verbose -Message ( + $script:localizedData.ComputerAccountHasBeenEnabled -f $Identity + ) + } + else + { + Write-Verbose -Message ( + $script:localizedData.ComputerAccountHasBeenDisabled -f $Identity + ) + } + } + } + } +} + +<# + .SYNOPSIS + This is a wrapper for Set-ADComputer. + + .PARAMETER Parameters + A hash table containing all parameters that will be passed trough to + Set-ADComputer. + + .NOTES + This is needed because of how Pester is unable to handle mocking the + cmdlet Set-ADComputer. +#> +function Set-DscADComputer +{ + param + ( + [Parameter(Mandatory = $true)] + [System.Collections.Hashtable] + $Parameters + ) + + Set-ADComputer @Parameters | Out-Null +} diff --git a/DSCResources/MSFT_xADObjectEnabledState/MSFT_xADObjectEnabledState.schema.mof b/DSCResources/MSFT_xADObjectEnabledState/MSFT_xADObjectEnabledState.schema.mof new file mode 100644 index 000000000..4c44b69a7 --- /dev/null +++ b/DSCResources/MSFT_xADObjectEnabledState/MSFT_xADObjectEnabledState.schema.mof @@ -0,0 +1,9 @@ +[ClassVersion("1.0.0.0"), FriendlyName("xADObjectEnabledState")] +class MSFT_xADObjectEnabledState : OMI_BaseResource +{ + [Key, Description("Specifies the identity of an object that has the object class specified in the parameter ObjectClass. When ObjectClass is set to 'Computer' then this property can be set to either distinguished name, GUID (objectGUID), security identifier (objectSid), or security Accounts Manager account name (sAMAccountName).")] String Identity; + [Key, Description("Specifies the object class."), ValueMap{"Computer"}, Values{"Computer"}] String ObjectClass; + [Required, Description("Specifies the value of the Enabled property.")] Boolean Enabled; + [Write, Description("Specifies the Active Directory Domain Services instance to connect to perform the task.")] String DomainController; + [Write, Description("Specifies the user account credentials to use to perform the task."), EmbeddedInstance("MSFT_Credential")] String Credential; +}; diff --git a/DSCResources/MSFT_xADObjectEnabledState/en-US/MSFT_xADObjectEnabledState.strings.psd1 b/DSCResources/MSFT_xADObjectEnabledState/en-US/MSFT_xADObjectEnabledState.strings.psd1 new file mode 100644 index 000000000..413140c08 --- /dev/null +++ b/DSCResources/MSFT_xADObjectEnabledState/en-US/MSFT_xADObjectEnabledState.strings.psd1 @@ -0,0 +1,13 @@ +# culture="en-US" +ConvertFrom-StringData @' + RetrievingComputerAccount = Retrieving the information about the computer account '{0}' from Active Directory. (ADOES0001) + ComputerAccountEnabled = The computer account is enabled. (ADOES0002) + ComputerAccountDisabled = The computer account is disabled. (ADOES0003) + ComputerAccountIsAbsent = The computer account '{0}' is absent from Active Directory. (ADOES0004) + FailedToRetrieveComputerAccount = Failed to retrieve the computer account '{0}' from Active Directory. (ADOES0005) + TestConfiguration = Determining the current state of the enabled property of the object with the identity '{0}' and object class '{1}'. (ADOES0006) + ComputerAccountInDesiredState = The property Enabled of the computer account '{0}' is in the desired state. (ADOES0007) + ComputerAccountNotInDesiredState = The property Enabled of the computer account '{0}' is not in the desired state. (ADOES0008) + ComputerAccountHasBeenDisabled = The computer account '{0}' has been disabled. (ADOES0009) + ComputerAccountHasBeenEnabled = The computer account '{0}' has been enabled. (ADOES0010) +'@ diff --git a/Examples/Resources/xADObjectEnabledState/1-EnabledComputerAccount_Config.ps1 b/Examples/Resources/xADObjectEnabledState/1-EnabledComputerAccount_Config.ps1 new file mode 100644 index 000000000..37cc542e1 --- /dev/null +++ b/Examples/Resources/xADObjectEnabledState/1-EnabledComputerAccount_Config.ps1 @@ -0,0 +1,46 @@ +<#PSScriptInfo +.VERSION 1.0.0 +.GUID b4d414dc-e230-4055-bdc3-fae268493881 +.AUTHOR Microsoft Corporation +.COMPANYNAME Microsoft Corporation +.COPYRIGHT (c) Microsoft Corporation. All rights reserved. +.TAGS DSCConfiguration +.LICENSEURI https://github.com/PowerShell/xActiveDirectory/blob/master/LICENSE +.PROJECTURI https://github.com/PowerShell/xActiveDirectory +.ICONURI +.EXTERNALMODULEDEPENDENCIES +.REQUIREDSCRIPTS +.EXTERNALSCRIPTDEPENDENCIES +.RELEASENOTES First version. +.PRIVATEDATA 2016-Datacenter,2016-Datacenter-Server-Core +#> + +#Requires -module xActiveDirectory + +<# + .DESCRIPTION + This configuration will create a computer account disabled, and + enforcing the account to be enabled. +#> +Configuration EnabledComputerAccount_Config +{ + Import-DscResource -ModuleName xActiveDirectory + + node localhost + { + xADComputer 'CreateDisabled' + { + ComputerName = 'CLU_CNO01' + EnabledOnCreation = $false + } + + xADObjectEnabledState 'EnforceEnabledPropertyToEnabled' + { + Identity = 'CLU_CNO01' + ObjectClass = 'Computer' + Enabled = $true + + DependsOn = '[xADComputer]CreateDisabled' + } + } +} diff --git a/Examples/Resources/xADObjectEnabledState/2-CreateClusterComputerAccount_Config.ps1 b/Examples/Resources/xADObjectEnabledState/2-CreateClusterComputerAccount_Config.ps1 new file mode 100644 index 000000000..8599894ec --- /dev/null +++ b/Examples/Resources/xADObjectEnabledState/2-CreateClusterComputerAccount_Config.ps1 @@ -0,0 +1,65 @@ +<#PSScriptInfo +.VERSION 1.0.0 +.GUID b4d414dc-e230-4055-bdc3-fae268493881 +.AUTHOR Microsoft Corporation +.COMPANYNAME Microsoft Corporation +.COPYRIGHT (c) Microsoft Corporation. All rights reserved. +.TAGS DSCConfiguration +.LICENSEURI https://github.com/PowerShell/xActiveDirectory/blob/master/LICENSE +.PROJECTURI https://github.com/PowerShell/xActiveDirectory +.ICONURI +.EXTERNALMODULEDEPENDENCIES +.REQUIREDSCRIPTS +.EXTERNALSCRIPTDEPENDENCIES +.RELEASENOTES First version. +.PRIVATEDATA 2016-Datacenter,2016-Datacenter-Server-Core +#> + +#Requires -module xActiveDirectory +#Requires -module xFailoverCluster + +<# + .DESCRIPTION + This configuration will create a computer account disabled, configure + a cluster using the disabled computer account, and enforcing the + computer account to be enabled. +#> +Configuration CreateClusterComputerAccount_Config +{ + param + ( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.Management.Automation.PSCredential] + $DomainAdministratorCredential + ) + + Import-DscResource -ModuleName xActiveDirectory + Import-DscResource -ModuleName xFailoverCluster + + node localhost + { + xADComputer 'ClusterAccount' + { + ComputerName = 'CLU_CNO01' + EnabledOnCreation = $false + } + + xCluster 'CreateCluster' + { + Name = 'CLU_CNO01' + StaticIPAddress = '192.168.100.20/24' + + DependsOn = '[xADComputer]ClusterAccount' + } + + xADObjectEnabledState 'EnforceEnabledPropertyToEnabled' + { + Identity = 'CLU_CNO01' + ObjectClass = 'Computer' + Enabled = $true + + DependsOn = '[xCluster]CreateCluster' + } + } +} diff --git a/Examples/Resources/xADObjectEnabledState/3-EnabledPrestagedClusterComputerAccount_Config.ps1 b/Examples/Resources/xADObjectEnabledState/3-EnabledPrestagedClusterComputerAccount_Config.ps1 new file mode 100644 index 000000000..f2b17aedf --- /dev/null +++ b/Examples/Resources/xADObjectEnabledState/3-EnabledPrestagedClusterComputerAccount_Config.ps1 @@ -0,0 +1,58 @@ +<#PSScriptInfo +.VERSION 1.0.0 +.GUID b4d414dc-e230-4055-bdc3-fae268493881 +.AUTHOR Microsoft Corporation +.COMPANYNAME Microsoft Corporation +.COPYRIGHT (c) Microsoft Corporation. All rights reserved. +.TAGS DSCConfiguration +.LICENSEURI https://github.com/PowerShell/xActiveDirectory/blob/master/LICENSE +.PROJECTURI https://github.com/PowerShell/xActiveDirectory +.ICONURI +.EXTERNALMODULEDEPENDENCIES +.REQUIREDSCRIPTS +.EXTERNALSCRIPTDEPENDENCIES +.RELEASENOTES First version. +.PRIVATEDATA 2016-Datacenter,2016-Datacenter-Server-Core +#> + +#Requires -module xActiveDirectory +#Requires -module xFailoverCluster + +<# + .DESCRIPTION + This configuration will configure a cluster using a pre-staged computer + account, and enforcing the pre-staged computer account to be enabled. +#> +Configuration EnabledPrestagedClusterComputerAccount_Config +{ + param + ( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.Management.Automation.PSCredential] + $DomainAdministratorCredential + ) + + Import-DscResource -ModuleName xActiveDirectory + Import-DscResource -ModuleName xFailoverCluster + + node localhost + { + xCluster 'CreateCluster' + { + Name = 'CLU_CNO01' + StaticIPAddress = '192.168.100.20/24' + } + + xADComputerState 'EnforceEnabledPropertyToEnabled' + { + Identity = 'CLU_CNO01' + ObjectClass = 'Computer' + Enabled = $true + + DependsOn = @( + '[xCluster]CreateCluster' + ) + } + } +} diff --git a/Tests/Integration/MSFT_xADObjectEnableState.Integration.Tests.ps1 b/Tests/Integration/MSFT_xADObjectEnableState.Integration.Tests.ps1 new file mode 100644 index 000000000..9c388c78a --- /dev/null +++ b/Tests/Integration/MSFT_xADObjectEnableState.Integration.Tests.ps1 @@ -0,0 +1,197 @@ +# Make sure this is run in the correct order. +[Microsoft.DscResourceKit.IntegrationTest(OrderNumber = 3)] +param() + +if ($env:APPVEYOR -eq $true) +{ + Write-Warning -Message 'Integration test is not supported in AppVeyor.' + return +} + +$script:dscModuleName = 'xActiveDirectory' +$script:dscResourceFriendlyName = 'xADObjectEnabledState' +$script:dscResourceName = "MSFT_$($script:dscResourceFriendlyName)" + +#region HEADER +# Integration Test Template Version: 1.3.3 +[String] $script:moduleRoot = Split-Path -Parent (Split-Path -Parent $PSScriptRoot) +if ( (-not (Test-Path -Path (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResource.Tests'))) -or ` + (-not (Test-Path -Path (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResource.Tests\TestHelper.psm1'))) ) +{ + & git @('clone', 'https://github.com/PowerShell/DscResource.Tests.git', (Join-Path -Path $script:moduleRoot -ChildPath 'DscResource.Tests')) +} + +Import-Module -Name (Join-Path -Path $script:moduleRoot -ChildPath (Join-Path -Path 'DSCResource.Tests' -ChildPath 'TestHelper.psm1')) -Force +$TestEnvironment = Initialize-TestEnvironment ` + -DSCModuleName $script:dscModuleName ` + -DSCResourceName $script:dscResourceName ` + -TestType Integration +#endregion + +try +{ + $configFile = Join-Path -Path $PSScriptRoot -ChildPath "$($script:dscResourceName).config.ps1" + . $configFile + + Describe "$($script:dscResourceName)_Integration" { + BeforeAll { + $resourceId = "[$($script:dscResourceFriendlyName)]Integration_Test" + } + + $configurationName = "$($script:dscResourceName)_Prerequisites_Config" + + Context ('When using configuration {0}' -f $configurationName) { + It 'Should compile and apply the MOF without throwing' { + { + $configurationParameters = @{ + OutputPath = $TestDrive + # The variable $ConfigurationData was dot-sourced above. + ConfigurationData = $ConfigurationData + } + + & $configurationName @configurationParameters + + $startDscConfigurationParameters = @{ + Path = $TestDrive + ComputerName = 'localhost' + Wait = $true + Verbose = $true + Force = $true + ErrorAction = 'Stop' + } + + Start-DscConfiguration @startDscConfigurationParameters + } | Should -Not -Throw + } + } + + $configurationName = "$($script:dscResourceName)_DisableComputerAccount_Config" + + Context ('When using configuration {0}' -f $configurationName) { + It 'Should compile and apply the MOF without throwing' { + { + $configurationParameters = @{ + OutputPath = $TestDrive + # The variable $ConfigurationData was dot-sourced above. + ConfigurationData = $ConfigurationData + } + + & $configurationName @configurationParameters + + $startDscConfigurationParameters = @{ + Path = $TestDrive + ComputerName = 'localhost' + Wait = $true + Verbose = $true + Force = $true + ErrorAction = 'Stop' + } + + Start-DscConfiguration @startDscConfigurationParameters + } | Should -Not -Throw + } + + It 'Should be able to call Get-DscConfiguration without throwing' { + { + $script:currentConfiguration = Get-DscConfiguration -Verbose -ErrorAction Stop + } | Should -Not -Throw + } + + It 'Should have set the resource and all the parameters should match' { + $resourceCurrentState = $script:currentConfiguration | Where-Object -FilterScript { + $_.ConfigurationName -eq $configurationName ` + -and $_.ResourceId -eq $resourceId + } + + $resourceCurrentState.Enabled | Should -BeFalse + } + + It 'Should return $true when Test-DscConfiguration is run' { + Test-DscConfiguration -Verbose | Should -Be 'True' + } + } + + $configurationName = "$($script:dscResourceName)_EnableComputerAccount_Config" + + Context ('When using configuration {0}' -f $configurationName) { + It 'Should compile and apply the MOF without throwing' { + { + $configurationParameters = @{ + OutputPath = $TestDrive + # The variable $ConfigurationData was dot-sourced above. + ConfigurationData = $ConfigurationData + } + + & $configurationName @configurationParameters + + $startDscConfigurationParameters = @{ + Path = $TestDrive + ComputerName = 'localhost' + Wait = $true + Verbose = $true + Force = $true + ErrorAction = 'Stop' + } + + Start-DscConfiguration @startDscConfigurationParameters + } | Should -Not -Throw + } + + It 'Should be able to call Get-DscConfiguration without throwing' { + { + $script:currentConfiguration = Get-DscConfiguration -Verbose -ErrorAction Stop + } | Should -Not -Throw + } + + It 'Should have set the resource and all the parameters should match' { + $resourceCurrentState = $script:currentConfiguration | Where-Object -FilterScript { + $_.ConfigurationName -eq $configurationName ` + -and $_.ResourceId -eq $resourceId + } + + $resourceCurrentState.Enabled | Should -BeTrue + } + + It 'Should return $true when Test-DscConfiguration is run' { + Test-DscConfiguration -Verbose | Should -Be 'True' + } + } + + $configurationName = "$($script:dscResourceName)_CleanUp_Config" + + Context ('When using configuration {0}' -f $configurationName) { + It 'Should compile and apply the MOF without throwing' { + { + $configurationParameters = @{ + OutputPath = $TestDrive + # The variable $ConfigurationData was dot-sourced above. + ConfigurationData = $ConfigurationData + } + + & $configurationName @configurationParameters + + $startDscConfigurationParameters = @{ + Path = $TestDrive + ComputerName = 'localhost' + Wait = $true + Verbose = $true + Force = $true + ErrorAction = 'Stop' + } + + Start-DscConfiguration @startDscConfigurationParameters + } | Should -Not -Throw + } + + It 'Should return $true when Test-DscConfiguration is run' { + Test-DscConfiguration -Verbose | Should -Be 'True' + } + } + } +} +finally +{ + #region FOOTER + Restore-TestEnvironment -TestEnvironment $TestEnvironment + #endregion +} diff --git a/Tests/Integration/MSFT_xADObjectEnabledState.config.ps1 b/Tests/Integration/MSFT_xADObjectEnabledState.config.ps1 new file mode 100644 index 000000000..963f1fa3f --- /dev/null +++ b/Tests/Integration/MSFT_xADObjectEnabledState.config.ps1 @@ -0,0 +1,101 @@ +#region HEADER +# Integration Test Config Template Version: 1.2.0 +#endregion + +$configFile = [System.IO.Path]::ChangeExtension($MyInvocation.MyCommand.Path, 'json') +if (Test-Path -Path $configFile) +{ + <# + Allows reading the configuration data from a JSON file, for real testing + scenarios outside of the CI. + #> + $ConfigurationData = Get-Content -Path $configFile | ConvertFrom-Json +} +else +{ + $ConfigurationData = @{ + AllNodes = @( + @{ + NodeName = 'localhost' + CertificateFile = $env:DscPublicCertificatePath + + ComputerName = 'DSCINTEGTEST01' + } + ) + } +} + +<# + .SYNOPSIS + Creates a computer account using the default values. +#> +Configuration MSFT_xADObjectEnabledState_Prerequisites_Config +{ + Import-DscResource -ModuleName 'xActiveDirectory' + + node $AllNodes.NodeName + { + xADComputer 'CreateComputerAccount' + { + ComputerName = $Node.ComputerName + } + } +} + +<# + .SYNOPSIS + Disables a computer account. + +#> +Configuration MSFT_xADObjectEnabledState_DisableComputerAccount_Config +{ + Import-DscResource -ModuleName 'xActiveDirectory' + + node $AllNodes.NodeName + { + xADObjectEnabledState 'Integration_Test' + { + Identity = $Node.ComputerName + ObjectClass = 'Computer' + Enabled = $false + } + } +} + +<# + .SYNOPSIS + Enables a computer account. + +#> +Configuration MSFT_xADObjectEnabledState_EnableComputerAccount_Config +{ + Import-DscResource -ModuleName 'xActiveDirectory' + + node $AllNodes.NodeName + { + xADObjectEnabledState 'Integration_Test' + { + Identity = $Node.ComputerName + ObjectClass = 'Computer' + Enabled = $true + } + } +} + +<# + .SYNOPSIS + Clean up the computer account. +#> +Configuration MSFT_xADObjectEnabledState_CleanUp_Config +{ + Import-DscResource -ModuleName 'xActiveDirectory' + + node $AllNodes.NodeName + { + xADComputer 'RemoveComputerAccount' + { + Ensure = 'Absent' + ComputerName = $Node.ComputerName + } + } +} diff --git a/Tests/Unit/MSFT_xADObjectEnabledState.Tests.ps1 b/Tests/Unit/MSFT_xADObjectEnabledState.Tests.ps1 new file mode 100644 index 000000000..44d0cd455 --- /dev/null +++ b/Tests/Unit/MSFT_xADObjectEnabledState.Tests.ps1 @@ -0,0 +1,477 @@ +[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingConvertToSecureStringWithPlainText', '')] +param() + +$script:dscModuleName = 'xActiveDirectory' +$script:dscResourceName = 'MSFT_xADObjectEnabledState' + +#region HEADER + +# Unit Test Template Version: 1.2.4 +$script:moduleRoot = Split-Path -Parent (Split-Path -Parent $PSScriptRoot) +if ( (-not (Test-Path -Path (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResource.Tests'))) -or ` + (-not (Test-Path -Path (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResource.Tests\TestHelper.psm1'))) ) +{ + & git @('clone', 'https://github.com/PowerShell/DscResource.Tests.git', (Join-Path -Path $script:moduleRoot -ChildPath 'DscResource.Tests')) +} + +Import-Module -Name (Join-Path -Path $script:moduleRoot -ChildPath (Join-Path -Path 'DSCResource.Tests' -ChildPath 'TestHelper.psm1')) -Force + +# TODO: Insert the correct and for your resource +$TestEnvironment = Initialize-TestEnvironment ` + -DSCModuleName $script:dscModuleName ` + -DSCResourceName $script:dscResourceName ` + -ResourceType 'Mof' ` + -TestType Unit + +#endregion HEADER + +function Invoke-TestSetup +{ +} + +function Invoke-TestCleanup +{ + Restore-TestEnvironment -TestEnvironment $TestEnvironment +} + +# Begin Testing +try +{ + Invoke-TestSetup + + InModuleScope $script:dscResourceName { + $mockComputerNamePresent = 'TEST01' + $mockDomain = 'contoso.com' + $mockEnabled = $true + $mockDisabled = $false + $mockObjectClass_Computer = 'Computer' + $mockDomainController = 'DC01' + + $mockCredentialUserName = 'COMPANY\User' + $mockCredentialPassword = 'dummyPassw0rd' | ConvertTo-SecureString -AsPlainText -Force + $mockCredential = New-Object -TypeName 'System.Management.Automation.PSCredential' -ArgumentList @( + $mockCredentialUserName, $mockCredentialPassword + ) + + Describe 'MSFT_xADComputer\Get-TargetResource' -Tag 'Get' { + BeforeAll { + Mock -CommandName Assert-Module + } + + Context 'When the system is not in the desired state' { + Context 'When the Get-ADComputer throws an unknown error' { + BeforeAll { + $errorMessage = 'Mocked error' + Mock -CommandName Get-ADComputer -MockWith { + throw $errorMessage + } + + $getTargetResourceParameters = @{ + Identity = $mockComputerNamePresent + ObjectClass = $mockObjectClass_Computer + Enabled = $false + Verbose = $true + } + } + + It 'Should throw the correct error' { + { Get-TargetResource @getTargetResourceParameters } | Should -Throw $errorMessage + + Assert-MockCalled -CommandName Get-ADComputer -Exactly -Times 1 -Scope It + } + } + + Context 'When the computer account is absent in Active Directory' { + BeforeAll { + Mock -CommandName Get-ADComputer -MockWith { + throw New-Object -TypeName 'Microsoft.ActiveDirectory.Management.ADIdentityNotFoundException' + } + + $getTargetResourceParameters = @{ + Identity = $mockComputerNamePresent + ObjectClass = $mockObjectClass_Computer + Enabled = $false + Verbose = $true + } + } + + It 'Should throw the correct error' { + { Get-TargetResource @getTargetResourceParameters } | Should -Throw ($script:localizedData.FailedToRetrieveComputerAccount -f $mockComputerNamePresent) + + Assert-MockCalled -CommandName Get-ADComputer -Exactly -Times 1 -Scope It + } + } + } + + Context 'When the system is in the desired state' { + BeforeEach { + Mock -CommandName Get-ADComputer -MockWith { + return @{ + CN = $mockComputerNamePresent + Enabled = $mockDynamicEnabledProperty + ObjectClass = $mockObjectClass + } + } + } + + Context 'When the computer account is present in Active Directory' { + Context 'When the computer account is enabled' { + BeforeAll { + $mockDynamicEnabledProperty = $mockEnabled + + $getTargetResourceParameters = @{ + Identity = $mockComputerNamePresent + ObjectClass = $mockObjectClass_Computer + DomainController = $mockDomainController + Credential = $mockCredential + Enabled = $false + Verbose = $true + } + } + + It 'Should return the same values as passed as parameters' { + $result = Get-TargetResource @getTargetResourceParameters + $result.Identity | Should -Be $getTargetResourceParameters.Identity + $result.ObjectClass | Should -Be $getTargetResourceParameters.ObjectClass + $result.DomainController | Should -Be $getTargetResourceParameters.DomainController + $result.Credential.UserName | Should -Be $getTargetResourceParameters.Credential.UserName + + Assert-MockCalled -CommandName Get-ADComputer -Exactly -Times 1 -Scope It + } + + It 'Should return correct values for the rest of the properties' { + $getTargetResourceResult = Get-TargetResource @getTargetResourceParameters + $getTargetResourceResult.Enabled | Should -BeTrue + } + } + + Context 'When the computer account is disabled' { + BeforeAll { + $mockDynamicEnabledProperty = $mockDisabled + + $getTargetResourceParameters = @{ + Identity = $mockComputerNamePresent + ObjectClass = $mockObjectClass_Computer + DomainController = $mockDomainController + Credential = $mockCredential + Enabled = $true + Verbose = $true + } + } + + It 'Should return the same values as passed as parameters' { + $result = Get-TargetResource @getTargetResourceParameters + $result.Identity | Should -Be $getTargetResourceParameters.Identity + $result.ObjectClass | Should -Be $getTargetResourceParameters.ObjectClass + $result.DomainController | Should -Be $getTargetResourceParameters.DomainController + $result.Credential.UserName | Should -Be $getTargetResourceParameters.Credential.UserName + + Assert-MockCalled -CommandName Get-ADComputer -Exactly -Times 1 -Scope It + } + + It 'Should return correct values for the rest of the properties' { + $getTargetResourceResult = Get-TargetResource @getTargetResourceParameters + $getTargetResourceResult.Enabled | Should -BeFalse + } + } + } + + Context 'When Get-TargetResource is called with only mandatory parameters' { + BeforeAll { + $mockDynamicEnabledProperty = $mockEnabled + + $getTargetResourceParameters = @{ + Identity = $mockComputerNamePresent + ObjectClass = $mockObjectClass_Computer + Enabled = $false + Verbose = $true + } + } + + It 'Should only call Get-ADComputer with only Identity parameter' { + $getTargetResourceResult = Get-TargetResource @getTargetResourceParameters + + Assert-MockCalled -CommandName Get-ADComputer -ParameterFilter { + $PSBoundParameters.ContainsKey('Identity') ` + -and -not $PSBoundParameters.ContainsKey('Server') ` + -and -not $PSBoundParameters.ContainsKey('Credential') + } -Exactly -Times 1 -Scope It + } + } + + Context 'When Get-TargetResource is called with DomainController parameter' { + BeforeAll { + $mockDynamicEnabledProperty = $mockEnabled + + $getTargetResourceParameters = @{ + Identity = $mockComputerNamePresent + ObjectClass = $mockObjectClass_Computer + Enabled = $false + DomainController = $mockDomainController + Verbose = $true + } + } + + It 'Should only call Get-ADComputer with Identity and Server parameter' { + $getTargetResourceResult = Get-TargetResource @getTargetResourceParameters + + Assert-MockCalled -CommandName Get-ADComputer -ParameterFilter { + $PSBoundParameters.ContainsKey('Identity') ` + -and $PSBoundParameters.ContainsKey('Server') ` + -and -not $PSBoundParameters.ContainsKey('Credential') + } -Exactly -Times 1 -Scope It + } + } + + Context 'When Get-TargetResource is called with Credential parameter' { + BeforeAll { + $mockDynamicEnabledProperty = $mockEnabled + + $getTargetResourceParameters = @{ + Identity = $mockComputerNamePresent + ObjectClass = $mockObjectClass_Computer + Enabled = $false + Credential = $mockCredential + Verbose = $true + } + } + + It 'Should only call Get-ADComputer with Identity and Credential parameter' { + $getTargetResourceResult = Get-TargetResource @getTargetResourceParameters + + Assert-MockCalled -CommandName Get-ADComputer -ParameterFilter { + $PSBoundParameters.ContainsKey('Identity') ` + -and -not $PSBoundParameters.ContainsKey('Server') ` + -and $PSBoundParameters.ContainsKey('Credential') + } -Exactly -Times 1 -Scope It + } + } + } + } + + Describe 'MSFT_xADComputer\Test-TargetResource' -Tag 'Test' { + BeforeAll { + Mock -CommandName Assert-Module + + $mockGetTargetResource_Enabled = { + return @{ + Identity = $null + ObjectClass = $mockObjectClass_Computer + Enabled = $true + DomainController = $mockDomainController + Credential = $mockCredential + } + } + + $mockGetTargetResource_Disabled = { + return @{ + Identity = $null + ObjectClass = $mockObjectClass_Computer + Enabled = $false + DomainController = $mockDomainController + Credential = $mockCredential + } + } + } + + Context 'When the system is in the desired state' { + Context 'When the computer account is disabled in Active Directory' { + BeforeAll { + Mock -CommandName Get-TargetResource $mockGetTargetResource_Disabled + + $testTargetResourceParameters = @{ + Identity = $mockComputerNamePresent + ObjectClass = $mockObjectClass_Computer + Enabled = $false + Verbose = $true + } + } + + It 'Should return $true' { + $testTargetResourceResult = Test-TargetResource @testTargetResourceParameters + $testTargetResourceResult | Should -BeTrue + + Assert-MockCalled -CommandName Get-TargetResource -Exactly -Times 1 -Scope It + } + } + + Context 'When the computer account is enabled in Active Directory' { + BeforeAll { + Mock -CommandName Get-TargetResource -MockWith $mockGetTargetResource_Enabled + + $testTargetResourceParameters = @{ + Identity = $mockComputerNamePresent + ObjectClass = $mockObjectClass_Computer + Enabled = $true + Verbose = $true + } + } + + It 'Should return $true' { + $testTargetResourceResult = Test-TargetResource @testTargetResourceParameters + $testTargetResourceResult | Should -BeTrue + + Assert-MockCalled -CommandName Get-TargetResource -Exactly -Times 1 -Scope It + } + } + } + + Context 'When the system is not in the desired state' { + Context 'When the computer account should be enabled in Active Directory' { + BeforeAll { + Mock -CommandName Get-TargetResource $mockGetTargetResource_Disabled + + $testTargetResourceParameters = @{ + Identity = $mockComputerNamePresent + ObjectClass = $mockObjectClass_Computer + Enabled = $true + Verbose = $true + } + } + + It 'Should return $false' { + $testTargetResourceResult = Test-TargetResource @testTargetResourceParameters + $testTargetResourceResult | Should -BeFalse + + Assert-MockCalled -CommandName Get-TargetResource -Exactly -Times 1 -Scope It + } + } + + Context 'When the computer account should be disabled in Active Directory' { + BeforeAll { + Mock -CommandName Get-TargetResource -MockWith $mockGetTargetResource_Enabled + + $testTargetResourceParameters = @{ + Identity = $mockComputerNamePresent + ObjectClass = $mockObjectClass_Computer + Enabled = $false + Verbose = $true + } + } + + It 'Should return $false' { + $testTargetResourceResult = Test-TargetResource @testTargetResourceParameters + $testTargetResourceResult | Should -BeFalse + + Assert-MockCalled -CommandName Get-TargetResource -Exactly -Times 1 -Scope It + } + } + } + } + + Describe 'MSFT_xADComputer\Set-TargetResource' -Tag 'Set' { + BeforeAll { + Mock -CommandName Assert-Module + Mock -CommandName Set-DscADComputer + + $mockGetTargetResource_Enabled = { + return @{ + Identity = $null + ObjectClass = $mockObjectClass_Computer + Enabled = $true + DomainController = $mockDomainController + Credential = $mockCredential + } + } + + $mockGetTargetResource_Disabled = { + return @{ + Identity = $null + ObjectClass = $mockObjectClass_Computer + Enabled = $false + DomainController = $mockDomainController + Credential = $mockCredential + } + } + } + + Context 'When the system is in the desired state' { + Context 'When the computer account is enabled in Active Directory' { + BeforeAll { + Mock -CommandName Get-TargetResource -MockWith $mockGetTargetResource_Enabled + + $setTargetResourceParameters = @{ + Identity = $mockComputerNamePresent + ObjectClass = $mockObjectClass_Computer + Enabled = $true + Verbose = $true + } + } + + It 'Should not call any mocks that changes state' { + { Set-TargetResource @setTargetResourceParameters } | Should -Not -Throw + + Assert-MockCalled -CommandName Set-DscADComputer -Exactly -Times 0 -Scope It + } + } + + Context 'When the computer account is disabled in Active Directory' { + BeforeAll { + Mock -CommandName Get-TargetResource -MockWith $mockGetTargetResource_Disabled + + $setTargetResourceParameters = @{ + Identity = $mockComputerNamePresent + ObjectClass = $mockObjectClass_Computer + Enabled = $false + Verbose = $true + } + } + + It 'Should not call any mocks that changes state' { + { Set-TargetResource @setTargetResourceParameters } | Should -Not -Throw + + Assert-MockCalled -CommandName Set-DscADComputer -Exactly -Times 0 -Scope It + } + } + } + + Context 'When the system is not in the desired state' { + Context 'When the computer account should be enabled in Active Directory' { + BeforeAll { + Mock -CommandName Get-TargetResource -MockWith $mockGetTargetResource_Disabled + + $setTargetResourceParameters = @{ + Identity = $mockComputerNamePresent + ObjectClass = $mockObjectClass_Computer + Enabled = $true + Verbose = $true + } + } + + It 'Should call the correct mocks' { + { Set-TargetResource @setTargetResourceParameters } | Should -Not -Throw + + Assert-MockCalled -CommandName Set-DscADComputer -ParameterFilter { + $PSBoundParameters.ContainsKey('Enabled') -and $Enabled -eq $true + } -Exactly -Times 0 -Scope It + } + } + + Context 'When the computer account should be disabled in Active Directory' { + BeforeAll { + Mock -CommandName Get-TargetResource -MockWith $mockGetTargetResource_Enabled + + $setTargetResourceParameters = @{ + Identity = $mockComputerNamePresent + ObjectClass = $mockObjectClass_Computer + Enabled = $false + Verbose = $true + } + } + + It 'Should call the correct mocks' { + { Set-TargetResource @setTargetResourceParameters } | Should -Not -Throw + + Assert-MockCalled -CommandName Set-DscADComputer -ParameterFilter { + $PSBoundParameters.ContainsKey('Enabled') -and $Enabled -eq $false + } -Exactly -Times 0 -Scope It + } + } + } + } + } +} +finally +{ + Invoke-TestCleanup +} From 2e41fe44cb1ede2fdaf03dd144da64d21378965f Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Tue, 4 Jun 2019 18:05:45 +0200 Subject: [PATCH 2/8] Add resource README.md --- DSCResources/MSFT_xADObjectEnabledState/README.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 DSCResources/MSFT_xADObjectEnabledState/README.md diff --git a/DSCResources/MSFT_xADObjectEnabledState/README.md b/DSCResources/MSFT_xADObjectEnabledState/README.md new file mode 100644 index 000000000..8a6978daa --- /dev/null +++ b/DSCResources/MSFT_xADObjectEnabledState/README.md @@ -0,0 +1,15 @@ +# Description + +This resource enforces the property `Enabled` on the object class *Computer*. + +>This resource could support other object classes like *msDS-ManagedServiceAccount*, +>*msDS-GroupManagedServiceAccount*, and *User*. But these object classes +>are not yet supported due to that other resources already enforces the +>`Enabled` property. If this resource should support another object class, +>then it should be made so that only one resource enforces the enabled +>property. This is to prevent a potential "ping-pong" behavior if both +>resource would be used in a configuration. + +## Requirements + +* Target machine must be running Windows Server 2008 R2 or later. From 69cf00599d3cf16bc71b07331f122bad2ab7a666 Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Tue, 4 Jun 2019 18:10:21 +0200 Subject: [PATCH 3/8] Fix examples --- .../2-CreateClusterComputerAccount_Config.ps1 | 7 ++++--- .../3-EnabledPrestagedClusterComputerAccount_Config.ps1 | 5 +++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/Examples/Resources/xADObjectEnabledState/2-CreateClusterComputerAccount_Config.ps1 b/Examples/Resources/xADObjectEnabledState/2-CreateClusterComputerAccount_Config.ps1 index 8599894ec..485ab0b6e 100644 --- a/Examples/Resources/xADObjectEnabledState/2-CreateClusterComputerAccount_Config.ps1 +++ b/Examples/Resources/xADObjectEnabledState/2-CreateClusterComputerAccount_Config.ps1 @@ -47,10 +47,11 @@ Configuration CreateClusterComputerAccount_Config xCluster 'CreateCluster' { - Name = 'CLU_CNO01' - StaticIPAddress = '192.168.100.20/24' + Name = 'CLU_CNO01' + StaticIPAddress = '192.168.100.20/24' + DomainAdministratorCredential = $DomainAdministratorCredential - DependsOn = '[xADComputer]ClusterAccount' + DependsOn = '[xADComputer]ClusterAccount' } xADObjectEnabledState 'EnforceEnabledPropertyToEnabled' diff --git a/Examples/Resources/xADObjectEnabledState/3-EnabledPrestagedClusterComputerAccount_Config.ps1 b/Examples/Resources/xADObjectEnabledState/3-EnabledPrestagedClusterComputerAccount_Config.ps1 index f2b17aedf..3152aaacb 100644 --- a/Examples/Resources/xADObjectEnabledState/3-EnabledPrestagedClusterComputerAccount_Config.ps1 +++ b/Examples/Resources/xADObjectEnabledState/3-EnabledPrestagedClusterComputerAccount_Config.ps1 @@ -40,8 +40,9 @@ Configuration EnabledPrestagedClusterComputerAccount_Config { xCluster 'CreateCluster' { - Name = 'CLU_CNO01' - StaticIPAddress = '192.168.100.20/24' + Name = 'CLU_CNO01' + StaticIPAddress = '192.168.100.20/24' + DomainAdministratorCredential = $DomainAdministratorCredential } xADComputerState 'EnforceEnabledPropertyToEnabled' From 66bbe935f261978a408b238bc703f91d0f1ff001 Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Tue, 4 Jun 2019 19:01:07 +0200 Subject: [PATCH 4/8] Fix typo in example --- .../3-EnabledPrestagedClusterComputerAccount_Config.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Examples/Resources/xADObjectEnabledState/3-EnabledPrestagedClusterComputerAccount_Config.ps1 b/Examples/Resources/xADObjectEnabledState/3-EnabledPrestagedClusterComputerAccount_Config.ps1 index 3152aaacb..76502d6b7 100644 --- a/Examples/Resources/xADObjectEnabledState/3-EnabledPrestagedClusterComputerAccount_Config.ps1 +++ b/Examples/Resources/xADObjectEnabledState/3-EnabledPrestagedClusterComputerAccount_Config.ps1 @@ -45,7 +45,7 @@ Configuration EnabledPrestagedClusterComputerAccount_Config DomainAdministratorCredential = $DomainAdministratorCredential } - xADComputerState 'EnforceEnabledPropertyToEnabled' + xADObjectEnabledState 'EnforceEnabledPropertyToEnabled' { Identity = 'CLU_CNO01' ObjectClass = 'Computer' From f639855165f081265007f3baddabff9da9a2572f Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Tue, 4 Jun 2019 19:25:24 +0200 Subject: [PATCH 5/8] Remove unnecessary localized string --- .../en-US/MSFT_xADObjectEnabledState.strings.psd1 | 1 - 1 file changed, 1 deletion(-) diff --git a/DSCResources/MSFT_xADObjectEnabledState/en-US/MSFT_xADObjectEnabledState.strings.psd1 b/DSCResources/MSFT_xADObjectEnabledState/en-US/MSFT_xADObjectEnabledState.strings.psd1 index 413140c08..c82f687d1 100644 --- a/DSCResources/MSFT_xADObjectEnabledState/en-US/MSFT_xADObjectEnabledState.strings.psd1 +++ b/DSCResources/MSFT_xADObjectEnabledState/en-US/MSFT_xADObjectEnabledState.strings.psd1 @@ -3,7 +3,6 @@ ConvertFrom-StringData @' RetrievingComputerAccount = Retrieving the information about the computer account '{0}' from Active Directory. (ADOES0001) ComputerAccountEnabled = The computer account is enabled. (ADOES0002) ComputerAccountDisabled = The computer account is disabled. (ADOES0003) - ComputerAccountIsAbsent = The computer account '{0}' is absent from Active Directory. (ADOES0004) FailedToRetrieveComputerAccount = Failed to retrieve the computer account '{0}' from Active Directory. (ADOES0005) TestConfiguration = Determining the current state of the enabled property of the object with the identity '{0}' and object class '{1}'. (ADOES0006) ComputerAccountInDesiredState = The property Enabled of the computer account '{0}' is in the desired state. (ADOES0007) From d19db0ec35edd2f03d7c8cf98a0d6dca6f7e47a0 Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Tue, 4 Jun 2019 19:34:06 +0200 Subject: [PATCH 6/8] Add Compare-TargetResourceState --- .../MSFT_xADObjectEnabledState.psm1 | 134 +++++++++++------- 1 file changed, 84 insertions(+), 50 deletions(-) diff --git a/DSCResources/MSFT_xADObjectEnabledState/MSFT_xADObjectEnabledState.psm1 b/DSCResources/MSFT_xADObjectEnabledState/MSFT_xADObjectEnabledState.psm1 index 304790717..e41e9f736 100644 --- a/DSCResources/MSFT_xADObjectEnabledState/MSFT_xADObjectEnabledState.psm1 +++ b/DSCResources/MSFT_xADObjectEnabledState/MSFT_xADObjectEnabledState.psm1 @@ -184,31 +184,7 @@ function Test-TargetResource $script:localizedData.TestConfiguration -f $Identity, $ObjectClass ) - $getTargetResourceParameters = @{ - Identity = $Identity - ObjectClass = $ObjectClass - Enabled = $Enabled - DomainController = $DomainController - Credential = $Credential - } - - # Need the @() around this to get a new array to enumerate. - @($getTargetResourceParameters.Keys) | ForEach-Object { - if (-not $PSBoundParameters.ContainsKey($_)) - { - $getTargetResourceParameters.Remove($_) - } - } - - $getTargetResourceResult = Get-TargetResource @getTargetResourceParameters - - $compareTargetResourceStateParameters = @{ - CurrentValues = $getTargetResourceResult - DesiredValues = $PSBoundParameters - Properties = @('Enabled') - } - - $compareTargetResourceStateResult = Compare-ResourcePropertyState @compareTargetResourceStateParameters + $compareTargetResourceStateResult = Compare-TargetResourceState @PSBoundParameters if ($false -in $compareTargetResourceStateResult.InDesiredState) { @@ -292,31 +268,7 @@ function Set-TargetResource $Credential ) - $getTargetResourceParameters = @{ - Identity = $Identity - ObjectClass = $ObjectClass - Enabled = $Enabled - DomainController = $DomainController - Credential = $Credential - } - - # Need the @() around this to get a new array to enumerate. - @($getTargetResourceParameters.Keys) | ForEach-Object { - if (-not $PSBoundParameters.ContainsKey($_)) - { - $getTargetResourceParameters.Remove($_) - } - } - - $getTargetResourceResult = Get-TargetResource @getTargetResourceParameters - - $compareTargetResourceStateParameters = @{ - CurrentValues = $getTargetResourceResult - DesiredValues = $PSBoundParameters - Properties = @('Enabled') - } - - $compareTargetResourceStateResult = Compare-ResourcePropertyState @compareTargetResourceStateParameters + $compareTargetResourceStateResult = Compare-TargetResourceState @PSBoundParameters # Get all properties that are not in desired state. $propertiesNotInDesiredState = $compareTargetResourceStateResult | Where-Object -FilterScript { @@ -353,6 +305,88 @@ function Set-TargetResource } } +<# + .SYNOPSIS + Sets the property Enabled of the Active Directory object. + + .PARAMETER Identity + Specifies the identity of an object that has the object class specified + in the parameter ObjectClass. When ObjectClass is set to 'Computer' then + this property can be set to either distinguished name, GUID (objectGUID), + security identifier (objectSid), or security Accounts Manager account + name (sAMAccountName). + + .PARAMETER ObjectClass + Specifies the object class. + + .PARAMETER Enabled + Specifies the value of the Enabled property. + + .PARAMETER DomainController + Specifies the Active Directory Domain Services instance to connect to + perform the task. + + .PARAMETER Credential + Specifies the user account credentials to use to perform the task. +#> +function Compare-TargetResourceState +{ + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [System.String] + $Identity, + + [Parameter(Mandatory = $true)] + [ValidateSet('Computer')] + [System.String] + $ObjectClass, + + [Parameter(Mandatory = $true)] + [ValidateNotNull()] + [System.Boolean] + $Enabled, + + [Parameter()] + [ValidateNotNull()] + [System.String] + $DomainController, + + [Parameter()] + [ValidateNotNull()] + [System.Management.Automation.PSCredential] + [System.Management.Automation.CredentialAttribute()] + $Credential + ) + + $getTargetResourceParameters = @{ + Identity = $Identity + ObjectClass = $ObjectClass + Enabled = $Enabled + DomainController = $DomainController + Credential = $Credential + } + + # Need the @() around this to get a new array to enumerate. + @($getTargetResourceParameters.Keys) | ForEach-Object { + if (-not $PSBoundParameters.ContainsKey($_)) + { + $getTargetResourceParameters.Remove($_) + } + } + + $getTargetResourceResult = Get-TargetResource @getTargetResourceParameters + + $compareTargetResourceStateParameters = @{ + CurrentValues = $getTargetResourceResult + DesiredValues = $PSBoundParameters + Properties = @('Enabled') + } + + return Compare-ResourcePropertyState @compareTargetResourceStateParameters +} + <# .SYNOPSIS This is a wrapper for Set-ADComputer. From 4b5e7b57d7f6781b385d69ceacb3f588a865879a Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Wed, 5 Jun 2019 18:18:55 +0200 Subject: [PATCH 7/8] Remove OrderNumber from integration tests --- Tests/Integration/MSFT_xADComputer.Integration.Tests.ps1 | 4 ---- .../MSFT_xADObjectEnableState.Integration.Tests.ps1 | 4 ---- 2 files changed, 8 deletions(-) diff --git a/Tests/Integration/MSFT_xADComputer.Integration.Tests.ps1 b/Tests/Integration/MSFT_xADComputer.Integration.Tests.ps1 index 98843c93b..6ae286f83 100644 --- a/Tests/Integration/MSFT_xADComputer.Integration.Tests.ps1 +++ b/Tests/Integration/MSFT_xADComputer.Integration.Tests.ps1 @@ -1,7 +1,3 @@ -# Make sure this is run in the correct order. -[Microsoft.DscResourceKit.IntegrationTest(OrderNumber = 2)] -param () - if ($env:APPVEYOR -eq $true) { Write-Warning -Message 'Integration test is not supported in AppVeyor.' diff --git a/Tests/Integration/MSFT_xADObjectEnableState.Integration.Tests.ps1 b/Tests/Integration/MSFT_xADObjectEnableState.Integration.Tests.ps1 index 9c388c78a..87f54a0bd 100644 --- a/Tests/Integration/MSFT_xADObjectEnableState.Integration.Tests.ps1 +++ b/Tests/Integration/MSFT_xADObjectEnableState.Integration.Tests.ps1 @@ -1,7 +1,3 @@ -# Make sure this is run in the correct order. -[Microsoft.DscResourceKit.IntegrationTest(OrderNumber = 3)] -param() - if ($env:APPVEYOR -eq $true) { Write-Warning -Message 'Integration test is not supported in AppVeyor.' From 6a722082db0d6c1c503780549bb6c09be2054922 Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Fri, 7 Jun 2019 09:45:50 +0200 Subject: [PATCH 8/8] Move Set-DscADComputer as a helper function --- .../MSFT_xADComputer/MSFT_xADComputer.psm1 | 24 ----------------- .../MSFT_xADObjectEnabledState.psm1 | 27 ------------------- .../xActiveDirectory.Common.psm1 | 24 +++++++++++++++++ 3 files changed, 24 insertions(+), 51 deletions(-) diff --git a/DSCResources/MSFT_xADComputer/MSFT_xADComputer.psm1 b/DSCResources/MSFT_xADComputer/MSFT_xADComputer.psm1 index 69bbab6d5..3bd5e1485 100644 --- a/DSCResources/MSFT_xADComputer/MSFT_xADComputer.psm1 +++ b/DSCResources/MSFT_xADComputer/MSFT_xADComputer.psm1 @@ -950,30 +950,6 @@ function Set-TargetResource } } -<# - .SYNOPSIS - This is a wrapper for Set-ADComputer. - - .PARAMETER Parameters - A hash table containing all parameters that will be passed trough to - Set-ADComputer. - - .NOTES - This is needed because of how Pester is unable to handle mocking the - cmdlet Set-ADComputer. -#> -function Set-DscADComputer -{ - param - ( - [Parameter(Mandatory = $true)] - [System.Collections.Hashtable] - $Parameters - ) - - Set-ADComputer @Parameters | Out-Null -} - <# .SYNOPSIS This evaluates the service principal names current state against the diff --git a/DSCResources/MSFT_xADObjectEnabledState/MSFT_xADObjectEnabledState.psm1 b/DSCResources/MSFT_xADObjectEnabledState/MSFT_xADObjectEnabledState.psm1 index e41e9f736..a5faabc37 100644 --- a/DSCResources/MSFT_xADObjectEnabledState/MSFT_xADObjectEnabledState.psm1 +++ b/DSCResources/MSFT_xADObjectEnabledState/MSFT_xADObjectEnabledState.psm1 @@ -4,9 +4,6 @@ $script:modulesFolderPath = Join-Path -Path $script:resourceModulePath -ChildPat $script:localizationModulePath = Join-Path -Path $script:modulesFolderPath -ChildPath 'xActiveDirectory.Common' Import-Module -Name (Join-Path -Path $script:localizationModulePath -ChildPath 'xActiveDirectory.Common.psm1') -$script:dscResourcePath = Split-Path -Path $PSScriptRoot -Parent -Import-Module -Name (Join-Path -Path $script:dscResourcePath -ChildPath '\MSFT_xADCommon\MSFT_xADCommon.psm1') - $script:localizedData = Get-LocalizedData -ResourceName 'MSFT_xADObjectEnabledState' <# @@ -386,27 +383,3 @@ function Compare-TargetResourceState return Compare-ResourcePropertyState @compareTargetResourceStateParameters } - -<# - .SYNOPSIS - This is a wrapper for Set-ADComputer. - - .PARAMETER Parameters - A hash table containing all parameters that will be passed trough to - Set-ADComputer. - - .NOTES - This is needed because of how Pester is unable to handle mocking the - cmdlet Set-ADComputer. -#> -function Set-DscADComputer -{ - param - ( - [Parameter(Mandatory = $true)] - [System.Collections.Hashtable] - $Parameters - ) - - Set-ADComputer @Parameters | Out-Null -} diff --git a/Modules/xActiveDirectory.Common/xActiveDirectory.Common.psm1 b/Modules/xActiveDirectory.Common/xActiveDirectory.Common.psm1 index c5dc3ee78..5039ae90f 100644 --- a/Modules/xActiveDirectory.Common/xActiveDirectory.Common.psm1 +++ b/Modules/xActiveDirectory.Common/xActiveDirectory.Common.psm1 @@ -1781,6 +1781,29 @@ function Assert-ADPSDrive } } +<# + .SYNOPSIS + This is a wrapper for Set-ADComputer. + + .PARAMETER Parameters + A hash table containing all parameters that will be passed trough to + Set-ADComputer. + + .NOTES + This is needed because of how Pester is unable to handle mocking the + cmdlet Set-ADComputer. Therefor there are no unit test for this function. +#> +function Set-DscADComputer +{ + param + ( + [Parameter(Mandatory = $true)] + [System.Collections.Hashtable] + $Parameters + ) + + Set-ADComputer @Parameters | Out-Null +} $script:localizedData = Get-LocalizedData -ResourceName 'xActiveDirectory.Common' -ScriptRoot $PSScriptRoot @@ -1817,4 +1840,5 @@ Export-ModuleMember -Function @( 'Compare-ResourcePropertyState' 'Test-DscPropertyState' 'Assert-ADPSDrive' + 'Set-DscADComputer' )