From bc7e0f6864f0d05f65fc17041036bf5799293339 Mon Sep 17 00:00:00 2001 From: Daniel Scott-Raynsford Date: Sun, 12 Jun 2016 22:05:07 +1200 Subject: [PATCH 1/9] First version of ODJ Request Change --- .../MSFT_xADCommon/MSFT_xADCommon.ps1 | 78 +- .../MSFT_xADComputer/MSFT_xADComputer.psm1 | 153 ++- .../MSFT_xADComputer.schema.mof | 3 +- .../en-US/MSFT_xADComputer.psd1 | 8 +- README.md | 910 ++++++++++-------- Tests/Unit/MSFT_xADComputer.Tests.ps1 | 204 ++-- 6 files changed, 742 insertions(+), 614 deletions(-) diff --git a/DSCResources/MSFT_xADCommon/MSFT_xADCommon.ps1 b/DSCResources/MSFT_xADCommon/MSFT_xADCommon.ps1 index ba73217fe..0af0b8414 100644 --- a/DSCResources/MSFT_xADCommon/MSFT_xADCommon.ps1 +++ b/DSCResources/MSFT_xADCommon/MSFT_xADCommon.ps1 @@ -8,7 +8,7 @@ data localizedString MembersIsEmptyError = The Members parameter is empty. At least one group member must be provided. IncludeAndExcludeConflictError = The member '{0}' is included in both '{1}' and '{2}' parameter values. The same member must not be included in both '{1}' and '{2}' parameter values. IncludeAndExcludeAreEmptyError = The '{0}' and '{1}' parameters are either both null or empty. At least one member must be specified in one of these parameters. - + CheckingMembers = Checking for '{0}' members. MembershipCountMismatch = Membership count is not correct. Expected '{0}' members, actual '{1}' members. MemberNotInDesiredState = Member '{0}' is not in the desired state. @@ -42,10 +42,20 @@ function Test-DomainMember { [CmdletBinding()] [OutputType([System.Boolean])] param ( ) - $isDomainMember = [System.Boolean] (Get-CimInstance -ClassName Win32_ComputerSystem -Verbose:$false).PartOfDomain; + $isDomainMember = [System.Boolean] (Get-CimInstance -ClassName Win32_ComputerSystem -Verbose:$false).PartOfDomain; return $isDomainMember; } + +# Internal function to get the domain name of the computer +function Get-DomainName { + [CmdletBinding()] + [OutputType([System.String])] + param ( ) + $domainName = [System.String] (Get-CimInstance -ClassName Win32_ComputerSystem -Verbose:$false).Domain; + return $domainName; +} # function Get-DomainName + # Internal function to build domain FQDN function Resolve-DomainFQDN { [CmdletBinding()] @@ -53,7 +63,7 @@ function Resolve-DomainFQDN { [Parameter(Mandatory)] [OutputType([System.String])] [System.String] $DomainName, - + [Parameter()] [AllowNull()] [System.String] $ParentDomainName ) @@ -102,7 +112,7 @@ function Get-ADObjectParentDN Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - + 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the @@ -128,7 +138,7 @@ function Get-ADObjectParentDN [System.String] $DN ) - + # https://www.uvm.edu/~gcd/2012/07/listing-parent-of-ad-object-in-powershell/ $distinguishedNameParts = $DN -split '(? function ConvertFrom-TimeSpan { @@ -397,7 +407,7 @@ function ConvertFrom-TimeSpan [ValidateNotNullOrEmpty()] [System.TimeSpan] $TimeSpan, - + [Parameter(Mandatory)] [ValidateSet('Seconds','Minutes','Hours','Days')] [System.String] @@ -415,22 +425,22 @@ function ConvertFrom-TimeSpan <# .SYNOPSIS Returns common AD cmdlet connection parameter for splatting - .PARAMETER CommonName + .PARAMETER CommonName When specified, a CommonName overrides theUsed by the xADUser cmdletReturns the Identity as the Name key. For example, the Get-ADUser, Set-ADUser and Remove-ADUser cmdlets take an Identity parameter, but the New-ADUser cmdlet uses the Name parameter. - .PARAMETER UseNameParameter + .PARAMETER UseNameParameter Returns the Identity as the Name key. For example, the Get-ADUser, Set-ADUser and Remove-ADUser cmdlets take an Identity parameter, but the New-ADUser cmdlet uses the Name parameter. .EXAMPLE $getADUserParams = Get-CommonADParameters @PSBoundParameters - + Returns connection parameters suitable for Get-ADUser using the splatted cmdlet parameters. .EXAMPLE $newADUserParams = Get-CommonADParameters @PSBoundParameters -UseNameParameter - + Returns connection parameters suitable for New-ADUser using the splatted cmdlet parameters. #> @@ -445,38 +455,38 @@ function Get-ADCommonParameters [Alias('UserName','GroupName','ComputerName')] [System.String] $Identity, - + [Parameter()] [ValidateNotNullOrEmpty()] [System.String] $CommonName, - + [Parameter()] [ValidateNotNull()] [Alias('DomainAdministratorCredential')] [System.Management.Automation.PSCredential] [System.Management.Automation.CredentialAttribute()] $Credential, - + [Parameter()] [ValidateNotNullOrEmpty()] [Alias('DomainController')] [System.String] $Server, - + [Parameter()] [System.Management.Automation.SwitchParameter] $UseNameParameter, - + [Parameter()] [System.Management.Automation.SwitchParameter] $PreferCommonName, - + ## Catch all to enable splatted $PSBoundParameters [Parameter(ValueFromRemainingArguments)] $RemainingArguments ) - + if ($UseNameParameter) { if ($PreferCommonName -and ($PSBoundParameters.ContainsKey('CommonName'))) @@ -497,17 +507,17 @@ function Get-ADCommonParameters $adConnectionParameters = @{ Identity = $Identity; } } } - + if ($Credential) { $adConnectionParameters['Credential'] = $Credential; } - + if ($Server) { $adConnectionParameters['Server'] = $Server; } - + return $adConnectionParameters; } #end function Get-ADCommonParameters diff --git a/DSCResources/MSFT_xADComputer/MSFT_xADComputer.psm1 b/DSCResources/MSFT_xADComputer/MSFT_xADComputer.psm1 index c53c12499..6ddede938 100644 --- a/DSCResources/MSFT_xADComputer/MSFT_xADComputer.psm1 +++ b/DSCResources/MSFT_xADComputer/MSFT_xADComputer.psm1 @@ -8,11 +8,11 @@ if (Test-Path -Path (Join-Path -Path $moduleRoot -ChildPath $PSUICulture)) $importLocalizedDataParams = @{ BindingVariable = 'LocalizedData' Filename = 'MSFT_xADComputer.psd1' - BaseDirectory = $moduleRoot + BaseDirectory = $moduleRoot UICulture = $culture } Import-LocalizedData @importLocalizedDataParams -#endregion +#endregion ## Create a property map that maps the DSC resource parameters to the ## Active Directory computer attributes. @@ -39,65 +39,68 @@ function Get-TargetResource # Common Name [Parameter(Mandatory)] [System.String] $ComputerName, - + [ValidateSet('Present', 'Absent')] [System.String] $Ensure = 'Present', - + [ValidateNotNull()] [System.String] $UserPrincipalName, - + [ValidateNotNull()] [System.String] $DisplayName, - + [ValidateNotNull()] [System.String] $Path, - + [ValidateNotNull()] [System.String] $Location, - + [ValidateNotNull()] [System.String] $DnsHostName, - + [ValidateNotNull()] [System.String[]] $ServicePrincipalNames, - + [ValidateNotNull()] [System.String] $Description, ## Computer's manager specified as a Distinguished Name (DN) [ValidateNotNull()] [System.String] $Manager, - + + [ValidateNotNull()] + [System.String] $ODJRequestFile, + [ValidateNotNull()] [System.Boolean] $Enabled = $true, [ValidateNotNull()] [System.String] $DomainController, - + ## Ideally this should just be called 'Credential' but is here for consistency with xADUser [ValidateNotNull()] [System.Management.Automation.PSCredential] [System.Management.Automation.CredentialAttribute()] $DomainAdministratorCredential ) - + Assert-Module -ModuleName 'ActiveDirectory'; Import-Module -Name 'ActiveDirectory' -Verbose:$false; try { $adCommonParameters = Get-ADCommonParameters @PSBoundParameters; - + $adProperties = @(); ## Create an array of the AD property names to retrieve from the property map foreach ($property in $adPropertyMap) { - + if ($property.ADProperty) { $adProperties += $property.ADProperty; } - else + else { $adProperties += $property.Parameter; } @@ -125,6 +128,7 @@ function Get-TargetResource SID = $adComputer.SID; ## Read-only property Ensure = $Ensure; DomainController = $DomainController; + ODJRequestFile = ''; # This property can only be set, not read. } ## Retrieve each property from the ADPropertyMap and add to the hashtable @@ -170,41 +174,44 @@ function Test-TargetResource # Common Name [Parameter(Mandatory)] [System.String] $ComputerName, - + [ValidateSet('Present', 'Absent')] [System.String] $Ensure = 'Present', - + [ValidateNotNull()] [System.String] $UserPrincipalName, - + [ValidateNotNull()] [System.String] $DisplayName, - + [ValidateNotNull()] [System.String] $Path, - + [ValidateNotNull()] [System.String] $Location, - + [ValidateNotNull()] [System.String] $DnsHostName, - + [ValidateNotNull()] [System.String[]] $ServicePrincipalNames, - + [ValidateNotNull()] [System.String] $Description, ## Computer's manager specified as a Distinguished Name (DN) [ValidateNotNull()] [System.String] $Manager, - + + [ValidateNotNull()] + [System.String] $ODJRequestFile, + [ValidateNotNull()] [System.Boolean] $Enabled = $true, [ValidateNotNull()] [System.String] $DomainController, - + ## Ideally this should just be called 'Credential' but is here for backwards compatibility [ValidateNotNull()] [System.Management.Automation.PSCredential] @@ -229,7 +236,7 @@ function Test-TargetResource ## Add ensure and enabled as they may not be explicitly passed and we want to enumerate them $PSBoundParameters['Ensure'] = $Ensure; $PSBoundParameters['Enabled'] = $Enabled; - + foreach ($parameter in $PSBoundParameters.Keys) { if ($targetResource.ContainsKey($parameter)) @@ -287,41 +294,44 @@ function Set-TargetResource # Common Name [Parameter(Mandatory)] [System.String] $ComputerName, - + [ValidateSet('Present', 'Absent')] [System.String] $Ensure = 'Present', - + [ValidateNotNull()] [System.String] $UserPrincipalName, - + [ValidateNotNull()] [System.String] $DisplayName, - + [ValidateNotNull()] [System.String] $Path, - + [ValidateNotNull()] [System.String] $Location, - + [ValidateNotNull()] [System.String] $DnsHostName, - + [ValidateNotNull()] [System.String[]] $ServicePrincipalNames, - + [ValidateNotNull()] [System.String] $Description, ## Computer's manager specified as a Distinguished Name (DN) [ValidateNotNull()] [System.String] $Manager, - + + [ValidateNotNull()] + [System.String] $ODJRequestFile, + [ValidateNotNull()] [System.Boolean] $Enabled = $true, [ValidateNotNull()] [System.String] $DomainController, - + ## Ideally this should just be called 'Credential' but is here for backwards compatibility [ValidateNotNull()] [System.Management.Automation.PSCredential] @@ -339,14 +349,54 @@ function Set-TargetResource { if ($targetResource.Ensure -eq 'Absent') { ## Computer does not exist and needs creating - $newADComputerParams = Get-ADCommonParameters @PSBoundParameters -UseNameParameter; - if ($PSBoundParameters.ContainsKey('Path')) + if ($ODJRequestFile) { - Write-Verbose -Message ($LocalizedData.UpdatingADComputerProperty -f 'Path', $Path); - $newADComputerParams['Path'] = $Path; + ## Use DJOIN to create the computer account as well as the ODJ Request file. + Write-Verbose -Message ($LocalizedData.ODJRequestStartMessage -f ` + $DomainName,$ComputerName,$RequestFile) + + # This should only be performed on a Domain Member, so detect the Domain Name. + $DomainName = Get-DomainName + $DJoinParameters = @( + '/PROVISION' + '/DOMAIN',$DomainName + '/MACHINE',$ComputerName ) + if ($PSBoundParameters.ContainsKey('Path')) + { + $DJoinParameters += @( '/MACHINEOU',$Path ) + } # if + + if ($PSBoundParameters.ContainsKey('DomainController')) + { + $DJoinParameters += @( '/DCNAME',$DomainController ) + } # if + + $DJoinParameters += @( '/SAVEFILE',$ODJRequestFile ) + $Result = & djoin.exe @DjoinParameters + + if ($LASTEXITCODE -ne 0) + { + $errorId = 'ODJRequestError' + $errorMessage = $($LocalizedData.ODJRequestError ` + -f $LASTEXITCODE,$Result) + ThrowInvalidOperationError -ErrorId $errorId -ErrorMessage $errorMessage + } # if + + Write-Verbose -Message ($LocalizedData.ODJRequestCompleteMessage -f ` + $DomainName,$ComputerName,$RequestFile) } - Write-Verbose -Message ($LocalizedData.AddingADComputer -f $ComputerName); - New-ADComputer @newADComputerParams; + else + { + ## Create the computer account using New-ADComputer + $newADComputerParams = Get-ADCommonParameters @PSBoundParameters -UseNameParameter; + if ($PSBoundParameters.ContainsKey('Path')) + { + Write-Verbose -Message ($LocalizedData.UpdatingADComputerProperty -f 'Path', $Path); + $newADComputerParams['Path'] = $Path; + } + Write-Verbose -Message ($LocalizedData.AddingADComputer -f $ComputerName); + New-ADComputer @newADComputerParams; + } # if ## Now retrieve the newly created computer $targetResource = Get-TargetResource @PSBoundParameters; } @@ -387,7 +437,7 @@ function Set-TargetResource { ## Find the associated AD property $adProperty = $adPropertyMap | Where-Object { $_.Parameter -eq $parameter }; - + if ([System.String]::IsNullOrEmpty($adProperty)) { ## We can't do anything with an empty AD property! @@ -435,20 +485,20 @@ function Set-TargetResource } } #end if replace existing value } - + } #end if TargetResource parameter } #end foreach PSBoundParameter - + ## Only pass -Remove and/or -Replace if we have something to set/change if ($replaceComputerProperties.Count -gt 0) - { + { $setADComputerParams['Replace'] = $replaceComputerProperties; } if ($removeComputerProperties.Count -gt 0) - { + { $setADComputerParams['Remove'] = $removeComputerProperties; } - + Write-Verbose -Message ($LocalizedData.UpdatingADComputer -f $ComputerName); [ref] $null = Set-ADComputer @setADComputerParams -Enabled $Enabled; } @@ -462,9 +512,10 @@ function Set-TargetResource } #end function Set-TargetResource - ## Import the common AD functions -$adCommonFunctions = Join-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -ChildPath '\MSFT_xADCommon\MSFT_xADCommon.ps1'; +$adCommonFunctions = Join-Path ` + -Path (Split-Path -Path $PSScriptRoot -Parent) ` + -ChildPath '\MSFT_xADCommon\MSFT_xADCommon.ps1'; . $adCommonFunctions; Export-ModuleMember -Function *-TargetResource diff --git a/DSCResources/MSFT_xADComputer/MSFT_xADComputer.schema.mof b/DSCResources/MSFT_xADComputer/MSFT_xADComputer.schema.mof index 86ac1e09c..1ed030629 100644 --- a/DSCResources/MSFT_xADComputer/MSFT_xADComputer.schema.mof +++ b/DSCResources/MSFT_xADComputer/MSFT_xADComputer.schema.mof @@ -1,4 +1,4 @@ -[ClassVersion("1.0.0.0"), FriendlyName("xADComputer")] +[ClassVersion("1.0.0.0"), FriendlyName("xADComputer")] class MSFT_xADComputer : OMI_BaseResource { [Key, Description("Specifies the name of the computer")] String ComputerName; @@ -13,6 +13,7 @@ class MSFT_xADComputer : OMI_BaseResource [Write, Description("Specifies the user or group Distinguished Name that manages the computer object")] String Manager; [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 DomainAdministratorCredential; + [Write, Description("Specifies the full path to the Offline Domain Join Request file to create.")] String ODJRequestFile; [Write, ValueMap{"Present", "Absent"},Values{"Present", "Absent"}] String Ensure; [Read, Description("Returns the X.500 path of the computer object")] String DistinguishedName; [Read, Description("Returns the security identifier of the computer object")] String SID; diff --git a/DSCResources/MSFT_xADComputer/en-US/MSFT_xADComputer.psd1 b/DSCResources/MSFT_xADComputer/en-US/MSFT_xADComputer.psd1 index 7387a59c7..acb3811cc 100644 --- a/DSCResources/MSFT_xADComputer/en-US/MSFT_xADComputer.psd1 +++ b/DSCResources/MSFT_xADComputer/en-US/MSFT_xADComputer.psd1 @@ -2,7 +2,7 @@ ConvertFrom-StringData @' RoleNotFoundError = Please ensure that the PowerShell module for role '{0}' is installed. RetrievingADComputerError = Error looking up Active Directory computer '{0}'. - + RetrievingADComputer = Retrieving Active Directory computer '{0}' ... CreatingADDomainConnection = Creating connection to Active Directory domain ... ADComputerIsPresent = Active Directory computer '{0}' is present. @@ -10,7 +10,7 @@ ConvertFrom-StringData @' ADComputerNotDesiredPropertyState = Computer '{0}' property is NOT in the desired state. Expected '{1}', actual '{2}'. ADComputerInDesiredState = Active Directory computer '{0}' is in the desired state. ADComputerNotInDesiredState = Active Directory computer '{0}' is NOT in the desired state. - + AddingADComputer = Adding Active Directory computer '{0}'. RemovingADComputer = Removing Active Directory computer '{0}'. UpdatingADComputer = Updating Active Directory computer '{0}'. @@ -18,4 +18,8 @@ ConvertFrom-StringData @' RemovingADComputerProperty = Removing computer property '{0}' with '{1}'. MovingADComputer = Moving computer from '{0}' to '{1}'. RenamingADComputer = Renaming computer from '{0}' to '{1}'. + + ODJRequestStartMessage=Attempting to create the ODJ request file '{2}' for computer '{1}' in Domain '{0}'. + ODJRequestCompleteMessage=The ODJ request file '{2}' for computer '{1}' in Domain '{0}' has been provisioned successfully. + ODJRequestError=Error {0} occured provisioning the computer using ODJ- {1}. '@ diff --git a/README.md b/README.md index da2eab7f4..6f98bc43c 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ These DSC Resources allow you to configure new domains, child domains, and high * **xADDomainTrust** establishes cross-domain trusts. * **xADGroup** modifies and removes Active Directory groups. * **xADOrganizationalUnit** creates and deletes Active Directory OUs. -* **xADUser** modifies and removes Active Directory Users. +* **xADUser** modifies and removes Active Directory Users. * **xWaitForDomain** waits for new, remote domain to setup. (Note: the RSAT tools will not be installed when these resources are used to configure AD.) @@ -33,14 +33,14 @@ These DSC Resources allow you to configure new domains, child domains, and high * **ParentDomainName**: Fully qualified name of the parent domain (optional). * **DomainAdministratorCredential**: Credentials used to query for domain existence. * __Note: These are NOT used during domain creation.__ -(AD sets the localadmin credentials as new domain administrator credentials during setup.) +(AD sets the localadmin credentials as new domain administrator credentials during setup.) * **SafemodeAdministratorPassword**: Password for the administrator account when the computer is started in Safe Mode. * **DnsDelegationCredential**: Credential used for creating DNS delegation (optional). * **DomainNetBIOSName**: Specifies the NetBIOS name for the new domain (optional). * If not specified, then the default is automatically computed from the value of the DomainName parameter. * **DatabasePath**: Specifies the fully qualified, non-Universal Naming Convention (UNC) path to a directory on a fixed disk of the local computer that contains the domain database (optional). * **LogPath**: Specifies the fully qualified, non-UNC path to a directory on a fixed disk of the local computer where the log file for this operation will be written (optional). -* **SysvolPath**: Specifies the fully qualified, non-UNC path to a directory on a fixed disk of the local computer where the Sysvol file will be written. (optional) +* **SysvolPath**: Specifies the fully qualified, non-UNC path to a directory on a fixed disk of the local computer where the Sysvol file will be written. (optional) ### **xADDomainController** @@ -62,7 +62,7 @@ These DSC Resources allow you to configure new domains, child domains, and high * **Ensure**: Specifies whether the given user is present or absent (optional). * If not specified, this value defaults to Present. * **DomainController**: Specifies the Active Directory Domain Services instance to connect to (optional). - * This is only required if not executing the task on a domain controller. + * This is only required if not executing the task on a domain controller. * **DomainAdministratorCredential**: User account credentials used to perform the task (optional). * This is only required if not executing the task on a domain controller or using the -DomainController parameter. * **CommonName**: Specifies the user's CN of the user account (optional). @@ -122,23 +122,23 @@ These DSC Resources allow you to configure new domains, child domains, and high ### **xADDomainTrust** -* **Ensure**: Specifies whether the domain trust is present or absent -* **TargetDomainAdministratorCredential**: Credentials to authenticate to the target domain -* **TargetDomainName**: Name of the AD domain that is being trusted -* **TrustType**: Type of trust -* **TrustDirection**: Direction of trust, the values for which may be Bidirectional,Inbound, or Outbound -* **SourceDomainName**: Name of the AD domain that is requesting the trust +* **Ensure**: Specifies whether the domain trust is present or absent +* **TargetDomainAdministratorCredential**: Credentials to authenticate to the target domain +* **TargetDomainName**: Name of the AD domain that is being trusted +* **TrustType**: Type of trust +* **TrustDirection**: Direction of trust, the values for which may be Bidirectional,Inbound, or Outbound +* **SourceDomainName**: Name of the AD domain that is requesting the trust ### **xADRecycleBin** -The xADRecycleBin DSC resource will enable the Active Directory Recycle Bin feature for the target forest. -This resource first verifies that the forest mode is Windows Server 2008 R2 or greater. If the forest mode -is insufficient, then the resource will exit with an error message. The change is executed against the -Domain Naming Master FSMO of the forest. +The xADRecycleBin DSC resource will enable the Active Directory Recycle Bin feature for the target forest. +This resource first verifies that the forest mode is Windows Server 2008 R2 or greater. If the forest mode +is insufficient, then the resource will exit with an error message. The change is executed against the +Domain Naming Master FSMO of the forest. (Note: This resource is compatible with a Windows 2008 R2 or above target node. ) -* **ForestFQDN**: Fully qualified domain name of forest to enable Active Directory Recycle Bin. -* **EnterpriseAdministratorCredential**: Credential with Enterprise Administrator rights to the forest. -* **RecycleBinEnabled**: Read-only. Returned by Get. -* **ForestMode**: Read-only. Returned by Get. +* **ForestFQDN**: Fully qualified domain name of forest to enable Active Directory Recycle Bin. +* **EnterpriseAdministratorCredential**: Credential with Enterprise Administrator rights to the forest. +* **RecycleBinEnabled**: Read-only. Returned by Get. +* **ForestMode**: Read-only. Returned by Get. ### **xADGroup** The xADGroup DSC resource will manage groups within Active Directory. @@ -215,23 +215,28 @@ The xADComputer DSC resource will manage computer accounts within Active Directo * **UserPrincipalName** :Specifies the UPN assigned to the computer account (optional). * **DisplayName**: "Specifies the display name of the computer (optional). * **Path**: Specifies the X.500 path of the container where the computer is located (optional). -* **Description**: Specifies a description of the computer object (optional). +* **Description**: Specifies a description of the computer object (optional). * **Enabled**: Specifies if the computer account is enabled (optional). * **Manager**: Specifies the user or group Distinguished Name that manages the computer object (optional). * Valid values are the user's or group's DistinguishedName, ObjectGUID, SID or SamAccountName. * **DomainController**: Specifies the Active Directory Domain Services instance to connect to perform the task (optional). * **DomainAdministratorCredential**: Specifies the user account credentials to use to perform the task (optional). +* **ODJRequestFile**: Specifies the full path to the Offline Domain Join Request file to create (optional). * **Ensure**: Specifies whether the computer account is present or absent. * Valid values are 'Present' and 'Absent'. * It not specified, it defaults to 'Present'. * **DistinguishedName**: Returns the X.500 path of the computer object (read-only). * **SID**: Returns the security identifier of the computer object (read-only). +Note: An ODJ Request file will only be created when a computer account is first created in the domain. +Setting an ODJ Request file path for a configuration that creates a computer account that already exists will not cause the file to be created. + ## Versions ### Unreleased * xADDomainController: Customer identified two cases of incorrect variables being called in Verbose output messages. Corrected. * xADComputer: New resource added. +* xADComputer: Added ODJRequestFile support. ### 2.11.0.0 * xWaitForADDomain: Made explicit credentials optional and other various updates @@ -311,21 +316,133 @@ In the following example configuration, a highly available domain is created by This example uses the xWaitForDomain resource to ensure that the domain is present before the second domain controller is added. ```powershell -# A configuration to Create High Availability Domain Controller +# A configuration to Create High Availability Domain Controller +Configuration AssertHADC +{ + param + ( + [Parameter(Mandatory)] + [pscredential]$safemodeAdministratorCred, + [Parameter(Mandatory)] + [pscredential]$domainCred, + [Parameter(Mandatory)] + [pscredential]$DNSDelegationCred, + [Parameter(Mandatory)] + [pscredential]$NewADUserCred + ) + Import-DscResource -ModuleName xActiveDirectory + Node $AllNodes.Where{$_.Role -eq "Primary DC"}.Nodename + { + WindowsFeature ADDSInstall + { + Ensure = "Present" + Name = "AD-Domain-Services" + } + xADDomain FirstDS + { + DomainName = $Node.DomainName + DomainAdministratorCredential = $domainCred + SafemodeAdministratorPassword = $safemodeAdministratorCred + DnsDelegationCredential = $DNSDelegationCred + DependsOn = "[WindowsFeature]ADDSInstall" + } + xWaitForADDomain DscForestWait + { + DomainName = $Node.DomainName + DomainUserCredential = $domainCred + RetryCount = $Node.RetryCount + RetryIntervalSec = $Node.RetryIntervalSec + DependsOn = "[xADDomain]FirstDS" + } + xADUser FirstUser + { + DomainName = $Node.DomainName + DomainAdministratorCredential = $domainCred + UserName = "dummy" + Password = $NewADUserCred + Ensure = "Present" + DependsOn = "[xWaitForADDomain]DscForestWait" + } + } + Node $AllNodes.Where{$_.Role -eq "Replica DC"}.Nodename + { + WindowsFeature ADDSInstall + { + Ensure = "Present" + Name = "AD-Domain-Services" + } + xWaitForADDomain DscForestWait + { + DomainName = $Node.DomainName + DomainUserCredential = $domainCred + RetryCount = $Node.RetryCount + RetryIntervalSec = $Node.RetryIntervalSec + DependsOn = "[WindowsFeature]ADDSInstall" + } + xADDomainController SecondDC + { + DomainName = $Node.DomainName + DomainAdministratorCredential = $domainCred + SafemodeAdministratorPassword = $safemodeAdministratorCred + DnsDelegationCredential = $DNSDelegationCred + DependsOn = "[xWaitForADDomain]DscForestWait" + } + } +} +# Configuration Data for AD +$ConfigData = @{ + AllNodes = @( + @{ + Nodename = "dsc-testNode1" + Role = "Primary DC" + DomainName = "dsc-test.contoso.com" + CertificateFile = "C:\publicKeys\targetNode.cer" + Thumbprint = "AC23EA3A9E291A75757A556D0B71CBBF8C4F6FD8" + RetryCount = 20 + RetryIntervalSec = 30 + }, + @{ + Nodename = "dsc-testNode2" + Role = "Replica DC" + DomainName = "dsc-test.contoso.com" + CertificateFile = "C:\publicKeys\targetNode.cer" + Thumbprint = "AC23EA3A9E291A75757A556D0B71CBBF8C4F6FD8" + RetryCount = 20 + RetryIntervalSec = 30 + } + ) +} +AssertHADC -configurationData $ConfigData ` +-safemodeAdministratorCred (Get-Credential -Message "New Domain Safe Mode Admin Credentials") ` +-domainCred (Get-Credential -Message "New Domain Admin Credentials") ` +-DNSDelegationCred (Get-Credential -Message "Credentials to Setup DNS Delegation") ` +-NewADUserCred (Get-Credential -Message "New AD User Credentials") +Start-DscConfiguration -Wait -Force -Verbose -ComputerName "dsc-testNode1" -Path $PSScriptRoot\AssertHADC ` +-Credential (Get-Credential -Message "Local Admin Credentials on Remote Machine") +Start-DscConfiguration -Wait -Force -Verbose -ComputerName "dsc-testNode2" -Path $PSScriptRoot\AssertHADC ` +-Credential (Get-Credential -Message "Local Admin Credentials on Remote Machine") +# A configuration to Create High Availability Domain Controller + Configuration AssertHADC { + param ( [Parameter(Mandatory)] [pscredential]$safemodeAdministratorCred, + [Parameter(Mandatory)] [pscredential]$domainCred, + [Parameter(Mandatory)] [pscredential]$DNSDelegationCred, + [Parameter(Mandatory)] [pscredential]$NewADUserCred ) + Import-DscResource -ModuleName xActiveDirectory + Node $AllNodes.Where{$_.Role -eq "Primary DC"}.Nodename { WindowsFeature ADDSInstall @@ -333,6 +450,7 @@ Configuration AssertHADC Ensure = "Present" Name = "AD-Domain-Services" } + xADDomain FirstDS { DomainName = $Node.DomainName @@ -341,6 +459,7 @@ Configuration AssertHADC DnsDelegationCredential = $DNSDelegationCred DependsOn = "[WindowsFeature]ADDSInstall" } + xWaitForADDomain DscForestWait { DomainName = $Node.DomainName @@ -349,6 +468,7 @@ Configuration AssertHADC RetryIntervalSec = $Node.RetryIntervalSec DependsOn = "[xADDomain]FirstDS" } + xADUser FirstUser { DomainName = $Node.DomainName @@ -358,7 +478,9 @@ Configuration AssertHADC Ensure = "Present" DependsOn = "[xWaitForADDomain]DscForestWait" } + } + Node $AllNodes.Where{$_.Role -eq "Replica DC"}.Nodename { WindowsFeature ADDSInstall @@ -366,6 +488,7 @@ Configuration AssertHADC Ensure = "Present" Name = "AD-Domain-Services" } + xWaitForADDomain DscForestWait { DomainName = $Node.DomainName @@ -374,6 +497,7 @@ Configuration AssertHADC RetryIntervalSec = $Node.RetryIntervalSec DependsOn = "[WindowsFeature]ADDSInstall" } + xADDomainController SecondDC { DomainName = $Node.DomainName @@ -384,163 +508,44 @@ Configuration AssertHADC } } } -# Configuration Data for AD + +# Configuration Data for AD + $ConfigData = @{ AllNodes = @( @{ Nodename = "dsc-testNode1" Role = "Primary DC" DomainName = "dsc-test.contoso.com" - CertificateFile = "C:\publicKeys\targetNode.cer" - Thumbprint = "AC23EA3A9E291A75757A556D0B71CBBF8C4F6FD8" - RetryCount = 20 - RetryIntervalSec = 30 + CertificateFile = "C:\publicKeys\targetNode.cer" + Thumbprint = "AC23EA3A9E291A75757A556D0B71CBBF8C4F6FD8" + RetryCount = 20 + RetryIntervalSec = 30 }, + @{ Nodename = "dsc-testNode2" Role = "Replica DC" DomainName = "dsc-test.contoso.com" - CertificateFile = "C:\publicKeys\targetNode.cer" - Thumbprint = "AC23EA3A9E291A75757A556D0B71CBBF8C4F6FD8" - RetryCount = 20 - RetryIntervalSec = 30 + CertificateFile = "C:\publicKeys\targetNode.cer" + Thumbprint = "AC23EA3A9E291A75757A556D0B71CBBF8C4F6FD8" + RetryCount = 20 + RetryIntervalSec = 30 } ) } + AssertHADC -configurationData $ConfigData ` -safemodeAdministratorCred (Get-Credential -Message "New Domain Safe Mode Admin Credentials") ` -domainCred (Get-Credential -Message "New Domain Admin Credentials") ` -DNSDelegationCred (Get-Credential -Message "Credentials to Setup DNS Delegation") ` -NewADUserCred (Get-Credential -Message "New AD User Credentials") + Start-DscConfiguration -Wait -Force -Verbose -ComputerName "dsc-testNode1" -Path $PSScriptRoot\AssertHADC ` -Credential (Get-Credential -Message "Local Admin Credentials on Remote Machine") + Start-DscConfiguration -Wait -Force -Verbose -ComputerName "dsc-testNode2" -Path $PSScriptRoot\AssertHADC ` -Credential (Get-Credential -Message "Local Admin Credentials on Remote Machine") -# A configuration to Create High Availability Domain Controller - -Configuration AssertHADC -{ - - param - ( - [Parameter(Mandatory)] - [pscredential]$safemodeAdministratorCred, - - [Parameter(Mandatory)] - [pscredential]$domainCred, - - [Parameter(Mandatory)] - [pscredential]$DNSDelegationCred, - - [Parameter(Mandatory)] - [pscredential]$NewADUserCred - ) - - Import-DscResource -ModuleName xActiveDirectory - - Node $AllNodes.Where{$_.Role -eq "Primary DC"}.Nodename - { - WindowsFeature ADDSInstall - { - Ensure = "Present" - Name = "AD-Domain-Services" - } - - xADDomain FirstDS - { - DomainName = $Node.DomainName - DomainAdministratorCredential = $domainCred - SafemodeAdministratorPassword = $safemodeAdministratorCred - DnsDelegationCredential = $DNSDelegationCred - DependsOn = "[WindowsFeature]ADDSInstall" - } - - xWaitForADDomain DscForestWait - { - DomainName = $Node.DomainName - DomainUserCredential = $domainCred - RetryCount = $Node.RetryCount - RetryIntervalSec = $Node.RetryIntervalSec - DependsOn = "[xADDomain]FirstDS" - } - - xADUser FirstUser - { - DomainName = $Node.DomainName - DomainAdministratorCredential = $domainCred - UserName = "dummy" - Password = $NewADUserCred - Ensure = "Present" - DependsOn = "[xWaitForADDomain]DscForestWait" - } - - } - - Node $AllNodes.Where{$_.Role -eq "Replica DC"}.Nodename - { - WindowsFeature ADDSInstall - { - Ensure = "Present" - Name = "AD-Domain-Services" - } - - xWaitForADDomain DscForestWait - { - DomainName = $Node.DomainName - DomainUserCredential = $domainCred - RetryCount = $Node.RetryCount - RetryIntervalSec = $Node.RetryIntervalSec - DependsOn = "[WindowsFeature]ADDSInstall" - } - - xADDomainController SecondDC - { - DomainName = $Node.DomainName - DomainAdministratorCredential = $domainCred - SafemodeAdministratorPassword = $safemodeAdministratorCred - DnsDelegationCredential = $DNSDelegationCred - DependsOn = "[xWaitForADDomain]DscForestWait" - } - } -} - -# Configuration Data for AD - -$ConfigData = @{ - AllNodes = @( - @{ - Nodename = "dsc-testNode1" - Role = "Primary DC" - DomainName = "dsc-test.contoso.com" - CertificateFile = "C:\publicKeys\targetNode.cer" - Thumbprint = "AC23EA3A9E291A75757A556D0B71CBBF8C4F6FD8" - RetryCount = 20 - RetryIntervalSec = 30 - }, - - @{ - Nodename = "dsc-testNode2" - Role = "Replica DC" - DomainName = "dsc-test.contoso.com" - CertificateFile = "C:\publicKeys\targetNode.cer" - Thumbprint = "AC23EA3A9E291A75757A556D0B71CBBF8C4F6FD8" - RetryCount = 20 - RetryIntervalSec = 30 - } - ) -} - -AssertHADC -configurationData $ConfigData ` --safemodeAdministratorCred (Get-Credential -Message "New Domain Safe Mode Admin Credentials") ` --domainCred (Get-Credential -Message "New Domain Admin Credentials") ` --DNSDelegationCred (Get-Credential -Message "Credentials to Setup DNS Delegation") ` --NewADUserCred (Get-Credential -Message "New AD User Credentials") - -Start-DscConfiguration -Wait -Force -Verbose -ComputerName "dsc-testNode1" -Path $PSScriptRoot\AssertHADC ` --Credential (Get-Credential -Message "Local Admin Credentials on Remote Machine") - -Start-DscConfiguration -Wait -Force -Verbose -ComputerName "dsc-testNode2" -Path $PSScriptRoot\AssertHADC ` --Credential (Get-Credential -Message "Local Admin Credentials on Remote Machine") ``` @@ -549,132 +554,132 @@ Start-DscConfiguration -Wait -Force -Verbose -ComputerName "dsc-testNode2" -Path In this example, we create a domain, and then create a child domain on another node. ```powershell -# Configuration to Setup Parent Child Domains - -Configuration AssertParentChildDomains -{ - param - ( - [Parameter(Mandatory)] - [pscredential]$safemodeAdministratorCred, - - [Parameter(Mandatory)] - [pscredential]$domainCred, - - [Parameter(Mandatory)] - [pscredential]$DNSDelegationCred, - - [Parameter(Mandatory)] - [pscredential]$NewADUserCred - ) - - Import-DscResource -ModuleName xActiveDirectory - - Node $AllNodes.Where{$_.Role -eq "Parent DC"}.Nodename - { - WindowsFeature ADDSInstall - { - Ensure = "Present" - Name = "AD-Domain-Services" - } - - xADDomain FirstDS - { - DomainName = $Node.DomainName - DomainAdministratorCredential = $domainCred - SafemodeAdministratorPassword = $safemodeAdministratorCred - DnsDelegationCredential = $DNSDelegationCred - DependsOn = "[WindowsFeature]ADDSInstall" - } - - xWaitForADDomain DscForestWait - { - DomainName = $Node.DomainName - DomainUserCredential = $domainCred - RetryCount = $Node.RetryCount - RetryIntervalSec = $Node.RetryIntervalSec - DependsOn = "[xADDomain]FirstDS" - } - - xADUser FirstUser - { - DomainName = $Node.DomainName - DomainAdministratorCredential = $domaincred - UserName = "dummy" - Password = $NewADUserCred - Ensure = "Present" - DependsOn = "[xWaitForADDomain]DscForestWait" - } - - } - - Node $AllNodes.Where{$_.Role -eq "Child DC"}.Nodename - { - WindowsFeature ADDSInstall - { - Ensure = "Present" - Name = "AD-Domain-Services" - } - - xWaitForADDomain DscForestWait - { - DomainName = $Node.ParentDomainName - DomainUserCredential = $domainCred - RetryCount = $Node.RetryCount - RetryIntervalSec = $Node.RetryIntervalSec - DependsOn = "[WindowsFeature]ADDSInstall" - } - - xADDomain ChildDS - { - DomainName = $Node.DomainName - ParentDomainName = $Node.ParentDomainName - DomainAdministratorCredential = $domainCred - SafemodeAdministratorPassword = $safemodeAdministratorCred - DependsOn = "[xWaitForADDomain]DscForestWait" - } - } -} - -$ConfigData = @{ - - AllNodes = @( - @{ - Nodename = "dsc-testNode1" - Role = "Parent DC" - DomainName = "dsc-test.contoso.com" - CertificateFile = "C:\publicKeys\targetNode.cer" - Thumbprint = "AC23EA3A9E291A75757A556D0B71CBBF8C4F6FD8" - RetryCount = 50 - RetryIntervalSec = 30 - }, - - @{ - Nodename = "dsc-testNode2" - Role = "Child DC" - DomainName = "dsc-child" - ParentDomainName = "dsc-test.contoso.com" - CertificateFile = "C:\publicKeys\targetNode.cer" - Thumbprint = "AC23EA3A9E291A75757A556D0B71CBBF8C4F6FD8" - RetryCount = 50 - RetryIntervalSec = 30 - } - ) -} - -AssertParentChildDomains -configurationData $ConfigData ` --safemodeAdministratorCred (Get-Credential -Message "New Domain Safe Mode Admin Credentials") ` --domainCred (Get-Credential -Message "New Domain Admin Credentials") ` --DNSDelegationCred (Get-Credential -Message "Credentials to Setup DNS Delegation") ` --NewADUserCred (Get-Credential -Message "New AD User Credentials") - - -Start-DscConfiguration -Wait -Force -Verbose -ComputerName "dsc-testNode1" -Path $PSScriptRoot\AssertParentChildDomains ` --Credential (Get-Credential -Message "Local Admin Credentials on Remote Machine") -Start-DscConfiguration -Wait -Force -Verbose -ComputerName "dsc-testNode2" -Path $PSScriptRoot\AssertParentChildDomains ` --Credential (Get-Credential -Message "Local Admin Credentials on Remote Machine") -``` - +# Configuration to Setup Parent Child Domains + +Configuration AssertParentChildDomains +{ + param + ( + [Parameter(Mandatory)] + [pscredential]$safemodeAdministratorCred, + + [Parameter(Mandatory)] + [pscredential]$domainCred, + + [Parameter(Mandatory)] + [pscredential]$DNSDelegationCred, + + [Parameter(Mandatory)] + [pscredential]$NewADUserCred + ) + + Import-DscResource -ModuleName xActiveDirectory + + Node $AllNodes.Where{$_.Role -eq "Parent DC"}.Nodename + { + WindowsFeature ADDSInstall + { + Ensure = "Present" + Name = "AD-Domain-Services" + } + + xADDomain FirstDS + { + DomainName = $Node.DomainName + DomainAdministratorCredential = $domainCred + SafemodeAdministratorPassword = $safemodeAdministratorCred + DnsDelegationCredential = $DNSDelegationCred + DependsOn = "[WindowsFeature]ADDSInstall" + } + + xWaitForADDomain DscForestWait + { + DomainName = $Node.DomainName + DomainUserCredential = $domainCred + RetryCount = $Node.RetryCount + RetryIntervalSec = $Node.RetryIntervalSec + DependsOn = "[xADDomain]FirstDS" + } + + xADUser FirstUser + { + DomainName = $Node.DomainName + DomainAdministratorCredential = $domaincred + UserName = "dummy" + Password = $NewADUserCred + Ensure = "Present" + DependsOn = "[xWaitForADDomain]DscForestWait" + } + + } + + Node $AllNodes.Where{$_.Role -eq "Child DC"}.Nodename + { + WindowsFeature ADDSInstall + { + Ensure = "Present" + Name = "AD-Domain-Services" + } + + xWaitForADDomain DscForestWait + { + DomainName = $Node.ParentDomainName + DomainUserCredential = $domainCred + RetryCount = $Node.RetryCount + RetryIntervalSec = $Node.RetryIntervalSec + DependsOn = "[WindowsFeature]ADDSInstall" + } + + xADDomain ChildDS + { + DomainName = $Node.DomainName + ParentDomainName = $Node.ParentDomainName + DomainAdministratorCredential = $domainCred + SafemodeAdministratorPassword = $safemodeAdministratorCred + DependsOn = "[xWaitForADDomain]DscForestWait" + } + } +} + +$ConfigData = @{ + + AllNodes = @( + @{ + Nodename = "dsc-testNode1" + Role = "Parent DC" + DomainName = "dsc-test.contoso.com" + CertificateFile = "C:\publicKeys\targetNode.cer" + Thumbprint = "AC23EA3A9E291A75757A556D0B71CBBF8C4F6FD8" + RetryCount = 50 + RetryIntervalSec = 30 + }, + + @{ + Nodename = "dsc-testNode2" + Role = "Child DC" + DomainName = "dsc-child" + ParentDomainName = "dsc-test.contoso.com" + CertificateFile = "C:\publicKeys\targetNode.cer" + Thumbprint = "AC23EA3A9E291A75757A556D0B71CBBF8C4F6FD8" + RetryCount = 50 + RetryIntervalSec = 30 + } + ) +} + +AssertParentChildDomains -configurationData $ConfigData ` +-safemodeAdministratorCred (Get-Credential -Message "New Domain Safe Mode Admin Credentials") ` +-domainCred (Get-Credential -Message "New Domain Admin Credentials") ` +-DNSDelegationCred (Get-Credential -Message "Credentials to Setup DNS Delegation") ` +-NewADUserCred (Get-Credential -Message "New AD User Credentials") + + +Start-DscConfiguration -Wait -Force -Verbose -ComputerName "dsc-testNode1" -Path $PSScriptRoot\AssertParentChildDomains ` +-Credential (Get-Credential -Message "Local Admin Credentials on Remote Machine") +Start-DscConfiguration -Wait -Force -Verbose -ComputerName "dsc-testNode2" -Path $PSScriptRoot\AssertParentChildDomains ` +-Credential (Get-Credential -Message "Local Admin Credentials on Remote Machine") +``` + ### Create a cross-domain trust In this example, we setup one-way trust between two domains. @@ -688,7 +693,7 @@ Configuration Sample_xADDomainTrust_OneWayTrust [String]$SourceDomain, [Parameter(Mandatory)] [String]$TargetDomain, - + [Parameter(Mandatory)] [PSCredential]$TargetDomainAdminCred, [Parameter(Mandatory)] @@ -723,130 +728,130 @@ Sample_xADDomainTrust_OneWayTrust -configurationdata $config ` -TargetDomain corporate.contoso.com ` -TargetDomainAdminCred (get-credential) ` -TrustDirection 'Inbound' -# Configuration to Setup Parent Child Domains - -configuration AssertParentChildDomains -{ - param - ( - [Parameter(Mandatory)] - [pscredential]$safemodeAdministratorCred, - - [Parameter(Mandatory)] - [pscredential]$domainCred, - - [Parameter(Mandatory)] - [pscredential]$DNSDelegationCred, - - [Parameter(Mandatory)] - [pscredential]$NewADUserCred - ) - - Import-DscResource -ModuleName xActiveDirectory - - Node $AllNodes.Where{$_.Role -eq "Parent DC"}.Nodename - { - WindowsFeature ADDSInstall - { - Ensure = "Present" - Name = "AD-Domain-Services" - } - - xADDomain FirstDS - { - DomainName = $Node.DomainName - DomainAdministratorCredential = $domainCred - SafemodeAdministratorPassword = $safemodeAdministratorCred - DnsDelegationCredential = $DNSDelegationCred - DependsOn = "[WindowsFeature]ADDSInstall" - } - - xWaitForADDomain DscForestWait - { - DomainName = $Node.DomainName - DomainUserCredential = $domainCred - RetryCount = $Node.RetryCount - RetryIntervalSec = $Node.RetryIntervalSec - DependsOn = "[xADDomain]FirstDS" - } - - xADUser FirstUser - { - DomainName = $Node.DomainName - DomainAdministratorCredential = $domaincred - UserName = "dummy" - Password = $NewADUserCred - Ensure = "Present" - DependsOn = "[xWaitForADDomain]DscForestWait" - } - - } - - Node $AllNodes.Where{$_.Role -eq "Child DC"}.Nodename - { - WindowsFeature ADDSInstall - { - Ensure = "Present" - Name = "AD-Domain-Services" - } - - xWaitForADDomain DscForestWait - { - DomainName = $Node.ParentDomainName - DomainUserCredential = $domainCred - RetryCount = $Node.RetryCount - RetryIntervalSec = $Node.RetryIntervalSec - DependsOn = "[WindowsFeature]ADDSInstall" - } - - xADDomain ChildDS - { - DomainName = $Node.DomainName - ParentDomainName = $Node.ParentDomainName - DomainAdministratorCredential = $domainCred - SafemodeAdministratorPassword = $safemodeAdministratorCred - DependsOn = "[xWaitForADDomain]DscForestWait" - } - } -} - -$ConfigData = @{ - - AllNodes = @( - @{ - Nodename = "dsc-testNode1" - Role = "Parent DC" - DomainName = "dsc-test.contoso.com" - CertificateFile = "C:\publicKeys\targetNode.cer" - Thumbprint = "AC23EA3A9E291A75757A556D0B71CBBF8C4F6FD8" - RetryCount = 50 - RetryIntervalSec = 30 - }, - - @{ - Nodename = "dsc-testNode2" - Role = "Child DC" - DomainName = "dsc-child" - ParentDomainName = "dsc-test.contoso.com" - CertificateFile = "C:\publicKeys\targetNode.cer" - Thumbprint = "AC23EA3A9E291A75757A556D0B71CBBF8C4F6FD8" - RetryCount = 50 - RetryIntervalSec = 30 - } - ) -} - -AssertParentChildDomains -configurationData $ConfigData ` --safemodeAdministratorCred (Get-Credential -Message "New Domain Safe Mode Admin Credentials") ` --domainCred (Get-Credential -Message "New Domain Admin Credentials") ` --DNSDelegationCred (Get-Credential -Message "Credentials to Setup DNS Delegation") ` --NewADUserCred (Get-Credential -Message "New AD User Credentials") - - -Start-DscConfiguration -Wait -Force -Verbose -ComputerName "dsc-testNode1" -Path $PSScriptRoot\AssertParentChildDomains ` --Credential (Get-Credential -Message "Local Admin Credentials on Remote Machine") -Start-DscConfiguration -Wait -Force -Verbose -ComputerName "dsc-testNode2" -Path $PSScriptRoot\AssertParentChildDomains ` --Credential (Get-Credential -Message "Local Admin Credentials on Remote Machine") +# Configuration to Setup Parent Child Domains + +configuration AssertParentChildDomains +{ + param + ( + [Parameter(Mandatory)] + [pscredential]$safemodeAdministratorCred, + + [Parameter(Mandatory)] + [pscredential]$domainCred, + + [Parameter(Mandatory)] + [pscredential]$DNSDelegationCred, + + [Parameter(Mandatory)] + [pscredential]$NewADUserCred + ) + + Import-DscResource -ModuleName xActiveDirectory + + Node $AllNodes.Where{$_.Role -eq "Parent DC"}.Nodename + { + WindowsFeature ADDSInstall + { + Ensure = "Present" + Name = "AD-Domain-Services" + } + + xADDomain FirstDS + { + DomainName = $Node.DomainName + DomainAdministratorCredential = $domainCred + SafemodeAdministratorPassword = $safemodeAdministratorCred + DnsDelegationCredential = $DNSDelegationCred + DependsOn = "[WindowsFeature]ADDSInstall" + } + + xWaitForADDomain DscForestWait + { + DomainName = $Node.DomainName + DomainUserCredential = $domainCred + RetryCount = $Node.RetryCount + RetryIntervalSec = $Node.RetryIntervalSec + DependsOn = "[xADDomain]FirstDS" + } + + xADUser FirstUser + { + DomainName = $Node.DomainName + DomainAdministratorCredential = $domaincred + UserName = "dummy" + Password = $NewADUserCred + Ensure = "Present" + DependsOn = "[xWaitForADDomain]DscForestWait" + } + + } + + Node $AllNodes.Where{$_.Role -eq "Child DC"}.Nodename + { + WindowsFeature ADDSInstall + { + Ensure = "Present" + Name = "AD-Domain-Services" + } + + xWaitForADDomain DscForestWait + { + DomainName = $Node.ParentDomainName + DomainUserCredential = $domainCred + RetryCount = $Node.RetryCount + RetryIntervalSec = $Node.RetryIntervalSec + DependsOn = "[WindowsFeature]ADDSInstall" + } + + xADDomain ChildDS + { + DomainName = $Node.DomainName + ParentDomainName = $Node.ParentDomainName + DomainAdministratorCredential = $domainCred + SafemodeAdministratorPassword = $safemodeAdministratorCred + DependsOn = "[xWaitForADDomain]DscForestWait" + } + } +} + +$ConfigData = @{ + + AllNodes = @( + @{ + Nodename = "dsc-testNode1" + Role = "Parent DC" + DomainName = "dsc-test.contoso.com" + CertificateFile = "C:\publicKeys\targetNode.cer" + Thumbprint = "AC23EA3A9E291A75757A556D0B71CBBF8C4F6FD8" + RetryCount = 50 + RetryIntervalSec = 30 + }, + + @{ + Nodename = "dsc-testNode2" + Role = "Child DC" + DomainName = "dsc-child" + ParentDomainName = "dsc-test.contoso.com" + CertificateFile = "C:\publicKeys\targetNode.cer" + Thumbprint = "AC23EA3A9E291A75757A556D0B71CBBF8C4F6FD8" + RetryCount = 50 + RetryIntervalSec = 30 + } + ) +} + +AssertParentChildDomains -configurationData $ConfigData ` +-safemodeAdministratorCred (Get-Credential -Message "New Domain Safe Mode Admin Credentials") ` +-domainCred (Get-Credential -Message "New Domain Admin Credentials") ` +-DNSDelegationCred (Get-Credential -Message "Credentials to Setup DNS Delegation") ` +-NewADUserCred (Get-Credential -Message "New AD User Credentials") + + +Start-DscConfiguration -Wait -Force -Verbose -ComputerName "dsc-testNode1" -Path $PSScriptRoot\AssertParentChildDomains ` +-Credential (Get-Credential -Message "Local Admin Credentials on Remote Machine") +Start-DscConfiguration -Wait -Force -Verbose -ComputerName "dsc-testNode2" -Path $PSScriptRoot\AssertParentChildDomains ` +-Credential (Get-Credential -Message "Local Admin Credentials on Remote Machine") ``` ### Enable the Active Directory Recycle Bin @@ -863,7 +868,7 @@ Param( [parameter(Mandatory = $true)] [System.Management.Automation.PSCredential] - $EACredential + $EACredential ) Import-DscResource -Module xActiveDirectory @@ -902,15 +907,15 @@ Param( [parameter(Mandatory = $true)] [System.String] $GroupName, - - [ValidateSet('DomainLocal','Global','Universal')] + + [ValidateSet('DomainLocal','Global','Universal')] [System.String] $Scope = 'Global', - - [ValidateSet('Security','Distribution')] + + [ValidateSet('Security','Distribution')] [System.String] $Category = 'Security', - + [ValidateNotNullOrEmpty()] [System.String] $Description @@ -947,14 +952,14 @@ Param( [parameter(Mandatory = $true)] [System.String] $Name, - - [parameter(Mandatory = $true)] + + [parameter(Mandatory = $true)] [System.String] $Path, - + [System.Boolean] $ProtectedFromAccidentalDeletion = $true, - + [ValidateNotNull()] [System.String] $Description = '' @@ -993,11 +998,11 @@ configuration Example_xADDomainDefaultPasswordPolicy [parameter(Mandatory = $true)] [System.String] $DomainName, - - [parameter(Mandatory = $true)] + + [parameter(Mandatory = $true)] [System.Boolean] $ComplexityEnabled, - + [parameter(Mandatory = $true)] [System.Int32] $MinPasswordLength, @@ -1030,22 +1035,22 @@ In this example, we create a 'NANO-001' computer account in the 'Server' OU of t configuration Example_xADComputerAccount { Param - ( + ( [parameter(Mandatory = $true)] [System.String] $DomainController, - + [parameter(Mandatory = $true)] [System.Management.Automation.PSCredential] $DomainCredential, - + [parameter(Mandatory = $true)] [System.String] $ComputerName, - - [parameter(Mandatory = $true)] + + [parameter(Mandatory = $true)] [System.String] - $Path + $Path ) Import-DscResource -Module xActiveDirectory @@ -1071,3 +1076,60 @@ Example_xADComputerAccount -DomainController 'DC01' ` Start-DscConfiguration -Path .\Example_xADComputerAccount -Wait -Verbose ``` + +### Create an Active Directory Computer Account and an ODJ Request File + +In this example, we create a 'NANO-200' computer account in the 'Nano' OU of the 'example.com' Active Directory domain as well as creating an Offline Domain Join Request file. + +```powershell +configuration Example_xADComputerAccountODJ +{ + Param + ( + [parameter(Mandatory = $true)] + [System.String] + $DomainController, + + [parameter(Mandatory = $true)] + [System.Management.Automation.PSCredential] + $DomainCredential, + + [parameter(Mandatory = $true)] + [System.String] + $ComputerName, + + [parameter(Mandatory = $true)] + [System.String] + $Path, + + [parameter(Mandatory = $true)] + [System.String] + $ODJRequestFile + ) + + Import-DscResource -Module xActiveDirectory + + Node $AllNodes.NodeName + { + xADComputer "$ComputerName" + { + DomainController = $DomainController + DomainAdministratorCredential = $DomainCredential + ComputerName = $ComputerName + Path = $Path + ODJRequestFile = $ODJRequestFile + } + } +} + +Example_xADComputerAccountODJ -DomainController 'DC01' ` + -DomainCredential (Get-Credential -Message "Domain Credentials") ` + -ComputerName 'NANO-200' ` + -Path 'ou=Nano,dc=example,dc=com' ` + -ODJRequestFile 'd:\ODJFiles\NANO-200.txt' ` + -ConfigurationData $ConfigurationData + +Start-DscConfiguration -Path .\Example_xADComputerAccount -Wait -Verbose + +``` + diff --git a/Tests/Unit/MSFT_xADComputer.Tests.ps1 b/Tests/Unit/MSFT_xADComputer.Tests.ps1 index 4bd9095f5..037158268 100644 --- a/Tests/Unit/MSFT_xADComputer.Tests.ps1 +++ b/Tests/Unit/MSFT_xADComputer.Tests.ps1 @@ -14,7 +14,7 @@ Import-Module (Join-Path -Path $moduleRoot -ChildPath 'DSCResource.Tests\TestHel $TestEnvironment = Initialize-TestEnvironment ` -DSCModuleName $Global:DSCModuleName ` -DSCResourceName $Global:DSCResourceName ` - -TestType Unit + -TestType Unit #endregion HEADER @@ -32,10 +32,10 @@ try ComputerName = 'TESTCOMPUTER'; Ensure = 'Present'; } - + $testAbsentParams = $testPresentParams.Clone(); $testAbsentParams['Ensure'] = 'Absent'; - + $fakeADComputer = @{ DistinguishedName = "CN=$($testPresentParams.ComputerName),CN=Computers,DC=contoso,DC=com"; Enabled = $true; @@ -55,56 +55,56 @@ try $testDomainController = 'TESTDC'; $testCredential = [System.Management.Automation.PSCredential]::Empty; - + #region Function Get-TargetResource Describe "$($Global:DSCResourceName)\Get-TargetResource" { - + It "Returns a 'System.Collections.Hashtable' object type" { Mock Get-ADComputer { return [PSCustomObject] $fakeADComputer; } - + $adUser = Get-TargetResource @testPresentParams; - + $adUser -is [System.Collections.Hashtable] | Should Be $true; } - + It "Returns 'Ensure' is 'Present' when user account exists" { Mock Get-ADComputer { return [PSCustomObject] $fakeADComputer; } - + $adUser = Get-TargetResource @testPresentParams; - + $adUser.Ensure | Should Be 'Present'; } - + It "Returns 'Ensure' is 'Absent' when user account does not exist" { Mock Get-ADComputer { throw New-Object Microsoft.ActiveDirectory.Management.ADIdentityNotFoundException } - + $adUser = Get-TargetResource @testPresentParams; - + $adUser.Ensure | Should Be 'Absent'; } - + It "Calls 'Get-ADComputer' with 'Server' parameter when 'DomainController' specified" { Mock Get-ADComputer -ParameterFilter { $Server -eq $testDomainController } -MockWith { return [PSCustomObject] $fakeADComputer; } - + Get-TargetResource @testPresentParams -DomainController $testDomainController; - + Assert-MockCalled Get-ADComputer -ParameterFilter { $Server -eq $testDomainController } -Scope It; } - + It "Calls 'Get-ADComputer' with 'Credential' parameter when 'DomainAdministratorCredential' specified" { Mock Get-ADComputer -ParameterFilter { $Credential -eq $testCredential } -MockWith { return [PSCustomObject] $fakeADComputer; } - + Get-TargetResource @testPresentParams -DomainAdministratorCredential $testCredential; - + Assert-MockCalled Get-ADComputer -ParameterFilter { $Credential -eq $testCredential } -Scope It; } - + } #endregion #region Function Test-TargetResource Describe "$($Global:DSCResourceName)\Test-TargetResource" { - + $testStringProperties = @( 'Location', 'DnsHostName', @@ -123,30 +123,30 @@ try It "Passes when computer account does not exist and 'Ensure' is 'Absent'" { Mock Get-TargetResource { return $testAbsentParams } - + Test-TargetResource @testAbsentParams | Should Be $true; } - + It "Passes when computer account exists and 'Ensure' is 'Present'" { Mock Get-TargetResource { return $testPresentParams } - + Test-TargetResource @testPresentParams | Should Be $true; } - + It "Fails when computer account does not exist and 'Ensure' is 'Present'" { Mock Get-TargetResource { return $testAbsentParams } - + Test-TargetResource @testPresentParams | Should Be $false; } - + It "Fails when computer account exists, and 'Ensure' is 'Absent'" { Mock Get-TargetResource { return $testPresentParams } - + Test-TargetResource @testAbsentParams | Should Be $false; } - + foreach ($testParameter in $testStringProperties) { - + It "Passes when computer account '$testParameter' matches AD account property" { $testParameterValue = 'Test Parameter String Value'; $testValidPresentParams = $testPresentParams.Clone(); @@ -156,10 +156,10 @@ try $validADComputer[$testParameter] = $testParameterValue; return $validADComputer; } - + Test-TargetResource @testValidPresentParams | Should Be $true; } - + It "Fails when computer account '$testParameter' does not match incorrect AD account property value" { $testParameterValue = 'Test Parameter String Value'; $testValidPresentParams = $testPresentParams.Clone(); @@ -169,10 +169,10 @@ try $invalidADComputer[$testParameter] = $testParameterValue.Substring(0, ([System.Int32] $testParameterValue.Length/2)); return $invalidADComputer; } - + Test-TargetResource @testValidPresentParams | Should Be $false; } - + It "Fails when computer account '$testParameter' does not match empty AD account property value" { $testParameterValue = 'Test Parameter String Value'; $testValidPresentParams = $testPresentParams.Clone(); @@ -182,10 +182,10 @@ try $invalidADComputer[$testParameter] = ''; return $invalidADComputer; } - + Test-TargetResource @testValidPresentParams | Should Be $false; } - + It "Fails when computer account '$testParameter' does not match null AD account property value" { $testParameterValue = 'Test Parameter String Value'; $testValidPresentParams = $testPresentParams.Clone(); @@ -195,10 +195,10 @@ try $invalidADComputer[$testParameter] = $null; return $invalidADComputer; } - + Test-TargetResource @testValidPresentParams | Should Be $false; } - + It "Passes when empty computer account '$testParameter' matches empty AD account property" { $testValidPresentParams = $testPresentParams.Clone(); $testValidPresentParams[$testParameter] = $testParameterValue; @@ -207,10 +207,10 @@ try $validADComputer[$testParameter] = ''; return $validADComputer; } - + Test-TargetResource @testValidPresentParams | Should Be $true; } - + It "Passes when empty computer account '$testParameter' matches null AD account property" { $testValidPresentParams = $testPresentParams.Clone(); $testValidPresentParams[$testParameter] = $testParameterValue; @@ -219,14 +219,14 @@ try $validADComputer[$testParameter] = $null; return $validADComputer; } - + Test-TargetResource @testValidPresentParams | Should Be $true; } - + } #end foreach test string property - + foreach ($testParameter in $testArrayProperties) { - + It "Passes when computer account '$testParameter' matches empty AD account property" { $testParameterValue = @(); $testValidPresentParams = $testPresentParams.Clone(); @@ -236,10 +236,10 @@ try $validADComputer[$testParameter] = $testParameterValue; return $validADComputer; } - + Test-TargetResource @testValidPresentParams | Should Be $true; } - + It "Passes when computer account '$testParameter' matches single AD account property" { $testParameterValue = @('Entry1'); $testValidPresentParams = $testPresentParams.Clone(); @@ -249,10 +249,10 @@ try $validADComputer[$testParameter] = $testParameterValue; return $validADComputer; } - + Test-TargetResource @testValidPresentParams | Should Be $true; } - + It "Passes when computer account '$testParameter' matches multiple AD account property" { $testParameterValue = @('Entry1','Entry2'); $testValidPresentParams = $testPresentParams.Clone(); @@ -262,10 +262,10 @@ try $validADComputer[$testParameter] = $testParameterValue; return $validADComputer; } - + Test-TargetResource @testValidPresentParams | Should Be $true; } - + It "Fails when computer account '$testParameter' does not match AD account property count" { $testParameterValue = @('Entry1','Entry2'); $testValidPresentParams = $testPresentParams.Clone(); @@ -275,10 +275,10 @@ try $validADComputer[$testParameter] = @('Entry1'); return $validADComputer; } - + Test-TargetResource @testValidPresentParams | Should Be $false; } - + It "Fails when computer account '$testParameter' does not match AD account property name" { $testParameterValue = @('Entry1'); $testValidPresentParams = $testPresentParams.Clone(); @@ -288,10 +288,10 @@ try $validADComputer[$testParameter] = @('Entry2'); return $validADComputer; } - + Test-TargetResource @testValidPresentParams | Should Be $false; } - + It "Fails when computer account '$testParameter' does not match empty AD account property" { $testParameterValue = @('Entry1'); $testValidPresentParams = $testPresentParams.Clone(); @@ -301,10 +301,10 @@ try $validADComputer[$testParameter] = @(); return $validADComputer; } - + Test-TargetResource @testValidPresentParams | Should Be $false; } - + It "Fails when empty computer account '$testParameter' does not match AD account property" { $testParameterValue = @(); $testValidPresentParams = $testPresentParams.Clone(); @@ -314,14 +314,14 @@ try $validADComputer[$testParameter] = @('ExtraEntry1'); return $validADComputer; } - + Test-TargetResource @testValidPresentParams | Should Be $false; } - + } #end foreach test string property - + foreach ($testParameter in $testBooleanProperties) { - + It "Passes when computer account '$testParameter' matches AD account property" { $testParameterValue = $true; $testValidPresentParams = $testPresentParams.Clone(); @@ -331,10 +331,10 @@ try $validADComputer[$testParameter] = $testParameterValue; return $validADComputer; } - + Test-TargetResource @testValidPresentParams | Should Be $true; } - + It "Fails when computer account '$testParameter' does not match AD account property value" { $testParameterValue = $true; $testValidPresentParams = $testPresentParams.Clone(); @@ -344,18 +344,18 @@ try $invalidADComputer[$testParameter] = -not $testParameterValue; return $invalidADComputer; } - + Test-TargetResource @testValidPresentParams | Should Be $false; } - + } #end foreach test boolean property - + } #endregion #region Function Set-TargetResource Describe "$($Global:DSCResourceName)\Set-TargetResource" { - + $testStringProperties = @( 'Location', 'DnsHostName', @@ -364,45 +364,45 @@ try 'Description' # Manager is translated to ManagedBy ); - + $testArrayProperties = @( 'ServicePrincipalNames' ); $testBooleanProperties = @( 'Enabled' ); - + It "Calls 'New-ADComputer' when 'Ensure' is 'Present' and the account does not exist" { $newComputerName = 'NEWCOMPUTER' $newAbsentParams = $testAbsentParams.Clone(); $newAbsentParams['ComputerName'] = $newComputerName; $newPresentParams = $testPresentParams.Clone(); - $newPresentParams['ComputerName'] = $newComputerName; + $newPresentParams['ComputerName'] = $newComputerName; Mock New-ADComputer -ParameterFilter { $Name -eq $newComputerName } -MockWith { } Mock Set-ADComputer { } Mock Get-TargetResource -ParameterFilter { $ComputerName -eq $newComputerName } -MockWith { return $newAbsentParams; } - + Set-TargetResource @newPresentParams; - + Assert-MockCalled New-ADComputer -ParameterFilter { $Name -eq $newComputerName } -Scope It; } - + It "Calls 'New-ADComputer' with 'Path' when specified" { $newComputerName = 'NEWCOMPUTER' $newAbsentParams = $testAbsentParams.Clone(); $newAbsentParams['ComputerName'] = $newComputerName; $newPresentParams = $testPresentParams.Clone(); $newPresentParams['ComputerName'] = $newComputerName; - $targetPath = 'OU=Test,DC=contoso,DC=com'; + $targetPath = 'OU=Test,DC=contoso,DC=com'; Mock New-ADComputer -ParameterFilter { $Path -eq $targetPath } -MockWith { } Mock Set-ADComputer { } Mock Get-TargetResource -ParameterFilter { $ComputerName -eq $newComputerName } -MockWith { return $newAbsentParams; } - + Set-TargetResource @newPresentParams -Path $targetPath; - + Assert-MockCalled New-ADComputer -ParameterFilter { $Path -eq $targetPath } -Scope It; } - + It "Calls 'Move-ADObject' when 'Ensure' is 'Present', the computer account exists but Path is incorrect" { $testTargetPath = 'OU=NewPath,DC=contoso,DC=com'; Mock Set-ADComputer { } @@ -412,92 +412,92 @@ try return $duffADComputer; } Mock Move-ADObject -ParameterFilter { $TargetPath -eq $testTargetPath } -MockWith { } - + Set-TargetResource @testPresentParams -Path $testTargetPath; - + Assert-MockCalled Move-ADObject -ParameterFilter { $TargetPath -eq $testTargetPath } -Scope It; } - + foreach ($testParameter in $testStringProperties) { - + It "Calls 'Set-ADComputer' with 'Remove' when '$testParameter' is `$null" { Mock Get-ADComputer { return $fakeADComputer; } Mock Set-ADComputer -ParameterFilter { $Remove.ContainsKey($testParameter) } { } - + $setTargetResourceParams = $testPresentParams.Clone(); $setTargetResourceParams[$testParameter] = ''; Set-TargetResource @setTargetResourceParams; - + Assert-MockCalled Set-ADComputer -ParameterFilter { $Remove.ContainsKey($testParameter) } -Scope It -Exactly 1; } - + It "Calls 'Set-ADComputer' with 'Replace' when existing '$testParameter' is not `$null" { Mock Get-ADComputer { return $fakeADComputer; } Mock Set-ADComputer -ParameterFilter { $Replace.ContainsKey($testParameter) } { } - + $setTargetResourceParams = $testPresentParams.Clone(); $setTargetResourceParams[$testParameter] = 'NewStringValue'; Set-TargetResource @setTargetResourceParams; - + Assert-MockCalled Set-ADComputer -ParameterFilter { $Replace.ContainsKey($testParameter) } -Scope It -Exactly 1; - } - + } + } #end foreach string parameter - + It "Calls 'Set-ADComputer' with 'Remove' when 'Manager' is `$null" { ## Manager translates to AD attribute 'managedBy' Mock Get-ADComputer { return $fakeADComputer; } Mock Set-ADComputer -ParameterFilter { $Remove.ContainsKey('ManagedBy') } { } - + $setTargetResourceParams = $testPresentParams.Clone(); $setTargetResourceParams['Manager'] = ''; Set-TargetResource @setTargetResourceParams; - + Assert-MockCalled Set-ADComputer -ParameterFilter { $Remove.ContainsKey('ManagedBy') } -Scope It -Exactly 1; } - + It "Calls 'Set-ADComputer' with 'Replace' when existing 'Manager' is not `$null" { ## Manager translates to AD attribute 'managedBy' Mock Get-ADComputer { return $fakeADComputer; } Mock Set-ADComputer -ParameterFilter { $Replace.ContainsKey('ManagedBy') } { } - + $setTargetResourceParams = $testPresentParams.Clone(); $setTargetResourceParams['Manager'] = 'NewValue'; Set-TargetResource @setTargetResourceParams; - + Assert-MockCalled Set-ADComputer -ParameterFilter { $Replace.ContainsKey('ManagedBy') } -Scope It -Exactly 1; } - + It "Calls 'Set-ADComputer' with 'Enabled' = 'True' by default" { Mock Get-ADComputer { return $fakeADComputer; } Mock Set-ADComputer -ParameterFilter { $Enabled -eq $true } { } - + $setTargetResourceParams = $testPresentParams.Clone(); $setTargetResourceParams[$testParameter] = -not $fakeADComputer.$testParameter; Set-TargetResource @setTargetResourceParams; - + Assert-MockCalled Set-ADComputer -ParameterFilter { $Enabled -eq $true } -Scope It -Exactly 1; } - + It "Calls 'Set-ADComputer' with 'ServicePrincipalNames' when specified" { $testSPNs = @('spn/a','spn/b'); Mock Get-ADComputer { return $fakeADComputer; } Mock Set-ADComputer -ParameterFilter { $Replace.ContainsKey('ServicePrincipalName') } { } - + Set-TargetResource @testPresentParams -ServicePrincipalNames $testSPNs; - + Assert-MockCalled Set-ADComputer -ParameterFilter { $Replace.ContainsKey('ServicePrincipalName') } -Scope It -Exactly 1; } It "Calls 'Remove-ADComputer' when 'Ensure' is 'Absent' and computer account exists" { Mock Get-ADComputer { return $fakeADComputer; } Mock Remove-ADComputer -ParameterFilter { $Identity.ToString() -eq $testAbsentParams.ComputerName } -MockWith { } - + Set-TargetResource @testAbsentParams; - + Assert-MockCalled Remove-ADComputer -ParameterFilter { $Identity.ToString() -eq $testAbsentParams.ComputerName } -Scope It; } - + } #endregion } From 5bc32931fd692d9223d27320cce79bf3a8a333ef Mon Sep 17 00:00:00 2001 From: Daniel Scott-Raynsford Date: Fri, 17 Jun 2016 18:31:43 +1200 Subject: [PATCH 2/9] Added unit tests --- Tests/Unit/MSFT_xADComputer.Tests.ps1 | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/Tests/Unit/MSFT_xADComputer.Tests.ps1 b/Tests/Unit/MSFT_xADComputer.Tests.ps1 index 037158268..a7a01ca36 100644 --- a/Tests/Unit/MSFT_xADComputer.Tests.ps1 +++ b/Tests/Unit/MSFT_xADComputer.Tests.ps1 @@ -387,6 +387,24 @@ try Assert-MockCalled New-ADComputer -ParameterFilter { $Name -eq $newComputerName } -Scope It; } + It "Calls 'New-ADComputer' when 'Ensure' is 'Present' and the account does not exist, ODJRequestFile is set, DJOIN OK" { + $newComputerName = 'NEWCOMPUTER' + $newAbsentParams = $testAbsentParams.Clone(); + $newAbsentParams['ComputerName'] = $newComputerName; + $newPresentParams = $testPresentParams.Clone(); + $newPresentParams['ComputerName'] = $newComputerName; + $newPresentParams['ODJRequestFile'] = 'c:\ODJTest.txt'; + Mock New-ADComputer -ParameterFilter { $Name -eq $newComputerName } -MockWith { } + Mock djoin.exe -MockWith { $LASTEXITCODE = 0; 'OK' } + Mock Set-ADComputer { } + Mock Get-TargetResource -ParameterFilter { $ComputerName -eq $newComputerName } -MockWith { return $newAbsentParams; } + + Set-TargetResource @newPresentParams; + + Assert-MockCalled New-ADComputer -ParameterFilter { $Name -eq $newComputerName } -Scope It -Exactly 0; + Assert-MockCalled djoin.exe -Exactly 1; + } + It "Calls 'New-ADComputer' with 'Path' when specified" { $newComputerName = 'NEWCOMPUTER' $newAbsentParams = $testAbsentParams.Clone(); From 25641dac8922b338594be958f87b8c864fa09f0a Mon Sep 17 00:00:00 2001 From: Daniel Scott-Raynsford Date: Fri, 17 Jun 2016 18:48:06 +1200 Subject: [PATCH 3/9] Added more unit tests --- Tests/Unit/MSFT_xADCommon.Tests.ps1 | 254 +++++++++++++++------------- 1 file changed, 133 insertions(+), 121 deletions(-) diff --git a/Tests/Unit/MSFT_xADCommon.Tests.ps1 b/Tests/Unit/MSFT_xADCommon.Tests.ps1 index 278282cb7..9e3a5b404 100644 --- a/Tests/Unit/MSFT_xADCommon.Tests.ps1 +++ b/Tests/Unit/MSFT_xADCommon.Tests.ps1 @@ -17,7 +17,7 @@ Import-Module (Join-Path -Path $moduleRoot -ChildPath 'DSCResource.Tests\TestHel $TestEnvironment = Initialize-TestEnvironment ` -DSCModuleName $Global:DSCModuleName ` -DSCResourceName $Global:DSCResourceName ` - -TestType Unit + -TestType Unit #endregion # Begin Testing @@ -31,71 +31,83 @@ try InModuleScope $Global:DSCResourceName { #region Pester Test Initialization - + #endregion - + #region Function ResolveDomainFQDN Describe "$($Global:DSCResourceName)\Resolve-DomainFQDN" { - + It 'Returns "DomainName" when "ParentDomainName" not supplied' { $testDomainName = 'contoso.com'; $testParentDomainName = $null; - + $result = Resolve-DomainFQDN -DomainName $testDomainName -ParentDomainName $testParentDOmainName; - + $result | Should Be $testDomainName; } - + It 'Returns compound "DomainName.ParentDomainName" when "ParentDomainName" supplied' { $testDomainName = 'subdomain'; $testParentDomainName = 'contoso.com'; - + $result = Resolve-DomainFQDN -DomainName $testDomainName -ParentDomainName $testParentDomainName; - + $result | Should Be "$testDomainName.$testParentDomainName"; } - + } #endregion - + #region Function TestDomainMember Describe "$($Global:DSCResourceName)\Test-DomainMember" { - + It 'Returns "True" when domain member' { Mock Get-CimInstance { return @{ Name = $env:COMPUTERNAME; PartOfDomain = $true; } } - + Test-DomainMember | Should Be $true; } - + It 'Returns "False" when workgroup member' { Mock Get-CimInstance { return @{ Name = $env:COMPUTERNAME; } } - + Test-DomainMember | Should Be $false; } - + + } + #endregion + + #region Function Get-DomainName + Describe "$($Global:DSCResourceName)\Get-DomainName" { + + It 'Returns exepected domain name' { + Mock Get-CimInstance { return @{ Name = $env:COMPUTERNAME; Domain = 'contoso.com'; } } + + Get-DomainName | Should Be 'contoso.com'; + } + } #endregion - + #region Function Assert-Module Describe "$($Global:DSCResourceName)\Assert-Module" { - + It 'Does not throw when module is installed' { $testModuleName = 'TestModule'; Mock Get-Module -ParameterFilter { $Name -eq $testModuleName } { return $true; } - + { Assert-Module -ModuleName $testModuleName } | Should Not Throw; } - + It 'Throws when module is not installed' { $testModuleName = 'TestModule'; Mock Get-Module -ParameterFilter { $Name -eq $testModuleName } { } - + { Assert-Module -ModuleName $testModuleName } | Should Throw; } - + } #endregion - + #region Function Assert-Module Describe "$($Global:DSCResourceName)\Get-ADObjectParentDN" { @@ -108,11 +120,11 @@ try } } - #endregion - + #endregion + #region Function Remove-DuplicateMembers Describe "$($Global:DSCResourceName)\Remove-DuplicateMembers" { - + It 'Removes one duplicate' { $members = Remove-DuplicateMembers -Members 'User1','User2','USER1'; @@ -120,7 +132,7 @@ try $members -contains 'User1' | Should Be $true; $members -contains 'User2' | Should Be $true; } - + It 'Removes two duplicates' { $members = Remove-DuplicateMembers -Members 'User1','User2','USER1','USER2'; @@ -128,7 +140,7 @@ try $members -contains 'User1' | Should Be $true; $members -contains 'User2' | Should Be $true; } - + It 'Removes double duplicates' { $members = Remove-DuplicateMembers -Members 'User1','User2','USER1','user1'; @@ -139,10 +151,10 @@ try } #endregion - + #region Function Test-Members Describe "$($Global:DSCResourceName)\Test-Members" { - + It 'Passes when nothing is passed' { Test-Members -ExistingMembers $null | Should Be $true; } @@ -177,333 +189,333 @@ try It 'Fails when there are more existing members than the members required' { $testExistingMembers = @('USER1', 'USER2'); $testMembers = @('USER1', 'USER3', 'USER2'); - + Test-Members -ExistingMembers $null -Members $testMembers | Should Be $false; } - + It 'Fails when existing members do not match required members' { $testExistingMembers = @('USER1', 'USER2'); $testMembers = @('USER1', 'USER3'); - + Test-Members -ExistingMembers $testExistingMembers -Members $testMembers | Should Be $false; } - + It 'Passes when existing members include required member' { $testExistingMembers = @('USER1', 'USER2'); $testMembersToInclude = @('USER2'); - + Test-Members -ExistingMembers $testExistingMembers -MembersToInclude $testMembersToInclude | Should Be $true; } - + It 'Passes when existing members include required members' { $testExistingMembers = @('USER1', 'USER2'); $testMembersToInclude = @('USER2', 'USER1'); - + Test-Members -ExistingMembers $testExistingMembers -MembersToInclude $testMembersToInclude | Should Be $true; } - + It 'Fails when existing members is missing a required member' { $testExistingMembers = @('USER1'); $testMembersToInclude = @('USER2'); - + Test-Members -ExistingMembers $testExistingMembers -MembersToInclude $testMembersToInclude | Should Be $false; } - + It 'Fails when existing members is missing a required member' { $testExistingMembers = @('USER1', 'USER3'); $testMembersToInclude = @('USER2'); - + Test-Members -ExistingMembers $testExistingMembers -MembersToInclude $testMembersToInclude | Should Be $false; } - + It 'Fails when existing members is missing a required members' { $testExistingMembers = @('USER3'); $testMembersToInclude = @('USER1', 'USER2'); - + Test-Members -ExistingMembers $testExistingMembers -MembersToInclude $testMembersToInclude | Should Be $false; } - + It 'Passes when existing member does not include excluded member' { $testExistingMembers = @('USER1'); $testMembersToExclude = @('USER2'); - + Test-Members -ExistingMembers $testExistingMembers -MembersToExclude $testMembersToInclude | Should Be $true; } - + It 'Passes when existing member does not include excluded members' { $testExistingMembers = @('USER1'); $testMembersToExclude = @('USER2', 'USER3'); - + Test-Members -ExistingMembers $testExistingMembers -MembersToExclude $testMembersToInclude | Should Be $true; } - + It 'Passes when existing members does not include excluded member' { $testExistingMembers = @('USER1', 'USER2'); $testMembersToExclude = @('USER3'); - + Test-Members -ExistingMembers $testExistingMembers -MembersToExclude $testMembersToInclude | Should Be $true; } } #endregion - + #region Function Assert-MemberParameters Describe "$($Global:DSCResourceName)\Assert-MemberParameters" { - + It "Throws if 'Members' is specified but is empty" { { Assert-MemberParameters -Members @() } | Should Throw 'The Members parameter value is null'; } - + It "Throws if 'Members' and 'MembersToInclude' are specified" { { Assert-MemberParameters -Members @('User1') -MembersToInclude @('User1') } | Should Throw 'parameters conflict'; } - + It "Throws if 'Members' and 'MembersToExclude' are specified" { { Assert-MemberParameters -Members @('User1') -MembersToExclude @('User2') } | Should Throw 'parameters conflict'; } - + It "Throws if 'MembersToInclude' and 'MembersToExclude' contain the same member" { { Assert-MemberParameters -MembersToExclude @('user1') -MembersToInclude @('USER1') } | Should Throw 'member must not be included in both'; } - + It "Throws if 'MembersToInclude' and 'MembersToExclude' are empty" { { Assert-MemberParameters -MembersToExclude @() -MembersToInclude @() } | Should Throw 'At least one member must be specified'; } } #endregion - + #region Function ConvertTo-Timespan Describe "$($Global:DSCResourceName)\ConvertTo-Timespan" { - + It "Returns 'System.TimeSpan' object type" { $testIntTimeSpan = 60; - + $result = ConvertTo-TimeSpan -TimeSpan $testIntTimeSpan -TimeSpanType Minutes; - + $result -is [System.TimeSpan] | Should Be $true; } - + It "Creates TimeSpan from seconds" { $testIntTimeSpan = 60; - + $result = ConvertTo-TimeSpan -TimeSpan $testIntTimeSpan -TimeSpanType Seconds; - + $result.TotalSeconds | Should Be $testIntTimeSpan; } - + It "Creates TimeSpan from minutes" { $testIntTimeSpan = 60; - + $result = ConvertTo-TimeSpan -TimeSpan $testIntTimeSpan -TimeSpanType Minutes; - + $result.TotalMinutes | Should Be $testIntTimeSpan; } - + It "Creates TimeSpan from hours" { $testIntTimeSpan = 60; - + $result = ConvertTo-TimeSpan -TimeSpan $testIntTimeSpan -TimeSpanType Hours; - + $result.TotalHours | Should Be $testIntTimeSpan; } - + It "Creates TimeSpan from days" { $testIntTimeSpan = 60; - + $result = ConvertTo-TimeSpan -TimeSpan $testIntTimeSpan -TimeSpanType Days; - + $result.TotalDays | Should Be $testIntTimeSpan; } - + } #endregion - + #region Function ConvertTo-Timespan Describe "$($Global:DSCResourceName)\ConvertFrom-Timespan" { - + It "Returns 'System.UInt32' object type" { $testIntTimeSpan = 60; $testTimeSpan = New-TimeSpan -Seconds $testIntTimeSpan; - + $result = ConvertFrom-TimeSpan -TimeSpan $testTimeSpan -TimeSpanType Seconds; $result -is [System.UInt32] | Should Be $true; } - + It "Converts TimeSpan to total seconds" { $testIntTimeSpan = 60; $testTimeSpan = New-TimeSpan -Seconds $testIntTimeSpan; - + $result = ConvertFrom-TimeSpan -TimeSpan $testTimeSpan -TimeSpanType Seconds; - + $result | Should Be $testTimeSpan.TotalSeconds; } - + It "Converts TimeSpan to total minutes" { $testIntTimeSpan = 60; $testTimeSpan = New-TimeSpan -Minutes $testIntTimeSpan; - + $result = ConvertFrom-TimeSpan -TimeSpan $testTimeSpan -TimeSpanType Minutes; - + $result | Should Be $testTimeSpan.TotalMinutes; } - + It "Converts TimeSpan to total hours" { $testIntTimeSpan = 60; $testTimeSpan = New-TimeSpan -Hours $testIntTimeSpan; - + $result = ConvertFrom-TimeSpan -TimeSpan $testTimeSpan -TimeSpanType Hours; - + $result | Should Be $testTimeSpan.TotalHours; } - + It "Converts TimeSpan to total days" { $testIntTimeSpan = 60; $testTimeSpan = New-TimeSpan -Days $testIntTimeSpan; - + $result = ConvertFrom-TimeSpan -TimeSpan $testTimeSpan -TimeSpanType Days; - + $result | Should Be $testTimeSpan.TotalDays; } - + } #endregion - + #region Function Get-ADCommonParameters Describe "$($Global:DSCResourceName)\Get-ADCommonParameters" { - + It "Returns 'System.Collections.Hashtable' object type" { $testIdentity = 'contoso.com'; - + $result = Get-ADCommonParameters -Identity $testIdentity; $result -is [System.Collections.Hashtable] | Should Be $true; } - + It "Returns 'Identity' key by default" { $testIdentity = 'contoso.com'; - + $result = Get-ADCommonParameters -Identity $testIdentity; $result['Identity'] | Should Be $testIdentity; } - + It "Returns 'Name' key when 'UseNameParameter' is specified" { $testIdentity = 'contoso.com'; - + $result = Get-ADCommonParameters -Identity $testIdentity -UseNameParameter; $result['Name'] | Should Be $testIdentity; } - + foreach ($identityParam in @('UserName','GroupName','ComputerName')) { It "Returns 'Identity' key when '$identityParam' alias is specified" { $testIdentity = 'contoso.com'; $getADCommonParameters = @{ $identityParam = $testIdentity; } - + $result = Get-ADCommonParameters @getADCommonParameters; $result['Identity'] | Should Be $testIdentity; } } - + It "Returns 'Identity' key by default when 'Identity' and 'CommonName' are specified" { $testIdentity = 'contoso.com'; $testCommonName = 'Test Common Name'; - + $result = Get-ADCommonParameters -Identity $testIdentity -CommonName $testCommonName; $result['Identity'] | Should Be $testIdentity; } - + It "Returns 'Identity' key with 'CommonName' when 'Identity', 'CommonName' and 'PreferCommonName' are specified" { $testIdentity = 'contoso.com'; $testCommonName = 'Test Common Name'; - + $result = Get-ADCommonParameters -Identity $testIdentity -CommonName $testCommonName -PreferCommonName; $result['Identity'] | Should Be $testCommonName; } - + It "Returns 'Identity' key with 'Identity' when 'Identity' and 'PreferCommonName' are specified" { $testIdentity = 'contoso.com'; - + $result = Get-ADCommonParameters -Identity $testIdentity -PreferCommonName; $result['Identity'] | Should Be $testIdentity; } - + it "Returns 'Name' key when 'UseNameParameter' and 'PreferCommonName' are supplied" { $testIdentity = 'contoso.com'; $testCommonName = 'Test Common Name'; - + $result = Get-ADCommonParameters -Identity $testIdentity -UseNameParameter -CommonName $testCommonName -PreferCommonName; $result['Name'] | Should Be $testCommonName; } - + It "Does not return 'Credential' key by default" { $testIdentity = 'contoso.com'; - + $result = Get-ADCommonParameters -Identity $testIdentity; $result.ContainsKey('Credential') | Should Be $false; } - + It "Returns 'Credential' key when specified" { $testIdentity = 'contoso.com'; $testCredential = [System.Management.Automation.PSCredential]::Empty; - + $result = Get-ADCommonParameters -Identity $testIdentity -Credential $testCredential; $result['Credential'] | Should Be $testCredential; } - + It "Does not return 'Server' key by default" { $testIdentity = 'contoso.com'; - + $result = Get-ADCommonParameters -Identity $testIdentity; $result.ContainsKey('Server') | Should Be $false; } - + It "Returns 'Server' key when specified" { $testIdentity = 'contoso.com'; $testServer = 'testserver.contoso.com'; - + $result = Get-ADCommonParameters -Identity $testIdentity -Server $testServer; $result['Server'] | Should Be $testServer; } - + It "Converts 'DomainAdministratorCredential' parameter to 'Credential' key" { $testIdentity = 'contoso.com'; $testCredential = [System.Management.Automation.PSCredential]::Empty; - + $result = Get-ADCommonParameters -Identity $testIdentity -DomainAdministratorCredential $testCredential; $result['Credential'] | Should Be $testCredential; } - + It "Converts 'DomainController' parameter to 'Server' key" { $testIdentity = 'contoso.com'; $testServer = 'testserver.contoso.com'; - + $result = Get-ADCommonParameters -Identity $testIdentity -DomainController $testServer; $result['Server'] | Should Be $testServer; } - + It 'Accepts remaining arguments' { $testIdentity = 'contoso.com'; - + $result = Get-ADCommonParameters -Identity $testIdentity -UnexpectedParameter 42; - + $result['Identity'] | Should Be $testIdentity; } - + } #endregion From 28332af97cef881240b2eed501e1785b88656140 Mon Sep 17 00:00:00 2001 From: Daniel Scott-Raynsford Date: Fri, 17 Jun 2016 20:05:55 +1200 Subject: [PATCH 4/9] Fix to Get-TargetResource in xADComputer --- DSCResources/MSFT_xADComputer/MSFT_xADComputer.psm1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DSCResources/MSFT_xADComputer/MSFT_xADComputer.psm1 b/DSCResources/MSFT_xADComputer/MSFT_xADComputer.psm1 index 6ddede938..08ae48668 100644 --- a/DSCResources/MSFT_xADComputer/MSFT_xADComputer.psm1 +++ b/DSCResources/MSFT_xADComputer/MSFT_xADComputer.psm1 @@ -128,7 +128,7 @@ function Get-TargetResource SID = $adComputer.SID; ## Read-only property Ensure = $Ensure; DomainController = $DomainController; - ODJRequestFile = ''; # This property can only be set, not read. + ODJRequestFile = $ODJRequestFile; } ## Retrieve each property from the ADPropertyMap and add to the hashtable From b6db46aad1ab0df8ab2b31ccb242a70b2151a221 Mon Sep 17 00:00:00 2001 From: Daniel Scott-Raynsford Date: Fri, 17 Jun 2016 21:03:25 +1200 Subject: [PATCH 5/9] Resolved PSSA violations --- Assert-HADC.ps1 | 6 +- Assert-ParentChildDomains.ps1 | 4 + DSCResources/MSFT_xADUser/MSFT_xADUser.psm1 | 158 ++++++------- README.md | 1 + Tests/Unit/MSFT_xADCommon.Tests.ps1 | 3 + Tests/Unit/MSFT_xADComputer.Tests.ps1 | 3 + Tests/Unit/MSFT_xADDomain.Tests.ps1 | 211 +++++++++--------- ...T_xADDomainDefaultPasswordPolicy.Tests.ps1 | 125 ++++++----- Tests/Unit/MSFT_xADGroup.Tests.ps1 | 189 ++++++++-------- .../Unit/MSFT_xADOrganizationalUnit.Tests.ps1 | 59 ++--- Tests/Unit/MSFT_xADUser.Tests.ps1 | 183 +++++++-------- Tests/Unit/MSFT_xWaitForADDomain.Tests.ps1 | 27 ++- 12 files changed, 501 insertions(+), 468 deletions(-) diff --git a/Assert-HADC.ps1 b/Assert-HADC.ps1 index 8d08555c0..41fe20dac 100644 --- a/Assert-HADC.ps1 +++ b/Assert-HADC.ps1 @@ -1,4 +1,8 @@ -# A configuration to Create High Availability Domain Controller +[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingComputerNameHardcoded', '')] +[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingConvertToSecureStringWithPlainText', '')] +param() + +# A configuration to Create High Availability Domain Controller $secpasswd = ConvertTo-SecureString "Adrumble@6" -AsPlainText -Force $domainCred = New-Object System.Management.Automation.PSCredential ("sva-dscdom\Administrator", $secpasswd) diff --git a/Assert-ParentChildDomains.ps1 b/Assert-ParentChildDomains.ps1 index 3488427c5..65ec972eb 100644 --- a/Assert-ParentChildDomains.ps1 +++ b/Assert-ParentChildDomains.ps1 @@ -1,3 +1,7 @@ +[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingComputerNameHardcoded', '')] +[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingConvertToSecureStringWithPlainText', '')] +param() + $secpasswd = ConvertTo-SecureString "Adrumble@6" -AsPlainText -Force $domainCred = New-Object System.Management.Automation.PSCredential ("sva-dscdom\Administrator", $secpasswd) $safemodeAdministratorCred = New-Object System.Management.Automation.PSCredential ("sva-dscdom\Administrator", $secpasswd) diff --git a/DSCResources/MSFT_xADUser/MSFT_xADUser.psm1 b/DSCResources/MSFT_xADUser/MSFT_xADUser.psm1 index 73e65f63f..0a09678c9 100644 --- a/DSCResources/MSFT_xADUser/MSFT_xADUser.psm1 +++ b/DSCResources/MSFT_xADUser/MSFT_xADUser.psm1 @@ -1,4 +1,7 @@ -# Localized messages +[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingUserNameAndPassWordParams', '')] +param() + +# Localized messages data LocalizedData { # culture="en-US" @@ -6,14 +9,14 @@ data LocalizedData RoleNotFoundError = Please ensure that the PowerShell module for role '{0}' is installed. RetrievingADUserError = Error looking up Active Directory user '{0}' ({0}@{1}). PasswordParameterConflictError = Parameter '{0}' cannot be set to '{1}' when the '{2}' parameter is specified. - + RetrievingADUser = Retrieving Active Directory user '{0}' ({0}@{1}) ... CreatingADDomainConnection = Creating connection to Active Directory domain '{0}' ... CheckingADUserPassword = Checking Active Directory user '{0}' password ... ADUserIsPresent = Active Directory user '{0}' ({0}@{1}) is present. ADUserNotPresent = Active Directory user '{0}' ({0}@{1}) was NOT present. ADUserNotDesiredPropertyState = User '{0}' property is NOT in the desired state. Expected '{1}', actual '{2}'. - + AddingADUser = Adding Active Directory user '{0}'. RemovingADUser = Removing Active Directory user '{0}'. UpdatingADUser = Updating Active Directory user '{0}'. @@ -72,17 +75,16 @@ function Get-TargetResource { [CmdletBinding()] [OutputType([System.Collections.Hashtable])] - [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingUserNameAndPassWordParams', '')] param ( ## Only used if password is managed. [Parameter(Mandatory)] [System.String] $DomainName, - + # SamAccountName [Parameter(Mandatory)] [System.String] $UserName, - + [ValidateNotNull()] [System.Management.Automation.PSCredential] [System.Management.Automation.CredentialAttribute()] @@ -90,29 +92,29 @@ function Get-TargetResource [ValidateSet('Present', 'Absent')] [System.String] $Ensure = 'Present', - + # Common name (CN) [ValidateNotNull()] [System.String] $CommonName = $UserName, [ValidateNotNull()] [System.String] $UserPrincipalName, - + [ValidateNotNull()] [System.String] $DisplayName, - + [ValidateNotNull()] [System.String] $Path, - + [ValidateNotNull()] [System.String] $GivenName, - + [ValidateNotNull()] [System.String] $Initials, - + [ValidateNotNull()] [System.String] $Surname, - + [ValidateNotNull()] [System.String] $Description, @@ -151,7 +153,7 @@ function Get-TargetResource [ValidateNotNull()] [System.String] $EmailAddress, - + [ValidateNotNull()] [System.String] $EmployeeID, @@ -166,19 +168,19 @@ function Get-TargetResource [ValidateNotNull()] [System.String] $HomePage, - + [ValidateNotNull()] [System.String] $ProfilePath, - + [ValidateNotNull()] [System.String] $LogonScript, - + [ValidateNotNull()] [System.String] $Notes, - + [ValidateNotNull()] [System.String] $OfficePhone, - + [ValidateNotNull()] [System.String] $MobilePhone, @@ -197,32 +199,32 @@ function Get-TargetResource ## User's manager specified as a Distinguished Name (DN) [ValidateNotNull()] [System.String] $Manager, - + [ValidateNotNull()] [System.Boolean] $Enabled = $true, [ValidateNotNull()] [System.Boolean] $CannotChangePassword, - + [ValidateNotNull()] [System.Boolean] $PasswordNeverExpires, - + [ValidateNotNull()] [System.String] $DomainController, - + ## Ideally this should just be called 'Credential' but is here for backwards compatibility [ValidateNotNull()] [System.Management.Automation.PSCredential] [System.Management.Automation.CredentialAttribute()] $DomainAdministratorCredential ) - + Assert-Module -ModuleName 'ActiveDirectory'; try { $adCommonParameters = Get-ADCommonParameters @PSBoundParameters; - + $adProperties = @(); ## Create an array of the AD propertie names to retrieve from the property map foreach ($property in $adPropertyMap) @@ -231,7 +233,7 @@ function Get-TargetResource { $adProperties += $property.ADProperty; } - else + else { $adProperties += $property.Parameter; } @@ -291,17 +293,16 @@ function Test-TargetResource { [CmdletBinding()] [OutputType([System.Boolean])] - [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingUserNameAndPassWordParams', '')] param ( ## Only used if password is managed. [Parameter(Mandatory)] [System.String] $DomainName, - + # SamAccountName [Parameter(Mandatory)] [System.String] $UserName, - + [ValidateNotNull()] [System.Management.Automation.PSCredential] [System.Management.Automation.CredentialAttribute()] @@ -309,29 +310,29 @@ function Test-TargetResource [ValidateSet('Present', 'Absent')] [System.String] $Ensure = 'Present', - + # Common name (CN) [ValidateNotNull()] [System.String] $CommonName = $UserName, - + [ValidateNotNull()] [System.String] $UserPrincipalName, - + [ValidateNotNull()] [System.String] $DisplayName, - + [ValidateNotNull()] [System.String] $Path, - + [ValidateNotNull()] [System.String] $GivenName, - + [ValidateNotNull()] [System.String] $Initials, - + [ValidateNotNull()] [System.String] $Surname, - + [ValidateNotNull()] [System.String] $Description, @@ -370,7 +371,7 @@ function Test-TargetResource [ValidateNotNull()] [System.String] $EmailAddress, - + [ValidateNotNull()] [System.String] $EmployeeID, @@ -385,19 +386,19 @@ function Test-TargetResource [ValidateNotNull()] [System.String] $HomePage, - + [ValidateNotNull()] [System.String] $ProfilePath, - + [ValidateNotNull()] [System.String] $LogonScript, - + [ValidateNotNull()] [System.String] $Notes, - + [ValidateNotNull()] [System.String] $OfficePhone, - + [ValidateNotNull()] [System.String] $MobilePhone, @@ -416,19 +417,19 @@ function Test-TargetResource ## User's manager specified as a Distinguished Name (DN) [ValidateNotNull()] [System.String] $Manager, - + [ValidateNotNull()] [System.Boolean] $Enabled = $true, [ValidateNotNull()] [System.Boolean] $CannotChangePassword, - + [ValidateNotNull()] [System.Boolean] $PasswordNeverExpires, - + [ValidateNotNull()] [System.String] $DomainController, - + [ValidateNotNull()] [System.Management.Automation.PSCredential] [System.Management.Automation.CredentialAttribute()] @@ -452,7 +453,7 @@ function Test-TargetResource ## Add common name, ensure and enabled as they may not be explicitly passed and we want to enumerate them $PSBoundParameters['Ensure'] = $Ensure; $PSBoundParameters['Enabled'] = $Enabled; - + foreach ($parameter in $PSBoundParameters.Keys) { if ($parameter -eq 'Password') @@ -496,17 +497,16 @@ function Test-TargetResource function Set-TargetResource { [CmdletBinding()] - [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingUserNameAndPassWordParams', '')] param ( ## Only used if password is managed. [Parameter(Mandatory)] [System.String] $DomainName, - + # SamAccountName [Parameter(Mandatory)] [System.String] $UserName, - + [ValidateNotNull()] [System.Management.Automation.PSCredential] [System.Management.Automation.CredentialAttribute()] @@ -514,28 +514,28 @@ function Set-TargetResource [ValidateSet('Present', 'Absent')] [System.String] $Ensure = 'Present', - + [ValidateNotNull()] [System.String] $CommonName = $UserName, [ValidateNotNull()] [System.String] $UserPrincipalName, - + [ValidateNotNull()] [System.String] $DisplayName, - + [ValidateNotNull()] [System.String] $Path, - + [ValidateNotNull()] [System.String] $GivenName, - + [ValidateNotNull()] [System.String] $Initials, - + [ValidateNotNull()] [System.String] $Surname, - + [ValidateNotNull()] [System.String] $Description, @@ -574,7 +574,7 @@ function Set-TargetResource [ValidateNotNull()] [System.String] $EmailAddress, - + [ValidateNotNull()] [System.String] $EmployeeID, @@ -589,19 +589,19 @@ function Set-TargetResource [ValidateNotNull()] [System.String] $HomePage, - + [ValidateNotNull()] [System.String] $ProfilePath, - + [ValidateNotNull()] [System.String] $LogonScript, - + [ValidateNotNull()] [System.String] $Notes, - + [ValidateNotNull()] [System.String] $OfficePhone, - + [ValidateNotNull()] [System.String] $MobilePhone, @@ -620,19 +620,19 @@ function Set-TargetResource ## User's manager specified as a Distinguished Name (DN) [ValidateNotNull()] [System.String] $Manager, - + [ValidateNotNull()] [System.Boolean] $Enabled = $true, - + [ValidateNotNull()] [System.Boolean] $CannotChangePassword, - + [ValidateNotNull()] [System.Boolean] $PasswordNeverExpires, - + [ValidateNotNull()] [System.String] $DomainController, - + [ValidateNotNull()] [System.Management.Automation.PSCredential] [System.Management.Automation.CredentialAttribute()] @@ -704,7 +704,7 @@ function Set-TargetResource { ## Find the associated AD property $adProperty = $adPropertyMap | Where-Object { $_.Parameter -eq $parameter }; - + if ([System.String]::IsNullOrEmpty($adProperty)) { ## We can't do anything is an empty AD property! @@ -750,20 +750,20 @@ function Set-TargetResource } } #end if replace existing value } - + } #end if TargetResource parameter } #end foreach PSBoundParameter - + ## Only pass -Remove and/or -Replace if we have something to set/change if ($replaceUserProperties.Count -gt 0) - { + { $setADUserParams['Replace'] = $replaceUserProperties; } if ($removeUserProperties.Count -gt 0) - { + { $setADUserParams['Remove'] = $removeUserProperties; } - + Write-Verbose -Message ($LocalizedData.UpdatingADUser -f $UserName); [ref] $null = Set-ADUser @setADUserParams -Enabled $Enabled; } @@ -792,7 +792,7 @@ function Assert-Parameters [Parameter(ValueFromRemainingArguments)] $IgnoredArguments ) - + ## We cannot test/set passwords on disabled AD accounts if (($PSBoundParameters.ContainsKey('Password')) -and ($Enabled -eq $false)) { @@ -816,12 +816,12 @@ function Test-Password [Parameter(Mandatory)] [System.String] $UserName, - + [Parameter(Mandatory)] [System.Management.Automation.PSCredential] [System.Management.Automation.CredentialAttribute()] $Password, - + [ValidateNotNull()] [System.Management.Automation.PSCredential] [System.Management.Automation.CredentialAttribute()] @@ -830,7 +830,7 @@ function Test-Password Write-Verbose -Message ($LocalizedData.CreatingADDomainConnection -f $DomainName); Add-Type -AssemblyName 'System.DirectoryServices.AccountManagement'; - + if ($DomainAdministratorCredential) { $principalContext = New-Object System.DirectoryServices.AccountManagement.PrincipalContext( diff --git a/README.md b/README.md index 6f98bc43c..53254976f 100644 --- a/README.md +++ b/README.md @@ -237,6 +237,7 @@ Setting an ODJ Request file path for a configuration that creates a computer acc * xADDomainController: Customer identified two cases of incorrect variables being called in Verbose output messages. Corrected. * xADComputer: New resource added. * xADComputer: Added ODJRequestFile support. +* Fixed PSScriptAnalyzer Errors with v1.6.0. ### 2.11.0.0 * xWaitForADDomain: Made explicit credentials optional and other various updates diff --git a/Tests/Unit/MSFT_xADCommon.Tests.ps1 b/Tests/Unit/MSFT_xADCommon.Tests.ps1 index 9e3a5b404..6a991c8f3 100644 --- a/Tests/Unit/MSFT_xADCommon.Tests.ps1 +++ b/Tests/Unit/MSFT_xADCommon.Tests.ps1 @@ -1,3 +1,6 @@ +[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingConvertToSecureStringWithPlainText', '')] +param() + $Global:DSCModuleName = 'xActiveDirectory' # Example xNetworking $Global:DSCResourceName = 'MSFT_xADCommon' # Example MSFT_xFirewall diff --git a/Tests/Unit/MSFT_xADComputer.Tests.ps1 b/Tests/Unit/MSFT_xADComputer.Tests.ps1 index a7a01ca36..a553f8552 100644 --- a/Tests/Unit/MSFT_xADComputer.Tests.ps1 +++ b/Tests/Unit/MSFT_xADComputer.Tests.ps1 @@ -1,3 +1,6 @@ +[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingConvertToSecureStringWithPlainText', '')] +param() + $Global:DSCModuleName = 'xActiveDirectory' # Example xNetworking $Global:DSCResourceName = 'MSFT_xADComputer' # Example MSFT_xFirewall diff --git a/Tests/Unit/MSFT_xADDomain.Tests.ps1 b/Tests/Unit/MSFT_xADDomain.Tests.ps1 index e07eaa2f8..95755b8e1 100644 --- a/Tests/Unit/MSFT_xADDomain.Tests.ps1 +++ b/Tests/Unit/MSFT_xADDomain.Tests.ps1 @@ -1,3 +1,6 @@ +[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingConvertToSecureStringWithPlainText', '')] +param() + $Global:DSCModuleName = 'xActiveDirectory' # Example xNetworking $Global:DSCResourceName = 'MSFT_xADDomain' # Example MSFT_xFirewall @@ -17,7 +20,7 @@ Import-Module (Join-Path -Path $moduleRoot -ChildPath 'DSCResource.Tests\TestHel $TestEnvironment = Initialize-TestEnvironment ` -DSCModuleName $Global:DSCModuleName ` -DSCResourceName $Global:DSCResourceName ` - -TestType Unit + -TestType Unit #endregion @@ -32,48 +35,48 @@ try InModuleScope $Global:DSCResourceName { #region Pester Test Initialization - + $correctDomainName = 'present.com'; $incorrectDomainName = 'incorrect.com'; $missingDomainName = 'missing.com'; $testAdminCredential = New-Object System.Management.Automation.PSCredential 'DummyUser', (ConvertTo-SecureString 'DummyPassword' -AsPlainText -Force); $invalidCredential = New-Object System.Management.Automation.PSCredential 'Invalid', (ConvertTo-SecureString 'InvalidPassword' -AsPlainText -Force); - + $testDefaultParams = @{ DomainAdministratorCredential = $testAdminCredential; SafemodeAdministratorPassword = $testAdminCredential; } - + #endregion #region Function Get-TargetResource Describe "$($Global:DSCResourceName)\Get-TargetResource" { - + Mock Assert-Module -ParameterFilter { $ModuleName -eq 'ADDSDeployment' } { } - + It 'Calls "Assert-Module" to check "ADDSDeployment" module is installed' { Mock Get-ADDomain { } $result = Get-TargetResource @testDefaultParams -DomainName $correctDomainName; - + Assert-MockCalled Assert-Module -ParameterFilter { $ModuleName -eq 'ADDSDeployment' } -Scope It; } - + It 'Returns "System.Collections.Hashtable" object type' { Mock Get-ADDomain { } $result = Get-TargetResource @testDefaultParams -DomainName $correctDomainName; - + $result -is [System.Collections.Hashtable] | Should Be $true; } - + It 'Calls "Get-ADDomain" without credentials if domain member' { Mock Test-DomainMember { $true; } Mock Get-ADDomain -ParameterFilter { $Credential -eq $null } { } - + $result = Get-TargetResource @testDefaultParams -DomainName $correctDomainName; - + Assert-MockCalled Get-ADDomain -ParameterFilter { $Credential -eq $null } -Scope It; } - + It 'Throws "Invalid credentials" when domain is available but authentication fails' { Mock Get-ADDomain -ParameterFilter { $Identity.ToString() -eq $incorrectDomainName } -MockWith { Write-Error -Exception (New-Object System.Security.Authentication.AuthenticationException); @@ -86,107 +89,107 @@ try Mock Get-ADDomain -ParameterFilter { $Identity.ToString() -eq $incorrectDomainName } -MockWith { Write-Error -Exception (New-Object Microsoft.ActiveDirectory.Management.ADIdentityNotFoundException); } - + { Get-TargetResource @testDefaultParams -DomainName $incorrectDomainName } | Should Throw 'Computer is already a domain member'; } - + It 'Does not throw when domain cannot be located' { Mock Get-ADDomain -ParameterFilter { $Identity.ToString() -eq $missingDomainName } -MockWith { Write-Error -Exception (New-Object Microsoft.ActiveDirectory.Management.ADServerDownException); } - + { Get-TargetResource @testDefaultParams -DomainName $missingDomainName } | Should Not Throw; } - + } #endregion #region Function Test-TargetResource Describe "$($Global:DSCResourceName)\Test-TargetResource" { - + $correctDomainName = 'present.com'; $correctChildDomainName = 'present'; $correctDomainNetBIOSName = 'PRESENT'; $incorrectDomainName = 'incorrect.com'; $parentDomainName = 'parent.com'; $testAdminCredential = New-Object System.Management.Automation.PSCredential 'DummyUser', (ConvertTo-SecureString 'DummyPassword' -AsPlainText -Force); - + $testDefaultParams = @{ DomainAdministratorCredential = $testAdminCredential; SafemodeAdministratorPassword = $testAdminCredential; } - + $stubDomain = @{ DomainName = $correctDomainName; DomainNetBIOSName = $correctDomainNetBIOSName; } - + ## Get-TargetResource returns the domain FQDN for .DomainName $stubChildDomain = @{ DomainName = "$correctChildDomainName.$parentDomainName"; ParentDomainName = $parentDomainName; DomainNetBIOSName = $correctDomainNetBIOSName; } - + It 'Returns "True" when "DomainName" matches' { Mock Get-TargetResource { return $stubDomain; } - + $result = Test-TargetResource @testDefaultParams -DomainName $correctDomainName; - + $result | Should Be $true; } - + It 'Returns "False" when "DomainName" does not match' { Mock Get-TargetResource { return $stubDomain; } - + $result = Test-TargetResource @testDefaultParams -DomainName $incorrectDomainName; - + $result | Should Be $false; } - + It 'Returns "True" when "DomainNetBIOSName" matches' { Mock Get-TargetResource { return $stubDomain; } - + $result = Test-TargetResource @testDefaultParams -DomainName $correctDomainName -DomainNetBIOSName $correctDomainNetBIOSName; - + $result | Should Be $true; } - + It 'Returns "False" when "DomainNetBIOSName" does not match' { Mock Get-TargetResource { return $stubDomain; } - + $result = Test-TargetResource @testDefaultParams -DomainName $correctDomainName -DomainNetBIOSName 'INCORRECT'; - + $result | Should Be $false; } - + It 'Returns "True" when "ParentDomainName" matches' { Mock Get-TargetResource { return $stubChildDomain; } - + $result = Test-TargetResource @testDefaultParams -DomainName $correctChildDomainName -ParentDomainName $parentDomainName; - + $result | Should Be $true; } It 'Returns "False" when "ParentDomainName" does not match' { Mock Get-TargetResource { return $stubChildDomain; } - + $result = Test-TargetResource @testDefaultParams -DomainName $correctChildDomainName -ParentDomainName 'incorrect.com'; - + $result | Should Be $false; } - + } #endregion #region Function Set-TargetResource Describe "$($Global:DSCResourceName)\Set-TargetResource" { - + function Install-ADDSForest { param ( $DomainName, $SafeModeAdministratorPassword, $CreateDnsDelegation, $DatabasePath, $DnsDelegationCredential, $InstallDns, $LogPath, $NoRebootOnCompletion, $SysvolPath, - $DomainNetbiosName + $DomainNetbiosName ) } function Install-ADDSDomain { @@ -196,7 +199,7 @@ try $NewDomainNetbiosName, $NoRebootOnCompletion, $SysvolPath ) } - + $testDomainName = 'present.com'; $testParentDomainName = 'parent.com'; $testDomainNetBIOSNameName = 'PRESENT'; @@ -204,192 +207,192 @@ try $testSafemodePassword = (ConvertTo-SecureString 'DummyPassword' -AsPlainText -Force); $testSafemodeCredential = New-Object System.Management.Automation.PSCredential 'Safemode', $testSafemodePassword; $testDelegationCredential = New-Object System.Management.Automation.PSCredential 'Delegation', (ConvertTo-SecureString 'DummyPassword' -AsPlainText -Force); - + $newForestParams = @{ DomainName = $testDomainName; DomainAdministratorCredential = $testAdminCredential; SafemodeAdministratorPassword = $testSafemodeCredential; } - + $newDomainParams = @{ DomainName = $testDomainName; ParentDomainName = $testParentDomainName; DomainAdministratorCredential = $testAdminCredential; SafemodeAdministratorPassword = $testSafemodeCredential; } - + $stubTargetResource = @{ DomainName = $testDomainName; ParentDomainName = $testParentDomainName; DomainNetBIOSName = $testDomainNetBIOSNameName; } Mock Get-TargetResource { return $stubTargetResource; } - + It 'Calls "Install-ADDSForest" with "DomainName" when creating forest' { Mock Install-ADDSForest -ParameterFilter { $DomainName -eq $testDomainName } { } - + Set-TargetResource @newForestParams; - + Assert-MockCalled Install-ADDSForest -ParameterFilter { $DomainName -eq $testDomainName } -Scope It; } - + It 'Calls "Install-ADDSForest" with "SafemodeAdministratorPassword" when creating forest' { Mock Install-ADDSForest -ParameterFilter { $SafemodeAdministratorPassword -eq $testSafemodePassword } { } - + Set-TargetResource @newForestParams; - + Assert-MockCalled Install-ADDSForest -ParameterFilter { $SafemodeAdministratorPassword -eq $testSafemodePassword } -Scope It; } - + It 'Calls "Install-ADDSForest" with "DnsDelegationCredential" when creating forest, if specified' { Mock Install-ADDSForest -ParameterFilter { $DnsDelegationCredential -eq $testDelegationCredential } { } - + Set-TargetResource @newForestParams -DnsDelegationCredential $testDelegationCredential; - + Assert-MockCalled Install-ADDSForest -ParameterFilter { $DnsDelegationCredential -eq $testDelegationCredential } -Scope It; } - + It 'Calls "Install-ADDSForest" with "CreateDnsDelegation" when creating forest, if specified' { Mock Install-ADDSForest -ParameterFilter { $CreateDnsDelegation -eq $true } { } - + Set-TargetResource @newForestParams -DnsDelegationCredential $testDelegationCredential; - + Assert-MockCalled Install-ADDSForest -ParameterFilter { $CreateDnsDelegation -eq $true } -Scope It; } It 'Calls "Install-ADDSForest" with "DatabasePath" when creating forest, if specified' { $testPath = 'TestPath'; Mock Install-ADDSForest -ParameterFilter { $DatabasePath -eq $testPath } { } - + Set-TargetResource @newForestParams -DatabasePath $testPath; - + Assert-MockCalled Install-ADDSForest -ParameterFilter { $DatabasePath -eq $testPath } -Scope It; } - + It 'Calls "Install-ADDSForest" with "LogPath" when creating forest, if specified' { $testPath = 'TestPath'; Mock Install-ADDSForest -ParameterFilter { $LogPath -eq $testPath } { } - + Set-TargetResource @newForestParams -LogPath $testPath; - + Assert-MockCalled Install-ADDSForest -ParameterFilter { $LogPath -eq $testPath } -Scope It; } - + It 'Calls "Install-ADDSForest" with "SysvolPath" when creating forest, if specified' { $testPath = 'TestPath'; Mock Install-ADDSForest -ParameterFilter { $SysvolPath -eq $testPath } { } - + Set-TargetResource @newForestParams -SysvolPath $testPath; - + Assert-MockCalled Install-ADDSForest -ParameterFilter { $SysvolPath -eq $testPath } -Scope It; } - + It 'Calls "Install-ADDSForest" with "DomainNetbiosName" when creating forest, if specified' { Mock Install-ADDSForest -ParameterFilter { $DomainNetbiosName -eq $testDomainNetBIOSNameName } { } - + Set-TargetResource @newForestParams -DomainNetBIOSName $testDomainNetBIOSNameName; - + Assert-MockCalled Install-ADDSForest -ParameterFilter { $DomainNetbiosName -eq $testDomainNetBIOSNameName } -Scope It; } - + #### ADDSDomain - + It 'Calls "Install-ADDSDomain" with "NewDomainName" when creating child domain' { Mock Install-ADDSDomain -ParameterFilter { $NewDomainName -eq $testDomainName } { } - + Set-TargetResource @newDomainParams; - + Assert-MockCalled Install-ADDSDomain -ParameterFilter { $NewDomainName -eq $testDomainName } -Scope It; } - + It 'Calls "Install-ADDSDomain" with "ParentDomainName" when creating child domain' { Mock Install-ADDSDomain -ParameterFilter { $ParentDomainName -eq $testParentDomainName } { } - + Set-TargetResource @newDomainParams; - + Assert-MockCalled Install-ADDSDomain -ParameterFilter { $ParentDomainName -eq $testParentDomainName } -Scope It; } - + It 'Calls "Install-ADDSDomain" with "DomainType" when creating child domain' { Mock Install-ADDSDomain -ParameterFilter { $DomainType -eq 'ChildDomain' } { } - + Set-TargetResource @newDomainParams; - + Assert-MockCalled Install-ADDSDomain -ParameterFilter { $DomainType -eq 'ChildDomain' } -Scope It; } - + It 'Calls "Install-ADDSDomain" with "SafemodeAdministratorPassword" when creating child domain' { Mock Install-ADDSDomain -ParameterFilter { $SafemodeAdministratorPassword -eq $testSafemodePassword } { } - + Set-TargetResource @newDomainParams; - + Assert-MockCalled Install-ADDSDomain -ParameterFilter { $SafemodeAdministratorPassword -eq $testSafemodePassword } -Scope It; } - + It 'Calls "Install-ADDSDomain" with "Credential" when creating child domain' { Mock Install-ADDSDomain -ParameterFilter { $Credential -eq $testParentDomainName } { } - + Set-TargetResource @newDomainParams; - + Assert-MockCalled Install-ADDSDomain -ParameterFilter { $ParentDomainName -eq $testParentDomainName } -Scope It; } - + It 'Calls "Install-ADDSDomain" with "ParentDomainName" when creating child domain' { Mock Install-ADDSDomain -ParameterFilter { $ParentDomainName -eq $testParentDomainName } { } - + Set-TargetResource @newDomainParams; - + Assert-MockCalled Install-ADDSDomain -ParameterFilter { $ParentDomainName -eq $testParentDomainName } -Scope It; } - + It 'Calls "Install-ADDSDomain" with "DnsDelegationCredential" when creating child domain, if specified' { Mock Install-ADDSDomain -ParameterFilter { $DnsDelegationCredential -eq $testDelegationCredential } { } - + Set-TargetResource @newDomainParams -DnsDelegationCredential $testDelegationCredential; - + Assert-MockCalled Install-ADDSDomain -ParameterFilter { $DnsDelegationCredential -eq $testDelegationCredential } -Scope It; } - + It 'Calls "Install-ADDSDomain" with "CreateDnsDelegation" when creating child domain, if specified' { Mock Install-ADDSDomain -ParameterFilter { $CreateDnsDelegation -eq $true } { } - + Set-TargetResource @newDomainParams -DnsDelegationCredential $testDelegationCredential; - + Assert-MockCalled Install-ADDSDomain -ParameterFilter { $CreateDnsDelegation -eq $true } -Scope It; } It 'Calls "Install-ADDSDomain" with "DatabasePath" when creating child domain, if specified' { $testPath = 'TestPath'; Mock Install-ADDSDomain -ParameterFilter { $DatabasePath -eq $testPath } { } - + Set-TargetResource @newDomainParams -DatabasePath $testPath; - + Assert-MockCalled Install-ADDSDomain -ParameterFilter { $DatabasePath -eq $testPath } -Scope It; } - + It 'Calls "Install-ADDSDomain" with "LogPath" when creating child domain, if specified' { $testPath = 'TestPath'; Mock Install-ADDSDomain -ParameterFilter { $LogPath -eq $testPath } { } - + Set-TargetResource @newDomainParams -LogPath $testPath; - + Assert-MockCalled Install-ADDSDomain -ParameterFilter { $LogPath -eq $testPath } -Scope It; } - + It 'Calls "Install-ADDSDomain" with "SysvolPath" when creating child domain, if specified' { $testPath = 'TestPath'; Mock Install-ADDSDomain -ParameterFilter { $SysvolPath -eq $testPath } { } - + Set-TargetResource @newDomainParams -SysvolPath $testPath; - + Assert-MockCalled Install-ADDSDomain -ParameterFilter { $SysvolPath -eq $testPath } -Scope It; } - + It 'Calls "Install-ADDSDomain" with "NewDomainNetbiosName" when creating child domain, if specified' { Mock Install-ADDSDomain -ParameterFilter { $NewDomainNetbiosName -eq $testDomainNetBIOSNameName } { } - + Set-TargetResource @newDomainParams -DomainNetBIOSName $testDomainNetBIOSNameName; - + Assert-MockCalled Install-ADDSDomain -ParameterFilter { $NewDomainNetbiosName -eq $testDomainNetBIOSNameName } -Scope It; } } diff --git a/Tests/Unit/MSFT_xADDomainDefaultPasswordPolicy.Tests.ps1 b/Tests/Unit/MSFT_xADDomainDefaultPasswordPolicy.Tests.ps1 index 7a8242941..560d4fa5e 100644 --- a/Tests/Unit/MSFT_xADDomainDefaultPasswordPolicy.Tests.ps1 +++ b/Tests/Unit/MSFT_xADDomainDefaultPasswordPolicy.Tests.ps1 @@ -1,3 +1,6 @@ +[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingConvertToSecureStringWithPlainText', '')] +param() + $Global:DSCModuleName = 'xActiveDirectory' # Example xNetworking $Global:DSCResourceName = 'MSFT_xADDomainDefaultPasswordPolicy' # Example MSFT_xFirewall @@ -17,7 +20,7 @@ Import-Module (Join-Path -Path $moduleRoot -ChildPath 'DSCResource.Tests\TestHel $TestEnvironment = Initialize-TestEnvironment ` -DSCModuleName $Global:DSCModuleName ` -DSCResourceName $Global:DSCResourceName ` - -TestType Unit + -TestType Unit #endregion @@ -32,7 +35,7 @@ try InModuleScope $Global:DSCResourceName { #region Pester Test Initialization - + $testDomainName = 'contoso.com'; $testDefaultParams = @{ DomainName = $testDomainName; @@ -40,7 +43,7 @@ try $testDomainController = 'testserver.contoso.com'; $testPassword = (ConvertTo-SecureString 'DummyPassword' -AsPlainText -Force); $testCredential = New-Object System.Management.Automation.PSCredential 'Safemode', $testPassword; - + $fakePasswordPolicy = @{ ComplexityEnabled = $true; LockoutDuration = New-TimeSpan -Minutes 30; @@ -52,68 +55,68 @@ try PasswordHistoryCount = 12; ReversibleEncryptionEnabled = $false; } - + #endregion #region Function Get-TargetResource Describe "$($Global:DSCResourceName)\Get-TargetResource" { - + Mock Assert-Module -ParameterFilter { $ModuleName -eq 'ActiveDirectory' } { } - + It 'Calls "Assert-Module" to check "ActiveDirectory" module is installed' { Mock Get-ADDefaultDomainPasswordPolicy { return $fakePasswordPolicy; } $result = Get-TargetResource @testDefaultParams; - + Assert-MockCalled Assert-Module -ParameterFilter { $ModuleName -eq 'ActiveDirectory' } -Scope It; } - + It 'Returns "System.Collections.Hashtable" object type' { Mock Get-ADDefaultDomainPasswordPolicy { return $fakePasswordPolicy; } $result = Get-TargetResource @testDefaultParams; - + $result -is [System.Collections.Hashtable] | Should Be $true; } It 'Calls "Get-ADDefaultDomainPasswordPolicy" without credentials by default' { Mock Get-ADDefaultDomainPasswordPolicy -ParameterFilter { $Credential -eq $null } -MockWith { return $fakePasswordPolicy; } - + $result = Get-TargetResource @testDefaultParams; - + Assert-MockCalled Get-ADDefaultDomainPasswordPolicy -ParameterFilter { $Credential -eq $null } -Scope It; } - + It 'Calls "Get-ADDefaultDomainPasswordPolicy" with credentials when specified' { Mock Get-ADDefaultDomainPasswordPolicy -ParameterFilter { $Credential -eq $testCredential } -MockWith { return $fakePasswordPolicy; } - + $result = Get-TargetResource @testDefaultParams -Credential $testCredential; - + Assert-MockCalled Get-ADDefaultDomainPasswordPolicy -ParameterFilter { $Credential -eq $testCredential } -Scope It; } - + It 'Calls "Get-ADDefaultDomainPasswordPolicy" without server by default' { Mock Get-ADDefaultDomainPasswordPolicy -ParameterFilter { $Server -eq $null } -MockWith { return $fakePasswordPolicy; } - + $result = Get-TargetResource @testDefaultParams; - + Assert-MockCalled Get-ADDefaultDomainPasswordPolicy -ParameterFilter { $Server -eq $null } -Scope It; } - + It 'Calls "Get-ADDefaultDomainPasswordPolicy" with server when specified' { Mock Get-ADDefaultDomainPasswordPolicy -ParameterFilter { $Server -eq $testDomainController } -MockWith { return $fakePasswordPolicy; } - + $result = Get-TargetResource @testDefaultParams -DomainController $testDomainController; - + Assert-MockCalled Get-ADDefaultDomainPasswordPolicy -ParameterFilter { $Server -eq $testDomainController } -Scope It; } - + } #endregion #region Function Test-TargetResource Describe "$($Global:DSCResourceName)\Test-TargetResource" { - + $testDomainName = 'contoso.com'; $testDefaultParams = @{ DomainName = $testDomainName; @@ -121,7 +124,7 @@ try $testDomainController = 'testserver.contoso.com'; $testPassword = (ConvertTo-SecureString 'DummyPassword' -AsPlainText -Force); $testCredential = New-Object System.Management.Automation.PSCredential 'Safemode', $testPassword; - + $stubPasswordPolicy = @{ ComplexityEnabled = $true; LockoutDuration = (New-TimeSpan -Minutes 30).TotalMinutes; @@ -133,43 +136,43 @@ try PasswordHistoryCount = 12; ReversibleEncryptionEnabled = $true; } - + It 'Returns "System.Boolean" object type' { Mock Get-TargetResource { return $stubPasswordPolicy; } - + $result = Test-TargetResource @testDefaultParams; - + $result -is [System.Boolean] | Should Be $true; } - + It 'Calls "Get-TargetResource" with "Credential" parameter when specified' { Mock Get-TargetResource -ParameterFilter { $Credential -eq $testCredential } { return $stubPasswordPolicy; } - + $result = Test-TargetResource @testDefaultParams -Credential $testCredential; - + Assert-MockCalled Get-TargetResource -ParameterFilter { $Credential -eq $testCredential } -Scope It; } - + It 'Calls "Get-TargetResource" with "DomainController" parameter when specified' { Mock Get-TargetResource -ParameterFilter { $DomainController -eq $testDomainController } { return $stubPasswordPolicy; } - + $result = Test-TargetResource @testDefaultParams -DomainController $testDomainController; - + Assert-MockCalled Get-TargetResource -ParameterFilter { $DomainController -eq $testDomainController } -Scope It; } - + foreach ($propertyName in $stubPasswordPolicy.Keys) { It "Passes when '$propertyName' parameter matches resource property value" { Mock Get-TargetResource { return $stubPasswordPolicy; } $propertyDefaultParams = $testDefaultParams.Clone(); $propertyDefaultParams[$propertyName] = $stubPasswordPolicy[$propertyName]; - + $result = Test-TargetResource @propertyDefaultParams; - + $result | Should Be $true; } - + It "Fails when '$propertyName' parameter does not match resource property value" { Mock Get-TargetResource { return $stubPasswordPolicy; } $propertyDefaultParams = $testDefaultParams.Clone(); @@ -186,19 +189,19 @@ try $propertyDefaultParams[$propertyName] = $stubPasswordPolicy[$propertyName] + 1; } } - + $result = Test-TargetResource @propertyDefaultParams; - + $result | Should Be $false; } } #end foreach property - + } #endregion #region Function Set-TargetResource Describe "$($Global:DSCResourceName)\Set-TargetResource" { - + $testDomainName = 'contoso.com'; $testDefaultParams = @{ DomainName = $testDomainName; @@ -206,7 +209,7 @@ try $testDomainController = 'testserver.contoso.com'; $testPassword = (ConvertTo-SecureString 'DummyPassword' -AsPlainText -Force); $testCredential = New-Object System.Management.Automation.PSCredential 'Safemode', $testPassword; - + $stubPasswordPolicy = @{ ComplexityEnabled = $true; LockoutDuration = (New-TimeSpan -Minutes 30).TotalMinutes; @@ -218,63 +221,63 @@ try PasswordHistoryCount = 12; ReversibleEncryptionEnabled = $true; } - + Mock Assert-Module -ParameterFilter { $ModuleName -eq 'ActiveDirectory' } { } - + It 'Calls "Assert-Module" to check "ActiveDirectory" module is installed' { Mock Set-ADDefaultDomainPasswordPolicy { } $result = Set-TargetResource @testDefaultParams; - + Assert-MockCalled Assert-Module -ParameterFilter { $ModuleName -eq 'ActiveDirectory' } -Scope It; } - + It 'Calls "Set-ADDefaultDomainPasswordPolicy" without "Credential" parameter by default' { Mock Set-ADDefaultDomainPasswordPolicy -ParameterFilter { $Credential -eq $null } -MockWith { } - + $result = Set-TargetResource @testDefaultParams; - + Assert-MockCalled Set-ADDefaultDomainPasswordPolicy -ParameterFilter { $Credential -eq $null } -Scope It; } - + It 'Calls "Set-ADDefaultDomainPasswordPolicy" with "Credential" parameter when specified' { Mock Set-ADDefaultDomainPasswordPolicy -ParameterFilter { $Credential -eq $testCredential } -MockWith { } - + $result = Set-TargetResource @testDefaultParams -Credential $testCredential; - + Assert-MockCalled Set-ADDefaultDomainPasswordPolicy -ParameterFilter { $Credential -eq $testCredential } -Scope It; } - + It 'Calls "Set-ADDefaultDomainPasswordPolicy" without "Server" parameter by default' { Mock Set-ADDefaultDomainPasswordPolicy -ParameterFilter { $Server -eq $null } -MockWith { } - + $result = Set-TargetResource @testDefaultParams; - + Assert-MockCalled Set-ADDefaultDomainPasswordPolicy -ParameterFilter { $Server -eq $null } -Scope It; } - + It 'Calls "Set-ADDefaultDomainPasswordPolicy" with "Server" parameter when specified' { Mock Set-ADDefaultDomainPasswordPolicy -ParameterFilter { $Server -eq $testDomainController } -MockWith { } - + $result = Set-TargetResource @testDefaultParams -DomainController $testDomainController; - + Assert-MockCalled Set-ADDefaultDomainPasswordPolicy -ParameterFilter { $Server -eq $testDomainController } -Scope It; } - + foreach ($propertyName in $stubPasswordPolicy.Keys) { It "Calls 'Set-ADDefaultDomainPasswordPolicy' with '$propertyName' parameter when specified" { $propertyDefaultParams = $testDefaultParams.Clone(); $propertyDefaultParams[$propertyName] = $stubPasswordPolicy[$propertyName]; Mock Set-ADDefaultDomainPasswordPolicy -ParameterFilter { $PSBoundParameters.ContainsKey($propertyName) } -MockWith { } - + $result = Set-TargetResource @propertyDefaultParams; - + Assert-MockCalled Set-ADDefaultDomainPasswordPolicy -ParameterFilter { $PSBoundParameters.ContainsKey($propertyName) } -Scope It; } - + } #end foreach property name - + } #endregion diff --git a/Tests/Unit/MSFT_xADGroup.Tests.ps1 b/Tests/Unit/MSFT_xADGroup.Tests.ps1 index 7219bb7ec..901fa1fa7 100644 --- a/Tests/Unit/MSFT_xADGroup.Tests.ps1 +++ b/Tests/Unit/MSFT_xADGroup.Tests.ps1 @@ -1,3 +1,6 @@ +[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingConvertToSecureStringWithPlainText', '')] +param() + $Global:DSCModuleName = 'xActiveDirectory' # Example xNetworking $Global:DSCResourceName = 'MSFT_xADGroup' # Example MSFT_xFirewall @@ -17,7 +20,7 @@ Import-Module (Join-Path -Path $moduleRoot -ChildPath 'DSCResource.Tests\TestHel $TestEnvironment = Initialize-TestEnvironment ` -DSCModuleName $Global:DSCModuleName ` -DSCResourceName $Global:DSCResourceName ` - -TestType Unit + -TestType Unit #endregion # Begin Testing @@ -42,10 +45,10 @@ try Notes = 'This is a test AD group'; ManagedBy = 'CN=User 1,CN=Users,DC=contoso,DC=com'; } - + $testAbsentParams = $testPresentParams.Clone(); $testAbsentParams['Ensure'] = 'Absent'; - + $fakeADGroup = @{ Name = $testPresentParams.GroupName; Identity = $testPresentParams.GroupName; @@ -82,156 +85,156 @@ try #region Function Get-TargetResource Describe "$($Global:DSCResourceName)\Get-TargetResource" { - + Mock Assert-Module -ParameterFilter { $ModuleName -eq 'ActiveDirectory' } { } - - It 'Calls "Assert-Module" to check AD module is installed' { + + It 'Calls "Assert-Module" to check AD module is installed' { Mock Get-ADGroup { return $fakeADGroup; } Mock Get-ADGroupMember { return @($fakeADUser1, $fakeADUser2); } - + $result = Get-TargetResource @testPresentParams; # -DomainName $correctDomainName; - + Assert-MockCalled Assert-Module -ParameterFilter { $ModuleName -eq 'ActiveDirectory' } -Scope It; } - + It "Returns 'Ensure' is 'Present' when group exists" { Mock Get-ADGroup { return $fakeADGroup; } Mock Get-ADGroupMember { return @($fakeADUser1, $fakeADUser2); } - + (Get-TargetResource @testPresentParams).Ensure | Should Be 'Present'; } - + It "Returns 'Ensure' is 'Absent' when group does not exist" { Mock Get-ADGroup { throw New-Object Microsoft.ActiveDirectory.Management.ADIdentityNotFoundException } - + (Get-TargetResource @testPresentParams).Ensure | Should Be 'Absent'; } - - + + It "Calls 'Get-ADGroup' with 'Server' parameter when 'DomainController' specified" { Mock Get-ADGroup -ParameterFilter { $Server -eq $testDomainController } -MockWith { return $fakeADGroup; } Mock Get-ADGroupMember { return @($fakeADUser1, $fakeADUser2); } - + Get-TargetResource @testPresentParams -DomainController $testDomainController; - + Assert-MockCalled Get-ADGroup -ParameterFilter { $Server -eq $testDomainController } -Scope It; } - + It "Calls 'Get-ADGroup' with 'Credential' parameter when specified" { Mock Get-ADGroup -ParameterFilter { $Credential -eq $testCredentials } -MockWith { return $fakeADGroup; } Mock Get-ADGroupMember { return @($fakeADUser1, $fakeADUser2); } - + Get-TargetResource @testPresentParams -Credential $testCredentials; - + Assert-MockCalled Get-ADGroup -ParameterFilter { $Credential -eq $testCredentials } -Scope It; } - + It "Calls 'Get-ADGroupMember' with 'Server' parameter when 'DomainController' specified" { Mock Get-ADGroup -MockWith { return $fakeADGroup; } Mock Get-ADGroupMember -ParameterFilter { $Server -eq $testDomainController } -MockWith { return @($fakeADUser1, $fakeADUser2); } - + Get-TargetResource @testPresentParams -DomainController $testDomainController; - + Assert-MockCalled Get-ADGroupMember -ParameterFilter { $Server -eq $testDomainController } -Scope It; } - + It "Calls 'Get-ADGroupMember' with 'Credential' parameter when specified" { Mock Get-ADGroup -MockWith { return $fakeADGroup; } Mock Get-ADGroupMember -ParameterFilter { $Credential -eq $testCredentials } -MockWith { return @($fakeADUser1, $fakeADUser2); } - + Get-TargetResource @testPresentParams -Credential $testCredentials; - + Assert-MockCalled Get-ADGroupMember -ParameterFilter { $Credential -eq $testCredentials } -Scope It; } } #end region - + #region Function Test-TargetResource Describe "$($Global:DSCResourceName)\Test-TargetResource" { - + Mock Assert-Module -ParameterFilter { $ModuleName -eq 'ActiveDirectory' } { } - + foreach ($attribute in @('SamAccountName','DistinguishedName','ObjectGUID','SID')) { - + It "Passes when group 'Members' match using '$attribute'" { Mock Get-ADGroup { return $fakeADGroup; } Mock Get-ADGroupMember { return @($fakeADUser1, $fakeADUser2); } - + $targetResource = Test-TargetResource @testPresentParams -Members $fakeADUser1.$attribute, $fakeADUser2.$attribute -MembershipAttribute $attribute; - + $targetResource | Should Be $true; } - + It "Fails when group membership counts do not match using '$attribute'" { Mock Get-ADGroup { return $fakeADGroup; } Mock Get-ADGroupMember { return @($fakeADUser1); } - + $targetResource = Test-TargetResource @testPresentParams -Members $fakeADUser2.$attribute, $fakeADUser3.$attribute -MembershipAttribute $attribute; - + $targetResource | Should Be $false; } - + It "Fails when group 'Members' do not match using '$attribute'" { Mock Get-ADGroup { return $fakeADGroup; } Mock Get-ADGroupMember { return @($fakeADUser1, $fakeADUser2); } - + $targetResource = Test-TargetResource @testPresentParams -Members $fakeADUser2.$attribute, $fakeADUser3.$attribute -MembershipAttribute $attribute; - + $targetResource | Should Be $false; } - + It "Passes when specified 'MembersToInclude' match using '$attribute'" { Mock Get-ADGroup { return $fakeADGroup; } Mock Get-ADGroupMember { return @($fakeADUser1, $fakeADUser2); } - + $targetResource = Test-TargetResource @testPresentParams -MembersToInclude $fakeADUser2.$attribute -MembershipAttribute $attribute; - + $targetResource | Should Be $true; } - + It "Fails when specified 'MembersToInclude' are missing using '$attribute'" { Mock Get-ADGroup { return $fakeADGroup; } Mock Get-ADGroupMember { return @($fakeADUser1, $fakeADUser2); } - + $targetResource = Test-TargetResource @testPresentParams -MembersToInclude $fakeADUser3.$attribute -MembershipAttribute $attribute; - + $targetResource | Should Be $false; } - + It "Passes when specified 'MembersToExclude' are missing using '$attribute'" { Mock Get-ADGroup { return $fakeADGroup; } Mock Get-ADGroupMember { return @($fakeADUser1, $fakeADUser2); } - + $targetResource = Test-TargetResource @testPresentParams -MembersToExclude $fakeADUser3.$attribute -MembershipAttribute $attribute; - + $targetResource | Should Be $true; } - + It "Fails when when specified 'MembersToExclude' match using '$attribute'" { Mock Get-ADGroup { return $fakeADGroup; } Mock Get-ADGroupMember { return @($fakeADUser1, $fakeADUser2); } - + $targetResource = Test-TargetResource @testPresentParams -MembersToExclude $fakeADUser2.$attribute -MembershipAttribute $attribute; - + $targetResource | Should Be $false; } - + } #end foreach attribute It "Fails when group does not exist and 'Ensure' is 'Present'" { Mock Get-TargetResource { return $testAbsentParams } - + Test-TargetResource @testPresentParams | Should Be $false } - + It "Fails when group exists, 'Ensure' is 'Present' but 'Scope' is wrong" { Mock Get-TargetResource { $duffADGroup = $testPresentParams.Clone(); $duffADGroup['GroupScope'] = 'Universal'; return $duffADGroup; } - + Test-TargetResource @testPresentParams | Should Be $false; } @@ -241,27 +244,27 @@ try $duffADGroup['Category'] = 'Distribution'; return $duffADGroup; } - + Test-TargetResource @testPresentParams | Should Be $false; } - + It "Fails when group exists, 'Ensure' is 'Present' but 'Path' is wrong" { Mock Get-TargetResource { $duffADGroup = $testPresentParams.Clone(); $duffADGroup['Path'] = 'OU=WrongPath,DC=contoso,DC=com'; return $duffADGroup; } - + Test-TargetResource @testPresentParams | Should Be $false; } - + It "Fails when group exists, 'Ensure' is 'Present' but 'Description' is wrong" { Mock Get-TargetResource { $duffADGroup = $testPresentParams.Clone(); $duffADGroup['Description'] = 'Test AD group description is wrong'; return $duffADGroup; } - + Test-TargetResource @testPresentParams | Should Be $false; } @@ -271,7 +274,7 @@ try $duffADGroup['DisplayName'] = 'Wrong display name'; return $duffADGroup; } - + Test-TargetResource @testPresentParams | Should Be $false; } @@ -281,62 +284,62 @@ try $duffADGroup['ManagedBy'] = $fakeADUser3.DistinguishedName; return $duffADGroup; } - + Test-TargetResource @testPresentParams | Should Be $false; } - + It "Fails when group exists, 'Ensure' is 'Present' but 'Notes' is wrong" { Mock Get-TargetResource { $duffADGroup = $testPresentParams.Clone(); $duffADGroup['Notes'] = 'These notes are clearly wrong'; return $duffADGroup; } - + Test-TargetResource @testPresentParams | Should Be $false; } - + It "Fails when group exists and 'Ensure' is 'Absent'" { Mock Get-TargetResource { return $testPresentParams } - + Test-TargetResource @testAbsentParams | Should Be $false } It "Passes when group exists, target matches and 'Ensure' is 'Present'" { - Mock Get-TargetResource { return $testPresentParams } - + Mock Get-TargetResource { return $testPresentParams } + Test-TargetResource @testPresentParams | Should Be $true } It "Passes when group does not exist and 'Ensure' is 'Absent'" { - Mock Get-TargetResource { return $testAbsentParams } - + Mock Get-TargetResource { return $testAbsentParams } + Test-TargetResource @testAbsentParams | Should Be $true } } #end region - + #region Function Set-TargetResource Describe "$($Global:DSCResourceName)\Set-TargetResource" { - + Mock Assert-Module -ParameterFilter { $ModuleName -eq 'ActiveDirectory' } { } It "Calls 'New-ADGroup' when 'Ensure' is 'Present' and the group does not exist" { Mock Get-ADGroup { throw New-Object Microsoft.ActiveDirectory.Management.ADIdentityNotFoundException } Mock Set-ADGroup { } Mock New-ADGroup { return [PSCustomObject] $fakeADGroup; } - + Set-TargetResource @testPresentParams; - + Assert-MockCalled New-ADGroup -Scope It; } - + $testProperties = @{ Description = 'Test AD Group description is wrong'; ManagedBy = $fakeADUser3.DistinguishedName; DisplayName = 'Test DisplayName'; } - + foreach ($property in $testProperties.Keys) { It "Calls 'Set-ADGroup' when 'Ensure' is 'Present' and '$property' is specified" { Mock Set-ADGroup { } @@ -346,9 +349,9 @@ try $duffADGroup[$property] = $testProperties.$property; return $duffADGroup; } - + Set-TargetResource @testPresentParams; - + Assert-MockCalled Set-ADGroup -Scope It -Exactly 1; } } @@ -361,9 +364,9 @@ try $duffADGroup['GroupCategory'] = 'Distribution'; return $duffADGroup; } - + Set-TargetResource @testPresentParams; - + Assert-MockCalled Set-ADGroup -ParameterFilter { $GroupCategory -eq $testPresentParams.Category } -Scope It -Exactly 1; } @@ -375,9 +378,9 @@ try $duffADGroup['Info'] = 'My test note..'; return $duffADGroup; } - + Set-TargetResource @testPresentParams; - + Assert-MockCalled Set-ADGroup -ParameterFilter { $Replace -ne $null } -Scope It -Exactly 1; } @@ -389,9 +392,9 @@ try $duffADGroup['GroupScope'] = 'DomainLocal'; return $duffADGroup; } - + Set-TargetResource @testPresentParams; - + Assert-MockCalled Set-ADGroup -Scope It -Exactly 2; } @@ -400,9 +403,9 @@ try Mock Set-ADGroup { } Mock Add-ADGroupMember { } Mock New-ADGroup { return [PSCustomObject] $fakeADGroup; } - + Set-TargetResource @testPresentParams -Members @($fakeADUser1.SamAccountName, $fakeADUser2.SamAccountName); - + Assert-MockCalled Add-ADGroupMember -Scope It; } @@ -411,9 +414,9 @@ try Mock Set-ADGroup { } Mock Add-ADGroupMember { } Mock New-ADGroup { return [PSCustomObject] $fakeADGroup; } - + Set-TargetResource @testPresentParams -MembersToInclude @($fakeADUser1.SamAccountName, $fakeADUser2.SamAccountName); - + Assert-MockCalled Add-ADGroupMember -Scope It; } @@ -431,7 +434,7 @@ try Assert-MockCalled Move-ADObject -Scope It; } - + It "Resets group membership when 'Ensure' is 'Present' and 'Members' is incorrect" { Mock Get-ADGroup { return [PSCustomObject] $fakeADGroup; } Mock Set-ADGroup { } @@ -444,7 +447,7 @@ try Assert-MockCalled Remove-ADGroupMember -Scope It -Exactly 1; Assert-MockCalled Add-ADGroupMember -Scope It -Exactly 1; } - + It "Does not reset group membership when 'Ensure' is 'Present' and existing group is empty" { Mock Get-ADGroup { return [PSCustomObject] $fakeADGroup; } Mock Set-ADGroup { } @@ -466,7 +469,7 @@ try Assert-MockCalled Remove-ADGroupMember -Scope It -Exactly 1; } - + It "Adds members when 'Ensure' is 'Present' and 'MembersToInclude' is incorrect" { Mock Get-ADGroup { return [PSCustomObject] $fakeADGroup; } Mock Set-ADGroup { } @@ -477,18 +480,18 @@ try Assert-MockCalled Add-ADGroupMember -Scope It -Exactly 1; } - + It "Removes group when 'Ensure' is 'Absent' and group exists" { Mock Get-ADGroup { return $fakeADGroup; } Mock Remove-ADGroup { } - + Set-TargetResource @testAbsentParams; - + Assert-MockCalled Remove-ADGroup -Scope It; } } #end region - + } #end region } diff --git a/Tests/Unit/MSFT_xADOrganizationalUnit.Tests.ps1 b/Tests/Unit/MSFT_xADOrganizationalUnit.Tests.ps1 index a5dc017e6..ace559401 100644 --- a/Tests/Unit/MSFT_xADOrganizationalUnit.Tests.ps1 +++ b/Tests/Unit/MSFT_xADOrganizationalUnit.Tests.ps1 @@ -1,3 +1,6 @@ +[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingConvertToSecureStringWithPlainText', '')] +param() + $Global:DSCModuleName = 'xActiveDirectory' # Example xNetworking $Global:DSCResourceName = 'MSFT_xADOrganizationalUnit' # Example MSFT_xFirewall @@ -17,7 +20,7 @@ Import-Module (Join-Path -Path $moduleRoot -ChildPath 'DSCResource.Tests\TestHel $TestEnvironment = Initialize-TestEnvironment ` -DSCModuleName $Global:DSCModuleName ` -DSCResourceName $Global:DSCResourceName ` - -TestType Unit + -TestType Unit #endregion # Begin Testing @@ -43,10 +46,10 @@ try Description = 'Test AD OU description'; Ensure = 'Present'; } - + $testAbsentParams = $testPresentParams.Clone(); $testAbsentParams['Ensure'] = 'Absent'; - + $protectedFakeAdOu = @{ Name = $testPresentParams.Name; ProtectedFromAccidentalDeletion = $true; @@ -60,7 +63,7 @@ try Mock Assert-Module -MockWith { } Mock Get-ADOrganizationalUnit -MockWith { return [PSCustomObject] $protectedFakeAdOu } $targetResource = Get-TargetResource -Name $testPresentParams.Name -Path $testPresentParams.Path - + $targetResource -is [System.Collections.Hashtable] | Should Be $true } @@ -68,7 +71,7 @@ try Mock Assert-Module -MockWith { } Mock Get-ADOrganizationalUnit -MockWith { return [PSCustomObject] $protectedFakeAdOu } $targetResource = Get-TargetResource -Name $testPresentParams.Name -Path $testPresentParams.Path - + $targetResource.Ensure | Should Be 'Present' } @@ -76,7 +79,7 @@ try Mock Assert-Module -MockWith { } Mock Get-ADOrganizationalUnit -MockWith { } $targetResource = Get-TargetResource -Name $testPresentParams.Name -Path $testPresentParams.Path - + $targetResource.Ensure | Should Be 'Absent' } @@ -84,7 +87,7 @@ try Mock Assert-Module -MockWith { } Mock Get-ADOrganizationalUnit -MockWith { return [PSCustomObject] $protectedFakeAdOu } $targetResource = Get-TargetResource -Name $testPresentParams.Name -Path $testPresentParams.Path - + $targetResource.ProtectedFromAccidentalDeletion | Should Be $true } @@ -96,7 +99,7 @@ try return [PSCustomObject] $unprotectedFakeAdOu } $targetResource = Get-TargetResource -Name $testPresentParams.Name -Path $testPresentParams.Path - + $targetResource.ProtectedFromAccidentalDeletion | Should Be $false } @@ -115,7 +118,7 @@ try } #endregion - + #region Function Test-TargetResource Describe "$($Global:DSCResourceName)\Test-TargetResource" { @@ -123,21 +126,21 @@ try Mock Assert-Module -MockWith { } Mock Get-ADOrganizationalUnit { return [PSCustomObject] $protectedFakeAdOu } $targetResource = Test-TargetResource @testPresentParams - + $targetResource -is [System.Boolean] | Should Be $true } It 'Fails when OU does not exist and "Ensure" = "Present"' { Mock Assert-Module -MockWith { } Mock Get-ADOrganizationalUnit -MockWith { } - + Test-TargetResource @testPresentParams | Should Be $false } It 'Fails when OU does exist and "Ensure" = "Absent"' { Mock Assert-Module -MockWith { } Mock Get-ADOrganizationalUnit -MockWith { return [PSCustomObject] $protectedFakeAdOu } - + Test-TargetResource @testAbsentParams | Should Be $false } @@ -146,7 +149,7 @@ try Mock Get-ADOrganizationalUnit { return [PSCustomObject] $protectedFakeAdOu } $testDescriptionParams = $testPresentParams.Clone() $testDescriptionParams['Description'] = 'Wrong description' - + Test-TargetResource @testDescriptionParams | Should Be $false } @@ -155,21 +158,21 @@ try Mock Get-ADOrganizationalUnit { return [PSCustomObject] $protectedFakeAdOu } $testProtectedFromAccidentalDeletionParams = $testPresentParams.Clone() $testProtectedFromAccidentalDeletionParams['ProtectedFromAccidentalDeletion'] = $false - + Test-TargetResource @testProtectedFromAccidentalDeletionParams | Should Be $false } It 'Passes when OU does exist, "Ensure" = "Present" and all properties are correct' { Mock Assert-Module -MockWith { } Mock Get-ADOrganizationalUnit -MockWith { return [PSCustomObject] $protectedFakeAdOu } - + Test-TargetResource @testPresentParams | Should Be $true } It 'Passes when OU does not exist and "Ensure" = "Absent"' { Mock Assert-Module -MockWith { } Mock Get-ADOrganizationalUnit -MockWith { } - + Test-TargetResource @testAbsentParams | Should Be $true } @@ -178,13 +181,13 @@ try Mock Get-ADOrganizationalUnit { return [PSCustomObject] $protectedFakeAdOu } $testEmptyDescriptionParams = $testPresentParams.Clone() $testEmptyDescriptionParams['Description'] = '' - + Test-TargetResource @testEmptyDescriptionParams | Should Be $true } } #endregion - + #region Function Set-TargetResource Describe "$($Global:DSCResourceName)\Set-TargetResource" { @@ -192,7 +195,7 @@ try Mock Assert-Module -MockWith { } Mock Get-ADOrganizationalUnit -MockWith { } Mock New-ADOrganizationalUnit -ParameterFilter { $Name -eq $testPresentParams.Name } -MockWith { } - + Set-TargetResource @testPresentParams Assert-MockCalled New-ADOrganizationalUnit -ParameterFilter { $Name -eq $testPresentParams.Name } -Scope It } @@ -201,7 +204,7 @@ try Mock Assert-Module -MockWith { } Mock Get-ADOrganizationalUnit -MockWith { } Mock New-ADOrganizationalUnit -ParameterFilter { $Credential -eq $testCredential } -MockWith { } - + Set-TargetResource @testPresentParams -Credential $testCredential Assert-MockCalled New-ADOrganizationalUnit -ParameterFilter { $Credential -eq $testCredential } -Scope It } @@ -210,7 +213,7 @@ try Mock Assert-Module -MockWith { } Mock Get-ADOrganizationalUnit -MockWith { return [PSCustomObject] $protectedFakeAdOu } Mock Set-ADOrganizationalUnit -MockWith { } - + Set-TargetResource @testPresentParams Assert-MockCalled Set-ADOrganizationalUnit -Scope It } @@ -219,7 +222,7 @@ try Mock Assert-Module -MockWith { } Mock Get-ADOrganizationalUnit -MockWith { return [PSCustomObject] $protectedFakeAdOu } Mock Set-ADOrganizationalUnit -ParameterFilter { $Credential -eq $testCredential } -MockWith { } - + Set-TargetResource @testPresentParams -Credential $testCredential Assert-MockCalled Set-ADOrganizationalUnit -ParameterFilter { $Credential -eq $testCredential } -Scope It } @@ -232,7 +235,7 @@ try return [PSCustomObject] $unprotectedFakeAdOu } Mock Remove-ADOrganizationalUnit -MockWith { } - + Set-TargetResource @testAbsentParams Assert-MockCalled Remove-ADOrganizationalUnit -Scope It } @@ -241,7 +244,7 @@ try Mock Assert-Module -MockWith { } Mock Get-ADOrganizationalUnit -MockWith { return [PSCustomObject] $protectedFakeAdOu } Mock Remove-ADOrganizationalUnit -MockWith { } - + Set-TargetResource @testAbsentParams Assert-MockCalled Remove-ADOrganizationalUnit -Scope It } @@ -250,7 +253,7 @@ try Mock Assert-Module -MockWith { } Mock Get-ADOrganizationalUnit -MockWith { return [PSCustomObject] $protectedFakeAdOu } Mock Remove-ADOrganizationalUnit -ParameterFilter { $Credential -eq $testCredential } -MockWith { } - + Set-TargetResource @testAbsentParams -Credential $testCredential Assert-MockCalled Remove-ADOrganizationalUnit -ParameterFilter { $Credential -eq $testCredential } -Scope It } @@ -260,7 +263,7 @@ try Mock Get-ADOrganizationalUnit -MockWith { return [PSCustomObject] $protectedFakeAdOu } Mock Remove-ADOrganizationalUnit -MockWith { } Mock Set-ADOrganizationalUnit -MockWith { } - + Set-TargetResource @testAbsentParams Assert-MockCalled Set-ADOrganizationalUnit -Scope It } @@ -274,14 +277,14 @@ try } Mock Remove-ADOrganizationalUnit -MockWith { } Mock Set-ADOrganizationalUnit -MockWith { } - + Set-TargetResource @testAbsentParams Assert-MockCalled Set-ADOrganizationalUnit -Scope It -Exactly 0 } } #endregion - + } #endregion } diff --git a/Tests/Unit/MSFT_xADUser.Tests.ps1 b/Tests/Unit/MSFT_xADUser.Tests.ps1 index 10d1959be..a6877e4c2 100644 --- a/Tests/Unit/MSFT_xADUser.Tests.ps1 +++ b/Tests/Unit/MSFT_xADUser.Tests.ps1 @@ -1,3 +1,6 @@ +[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingConvertToSecureStringWithPlainText', '')] +param() + $Global:DSCModuleName = 'xActiveDirectory' # Example xNetworking $Global:DSCResourceName = 'MSFT_xADUser' # Example MSFT_xFirewall @@ -17,7 +20,7 @@ Import-Module (Join-Path -Path $moduleRoot -ChildPath 'DSCResource.Tests\TestHel $TestEnvironment = Initialize-TestEnvironment ` -DSCModuleName $Global:DSCModuleName ` -DSCResourceName $Global:DSCResourceName ` - -TestType Unit + -TestType Unit #endregion @@ -36,10 +39,10 @@ try UserName = 'TestUser'; Ensure = 'Present'; } - + $testAbsentParams = $testPresentParams.Clone(); $testAbsentParams['Ensure'] = 'Absent'; - + $fakeADUser = @{ DistinguishedName = "CN=$($testPresentParams.UserName),CN=Users,DC=contoso,DC=com"; Enabled = $true; @@ -63,93 +66,93 @@ try #region Function Get-TargetResource Describe "$($Global:DSCResourceName)\Get-TargetResource" { - + It "Returns a 'System.Collections.Hashtable' object type" { Mock Get-ADUser { return [PSCustomObject] $fakeADUser; } - + $adUser = Get-TargetResource @testPresentParams; - + $adUser -is [System.Collections.Hashtable] | Should Be $true; } - + It "Returns 'Ensure' is 'Present' when user account exists" { Mock Get-ADUser { return [PSCustomObject] $fakeADUser; } - + $adUser = Get-TargetResource @testPresentParams; - + $adUser.Ensure | Should Be 'Present'; } - + It "Returns 'Ensure' is 'Absent' when user account does not exist" { Mock Get-ADUser { throw New-Object Microsoft.ActiveDirectory.Management.ADIdentityNotFoundException } - + $adUser = Get-TargetResource @testPresentParams; - + $adUser.Ensure | Should Be 'Absent'; } - + It "Calls 'Get-ADUser' with 'Server' parameter when 'DomainController' specified" { Mock Get-ADUser -ParameterFilter { $Server -eq $testDomainController } -MockWith { return [PSCustomObject] $fakeADUser; } - + Get-TargetResource @testPresentParams -DomainController $testDomainController; - + Assert-MockCalled Get-ADUser -ParameterFilter { $Server -eq $testDomainController } -Scope It; } - + It "Calls 'Get-ADUser' with 'Credential' parameter when 'DomainAdministratorCredential' specified" { Mock Get-ADUser -ParameterFilter { $Credential -eq $testCredential } -MockWith { return [PSCustomObject] $fakeADUser; } - + Get-TargetResource @testPresentParams -DomainAdministratorCredential $testCredential; - + Assert-MockCalled Get-ADUser -ParameterFilter { $Credential -eq $testCredential } -Scope It; } - + } #endregion - + #region Function Test-TargetResource Describe "$($Global:DSCResourceName)\Test-TargetResource" { - + It "Passes when user account does not exist and 'Ensure' is 'Absent'" { Mock Get-TargetResource { return $testAbsentParams } - + Test-TargetResource @testAbsentParams | Should Be $true; } - + It "Passes when user account exists and 'Ensure' is 'Present'" { Mock Get-TargetResource { return $testPresentParams } - + Test-TargetResource @testPresentParams | Should Be $true; } - + It "Passes when user account password matches and 'Password' is specified" { Mock Get-TargetResource { return $testPresentParams } Mock Test-Password { return $true; } - + Test-TargetResource @testPresentParams -Password $testCredential | Should Be $true; } - + It "Fails when user account does not exist and 'Ensure' is 'Present'" { Mock Get-TargetResource { return $testAbsentParams } - + Test-TargetResource @testPresentParams | Should Be $false; } - + It "Fails when user account exists, and 'Ensure' is 'Absent'" { Mock Get-TargetResource { return $testPresentParams } - + Test-TargetResource @testAbsentParams | Should Be $false; } - + It "Fails when user account password is incorrect and 'Password' is specified" { Mock Get-TargetResource { return $testPresentParams } Mock Test-Password { return $false; } - + Test-TargetResource @testPresentParams -Password $testCredential | Should Be $false; } - + foreach ($testParameter in $testStringProperties) { - + It "Passes when user account '$testParameter' matches AD account property" { $testParameterValue = 'Test Parameter String Value'; $testValidPresentParams = $testPresentParams.Clone(); @@ -160,10 +163,10 @@ try $validADUser[$testParameter] = $testParameterValue; return $validADUser; } - + Test-TargetResource @testValidPresentParams | Should Be $true; } - + It "Fails when user account '$testParameter' does not match incorrect AD account property value" { $testParameterValue = 'Test Parameter String Value'; $testValidPresentParams = $testPresentParams.Clone(); @@ -174,10 +177,10 @@ try $invalidADUser[$testParameter] = $testParameterValue.Substring(0, ([System.Int32] $testParameterValue.Length/2)); return $invalidADUser; } - + Test-TargetResource @testValidPresentParams | Should Be $false; } - + It "Fails when user account '$testParameter' does not match empty AD account property value" { $testParameterValue = 'Test Parameter String Value'; $testValidPresentParams = $testPresentParams.Clone(); @@ -188,10 +191,10 @@ try $invalidADUser[$testParameter] = ''; return $invalidADUser; } - + Test-TargetResource @testValidPresentParams | Should Be $false; } - + It "Fails when user account '$testParameter' does not match null AD account property value" { $testParameterValue = 'Test Parameter String Value'; $testValidPresentParams = $testPresentParams.Clone(); @@ -202,10 +205,10 @@ try $invalidADUser[$testParameter] = $null; return $invalidADUser; } - + Test-TargetResource @testValidPresentParams | Should Be $false; } - + It "Passes when empty user account '$testParameter' matches empty AD account property" { $testValidPresentParams = $testPresentParams.Clone(); $testValidPresentParams[$testParameter] = $testParameterValue; @@ -214,10 +217,10 @@ try $validADUser[$testParameter] = ''; return $validADUser; } - + Test-TargetResource @testValidPresentParams | Should Be $true; } - + It "Passes when empty user account '$testParameter' matches null AD account property" { $testValidPresentParams = $testPresentParams.Clone(); $testValidPresentParams[$testParameter] = $testParameterValue; @@ -226,14 +229,14 @@ try $validADUser[$testParameter] = $null; return $validADUser; } - + Test-TargetResource @testValidPresentParams | Should Be $true; } - + } #end foreach test string property - + foreach ($testParameter in $testBooleanProperties) { - + It "Passes when user account '$testParameter' matches AD account property" { $testParameterValue = $true; $testValidPresentParams = $testPresentParams.Clone(); @@ -244,10 +247,10 @@ try $validADUser[$testParameter] = $testParameterValue; return $validADUser; } - + Test-TargetResource @testValidPresentParams | Should Be $true; } - + It "Fails when user account '$testParameter' does not match AD account property value" { $testParameterValue = $true; $testValidPresentParams = $testPresentParams.Clone(); @@ -258,33 +261,33 @@ try $invalidADUser[$testParameter] = -not $testParameterValue; return $invalidADUser; } - + Test-TargetResource @testValidPresentParams | Should Be $false; } - + } #end foreach test boolean property - + } #endregion - + #region Function Set-TargetResource Describe "$($Global:DSCResourceName)\Set-TargetResource" { - + It "Calls 'New-ADUser' when 'Ensure' is 'Present' and the account does not exist" { $newUserName = 'NewUser' $newAbsentParams = $testAbsentParams.Clone(); $newAbsentParams['UserName'] = $newUserName; $newPresentParams = $testPresentParams.Clone(); - $newPresentParams['UserName'] = $newUserName; + $newPresentParams['UserName'] = $newUserName; Mock New-ADUser -ParameterFilter { $Name -eq $newUserName } { } Mock Set-ADUser { } Mock Get-TargetResource -ParameterFilter { $Username -eq $newUserName } { return $newAbsentParams; } - + Set-TargetResource @newPresentParams; - + Assert-MockCalled New-ADUser -ParameterFilter { $Name -eq $newUserName } -Scope It; } - + It "Calls 'Move-ADObject' when 'Ensure' is 'Present', the account exists but Path is incorrect" { $testTargetPath = 'CN=Users,DC=contoso,DC=com'; Mock Set-ADUser { } @@ -294,9 +297,9 @@ try return $duffADUser; } Mock Move-ADObject -ParameterFilter { $TargetPath -eq $testTargetPath } -MockWith { } - + Set-TargetResource @testPresentParams -Path $testTargetPath -Enabled $true; - + Assert-MockCalled Move-ADObject -ParameterFilter { $TargetPath -eq $testTargetPath } -Scope It; } @@ -305,22 +308,22 @@ try Mock Set-ADUser { } Mock Get-ADUser { return $fakeADUser; } Mock Rename-ADObject -ParameterFilter { $NewName -eq $testCommonName } -MockWith { } - + Set-TargetResource @testPresentParams -CommonName $testCommonName -Enabled $true; - + Assert-MockCalled Rename-ADObject -ParameterFilter { $NewName -eq $testCommonName } -Scope It; } - + It "Calls 'Set-ADAccountPassword' when 'Password' parameter is specified" { Mock Get-ADUser { return $fakeADUser; } Mock Set-ADUser { } Mock Set-ADAccountPassword -ParameterFilter { $NewPassword -eq $testCredential.Password } -MockWith { } - + Set-TargetResource @testPresentParams -Password $testCredential; - + Assert-MockCalled Set-ADAccountPassword -ParameterFilter { $NewPassword -eq $testCredential.Password } -Scope It; } - + It "Calls 'Set-ADUser' with 'Replace' when existing matching AD property is null" { $testADPropertyName = 'Description'; Mock Get-ADUser { @@ -329,12 +332,12 @@ try return $duffADUser; } Mock Set-ADUser -ParameterFilter { $Replace.ContainsKey($testADPropertyName) } -MockWith { } - + Set-TargetResource @testPresentParams -Description 'My custom description'; - + Assert-MockCalled Set-ADUser -ParameterFilter { $Replace.ContainsKey($testADPropertyName) } -Scope It -Exactly 1; } - + It "Calls 'Set-ADUser' with 'Replace' when existing matching AD property is empty" { $testADPropertyName = 'Description'; Mock Get-ADUser { @@ -343,12 +346,12 @@ try return $duffADUser; } Mock Set-ADUser -ParameterFilter { $Replace.ContainsKey($testADPropertyName) } -MockWith { } - + Set-TargetResource @testPresentParams -Description 'My custom description'; - + Assert-MockCalled Set-ADUser -ParameterFilter { $Replace.ContainsKey($testADPropertyName) } -Scope It -Exactly 1; } - + It "Calls 'Set-ADUser' with 'Remove' when new matching AD property is empty" { $testADPropertyName = 'Description'; Mock Get-ADUser { @@ -357,12 +360,12 @@ try return $duffADUser; } Mock Set-ADUser -ParameterFilter { $Remove.ContainsKey($testADPropertyName) } -MockWith { } - + Set-TargetResource @testPresentParams -Description ''; - + Assert-MockCalled Set-ADUser -ParameterFilter { $Remove.ContainsKey($testADPropertyName) } -Scope It -Exactly 1; } - + It "Calls 'Set-ADUser' with 'Replace' when existing mismatched AD property is null" { $testADPropertyName = 'Title'; Mock Get-ADUser { @@ -371,12 +374,12 @@ try return $duffADUser; } Mock Set-ADUser -ParameterFilter { $Replace.ContainsKey($testADPropertyName) } -MockWith { } - + Set-TargetResource @testPresentParams -JobTitle 'Gaffer'; - + Assert-MockCalled Set-ADUser -ParameterFilter { $Replace.ContainsKey($testADPropertyName) } -Scope It -Exactly 1; } - + It "Calls 'Set-ADUser' with 'Replace' when existing mismatched AD property is empty" { $testADPropertyName = 'Title'; Mock Get-ADUser { @@ -385,12 +388,12 @@ try return $duffADUser; } Mock Set-ADUser -ParameterFilter { $Replace.ContainsKey($testADPropertyName) } -MockWith { } - + Set-TargetResource @testPresentParams -JobTitle 'Gaffer'; - + Assert-MockCalled Set-ADUser -ParameterFilter { $Replace.ContainsKey($testADPropertyName) } -Scope It -Exactly 1; } - + It "Calls 'Set-ADUser' with 'Remove' when new mismatched AD property is empty" { $testADPropertyName = 'Title'; Mock Get-ADUser { @@ -399,24 +402,24 @@ try return $duffADUser; } Mock Set-ADUser -ParameterFilter { $Remove.ContainsKey($testADPropertyName) } -MockWith { } - + Set-TargetResource @testPresentParams -JobTitle ''; - + Assert-MockCalled Set-ADUser -ParameterFilter { $Remove.ContainsKey($testADPropertyName) } -Scope It -Exactly 1; } - + It "Calls 'Remove-ADUser' when 'Ensure' is 'Absent' and user account exists" { Mock Get-ADUser { return [PSCustomObject] $fakeADUser; } Mock Remove-ADUser -ParameterFilter { $Identity.ToString() -eq $testAbsentParams.UserName } -MockWith { } - + Set-TargetResource @testAbsentParams; - + Assert-MockCalled Remove-ADUser -ParameterFilter { $Identity.ToString() -eq $testAbsentParams.UserName } -Scope It; } - + } #endregion - + #region Function Assert-TargetResource Describe "$($Global:DSCResourceName)\Assert-Parameters" { @@ -430,7 +433,7 @@ try } #endregion - + } #endregion } diff --git a/Tests/Unit/MSFT_xWaitForADDomain.Tests.ps1 b/Tests/Unit/MSFT_xWaitForADDomain.Tests.ps1 index 4fbd26c94..fcb6c8880 100644 --- a/Tests/Unit/MSFT_xWaitForADDomain.Tests.ps1 +++ b/Tests/Unit/MSFT_xWaitForADDomain.Tests.ps1 @@ -1,3 +1,6 @@ +[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingConvertToSecureStringWithPlainText', '')] +param() + $Global:DSCModuleName = 'xActiveDirectory' $Global:DSCResourceName = 'MSFT_xWaitForADDomain' @@ -16,7 +19,7 @@ Import-Module (Join-Path -Path $moduleRoot -ChildPath 'DSCResource.Tests\TestHel $TestEnvironment = Initialize-TestEnvironment ` -DSCModuleName $Global:DSCModuleName ` -DSCResourceName $Global:DSCResourceName ` - -TestType Unit + -TestType Unit #endregion # Begin Testing @@ -39,7 +42,7 @@ try RetryIntervalSec = 10 RetryCount = 5 } - + $rebootTestParams = @{ DomainName = $domainName DomainUserCredential = $DomainUserCredential @@ -47,7 +50,7 @@ try RetryCount = 5 RebootRetryCount = 3 } - + $fakeDomainObject = @{Name = $domainName} #endregion @@ -70,7 +73,7 @@ try Mock -CommandName Get-Domain -MockWith {} $targetResource = Get-TargetResource @testParams $targetResource.DomainName | Should Be $null - } + } } #endregion @@ -99,15 +102,15 @@ try #region Function Set-TargetResource Describe "$($Global:DSCResourceName)\Set-TargetResource" { BeforeEach{ - $global:DSCMachineStatus = $null + $global:DSCMachineStatus = $null } - + It "Doesn't throw exception and doesn't call Start-Sleep, Clear-DnsClientCache or set `$global:DSCMachineStatus when domain found" { Mock -CommandName Get-Domain -MockWith {return $fakeDomainObject} Mock -CommandName Start-Sleep -MockWith {} Mock -CommandName Clear-DnsClientCache -MockWith {} {Set-TargetResource @testParams} | Should Not Throw - $global:DSCMachineStatus | should not be 1 + $global:DSCMachineStatus | should not be 1 Assert-MockCalled -CommandName Start-Sleep -Times 0 -Scope It Assert-MockCalled -CommandName Clear-DnsClientCache -Times 0 -Scope It } @@ -115,9 +118,9 @@ try It "Throws exception and does not set `$global:DSCMachineStatus when domain not found after $($testParams.RetryCount) retries when RebootRetryCount is not set" { Mock -CommandName Get-Domain -MockWith {} {Set-TargetResource @testParams} | Should Throw - $global:DSCMachineStatus | should not be 1 + $global:DSCMachineStatus | should not be 1 } - + It "Throws exception when domain not found after $($rebootTestParams.RebootRetryCount) reboot retries when RebootRetryCount is exceeded" { Mock -CommandName Get-Domain -MockWith {} Mock -CommandName Get-Content -MockWith {return $rebootTestParams.RebootRetryCount} @@ -131,14 +134,14 @@ try {Set-TargetResource @rebootTestParams} | Should Not Throw Assert-MockCalled -CommandName Set-Content -Times 1 -Exactly -Scope It } - + It "Sets `$global:DSCMachineStatus = 1 and does not throw an exception if the domain is not found and RebootRetryCount is not exceeded" { Mock -CommandName Get-Domain -MockWith {} Mock -CommandName Get-Content -MockWith {return 0} {Set-TargetResource @rebootTestParams} | Should Not Throw - $global:DSCMachineStatus | should be 1 + $global:DSCMachineStatus | should be 1 } - + It "Calls Get-Domain exactly $($testParams.RetryCount) times when domain not found" { Mock -CommandName Get-Domain -MockWith {} Mock -CommandName Start-Sleep -MockWith {} From 51bb913bf4213cef44973d96ba9e5169265121e0 Mon Sep 17 00:00:00 2001 From: Daniel Scott-Raynsford Date: Sun, 19 Jun 2016 10:01:12 +1200 Subject: [PATCH 6/9] Change parameter name to RequestFile --- DSCResources/MSFT_xADComputer/MSFT_xADComputer.psm1 | 12 ++++++------ .../MSFT_xADComputer/MSFT_xADComputer.schema.mof | 2 +- README.md | 10 +++++----- Tests/Unit/MSFT_xADComputer.Tests.ps1 | 4 ++-- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/DSCResources/MSFT_xADComputer/MSFT_xADComputer.psm1 b/DSCResources/MSFT_xADComputer/MSFT_xADComputer.psm1 index 08ae48668..c28cc143d 100644 --- a/DSCResources/MSFT_xADComputer/MSFT_xADComputer.psm1 +++ b/DSCResources/MSFT_xADComputer/MSFT_xADComputer.psm1 @@ -69,7 +69,7 @@ function Get-TargetResource [System.String] $Manager, [ValidateNotNull()] - [System.String] $ODJRequestFile, + [System.String] $RequestFile, [ValidateNotNull()] [System.Boolean] $Enabled = $true, @@ -128,7 +128,7 @@ function Get-TargetResource SID = $adComputer.SID; ## Read-only property Ensure = $Ensure; DomainController = $DomainController; - ODJRequestFile = $ODJRequestFile; + RequestFile = $RequestFile; } ## Retrieve each property from the ADPropertyMap and add to the hashtable @@ -204,7 +204,7 @@ function Test-TargetResource [System.String] $Manager, [ValidateNotNull()] - [System.String] $ODJRequestFile, + [System.String] $RequestFile, [ValidateNotNull()] [System.Boolean] $Enabled = $true, @@ -324,7 +324,7 @@ function Set-TargetResource [System.String] $Manager, [ValidateNotNull()] - [System.String] $ODJRequestFile, + [System.String] $RequestFile, [ValidateNotNull()] [System.Boolean] $Enabled = $true, @@ -349,7 +349,7 @@ function Set-TargetResource { if ($targetResource.Ensure -eq 'Absent') { ## Computer does not exist and needs creating - if ($ODJRequestFile) + if ($RequestFile) { ## Use DJOIN to create the computer account as well as the ODJ Request file. Write-Verbose -Message ($LocalizedData.ODJRequestStartMessage -f ` @@ -371,7 +371,7 @@ function Set-TargetResource $DJoinParameters += @( '/DCNAME',$DomainController ) } # if - $DJoinParameters += @( '/SAVEFILE',$ODJRequestFile ) + $DJoinParameters += @( '/SAVEFILE',$RequestFile ) $Result = & djoin.exe @DjoinParameters if ($LASTEXITCODE -ne 0) diff --git a/DSCResources/MSFT_xADComputer/MSFT_xADComputer.schema.mof b/DSCResources/MSFT_xADComputer/MSFT_xADComputer.schema.mof index 1ed030629..d2fdfce7d 100644 --- a/DSCResources/MSFT_xADComputer/MSFT_xADComputer.schema.mof +++ b/DSCResources/MSFT_xADComputer/MSFT_xADComputer.schema.mof @@ -13,7 +13,7 @@ class MSFT_xADComputer : OMI_BaseResource [Write, Description("Specifies the user or group Distinguished Name that manages the computer object")] String Manager; [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 DomainAdministratorCredential; - [Write, Description("Specifies the full path to the Offline Domain Join Request file to create.")] String ODJRequestFile; + [Write, Description("Specifies the full path to the Offline Domain Join Request file to create.")] String RequestFile; [Write, ValueMap{"Present", "Absent"},Values{"Present", "Absent"}] String Ensure; [Read, Description("Returns the X.500 path of the computer object")] String DistinguishedName; [Read, Description("Returns the security identifier of the computer object")] String SID; diff --git a/README.md b/README.md index 53254976f..060e5cb74 100644 --- a/README.md +++ b/README.md @@ -221,7 +221,7 @@ The xADComputer DSC resource will manage computer accounts within Active Directo * Valid values are the user's or group's DistinguishedName, ObjectGUID, SID or SamAccountName. * **DomainController**: Specifies the Active Directory Domain Services instance to connect to perform the task (optional). * **DomainAdministratorCredential**: Specifies the user account credentials to use to perform the task (optional). -* **ODJRequestFile**: Specifies the full path to the Offline Domain Join Request file to create (optional). +* **RequestFile**: Specifies the full path to the Offline Domain Join Request file to create (optional). * **Ensure**: Specifies whether the computer account is present or absent. * Valid values are 'Present' and 'Absent'. * It not specified, it defaults to 'Present'. @@ -236,7 +236,7 @@ Setting an ODJ Request file path for a configuration that creates a computer acc ### Unreleased * xADDomainController: Customer identified two cases of incorrect variables being called in Verbose output messages. Corrected. * xADComputer: New resource added. -* xADComputer: Added ODJRequestFile support. +* xADComputer: Added RequestFile support. * Fixed PSScriptAnalyzer Errors with v1.6.0. ### 2.11.0.0 @@ -1105,7 +1105,7 @@ configuration Example_xADComputerAccountODJ [parameter(Mandatory = $true)] [System.String] - $ODJRequestFile + $RequestFile ) Import-DscResource -Module xActiveDirectory @@ -1118,7 +1118,7 @@ configuration Example_xADComputerAccountODJ DomainAdministratorCredential = $DomainCredential ComputerName = $ComputerName Path = $Path - ODJRequestFile = $ODJRequestFile + RequestFile = $RequestFile } } } @@ -1127,7 +1127,7 @@ Example_xADComputerAccountODJ -DomainController 'DC01' ` -DomainCredential (Get-Credential -Message "Domain Credentials") ` -ComputerName 'NANO-200' ` -Path 'ou=Nano,dc=example,dc=com' ` - -ODJRequestFile 'd:\ODJFiles\NANO-200.txt' ` + -RequestFile 'd:\ODJFiles\NANO-200.txt' ` -ConfigurationData $ConfigurationData Start-DscConfiguration -Path .\Example_xADComputerAccount -Wait -Verbose diff --git a/Tests/Unit/MSFT_xADComputer.Tests.ps1 b/Tests/Unit/MSFT_xADComputer.Tests.ps1 index a553f8552..b7eae71ef 100644 --- a/Tests/Unit/MSFT_xADComputer.Tests.ps1 +++ b/Tests/Unit/MSFT_xADComputer.Tests.ps1 @@ -390,13 +390,13 @@ try Assert-MockCalled New-ADComputer -ParameterFilter { $Name -eq $newComputerName } -Scope It; } - It "Calls 'New-ADComputer' when 'Ensure' is 'Present' and the account does not exist, ODJRequestFile is set, DJOIN OK" { + It "Calls 'New-ADComputer' when 'Ensure' is 'Present' and the account does not exist, RequestFile is set, DJOIN OK" { $newComputerName = 'NEWCOMPUTER' $newAbsentParams = $testAbsentParams.Clone(); $newAbsentParams['ComputerName'] = $newComputerName; $newPresentParams = $testPresentParams.Clone(); $newPresentParams['ComputerName'] = $newComputerName; - $newPresentParams['ODJRequestFile'] = 'c:\ODJTest.txt'; + $newPresentParams['RequestFile'] = 'c:\ODJTest.txt'; Mock New-ADComputer -ParameterFilter { $Name -eq $newComputerName } -MockWith { } Mock djoin.exe -MockWith { $LASTEXITCODE = 0; 'OK' } Mock Set-ADComputer { } From eae79f86c6b6fb4bf1d39869208b7cfeb4e0b999 Mon Sep 17 00:00:00 2001 From: Daniel Scott-Raynsford Date: Sat, 2 Jul 2016 12:16:03 +1200 Subject: [PATCH 7/9] Moved Assert_*.ps1 files to Tests folder. --- README.md | 1 + Assert-HADC.ps1 => Tests/Assert-HADC.ps1 | 0 .../Assert-ParentChildDomains.ps1 | 0 3 files changed, 1 insertion(+) rename Assert-HADC.ps1 => Tests/Assert-HADC.ps1 (100%) rename Assert-ParentChildDomains.ps1 => Tests/Assert-ParentChildDomains.ps1 (100%) diff --git a/README.md b/README.md index 060e5cb74..272a5b5f7 100644 --- a/README.md +++ b/README.md @@ -238,6 +238,7 @@ Setting an ODJ Request file path for a configuration that creates a computer acc * xADComputer: New resource added. * xADComputer: Added RequestFile support. * Fixed PSScriptAnalyzer Errors with v1.6.0. +* Moved Assert_*.ps1 files to Tests folder. ### 2.11.0.0 * xWaitForADDomain: Made explicit credentials optional and other various updates diff --git a/Assert-HADC.ps1 b/Tests/Assert-HADC.ps1 similarity index 100% rename from Assert-HADC.ps1 rename to Tests/Assert-HADC.ps1 diff --git a/Assert-ParentChildDomains.ps1 b/Tests/Assert-ParentChildDomains.ps1 similarity index 100% rename from Assert-ParentChildDomains.ps1 rename to Tests/Assert-ParentChildDomains.ps1 From f6996273f9e54001aa4c7a2c1e572ca0bd6a870c Mon Sep 17 00:00:00 2001 From: Daniel Scott-Raynsford Date: Sat, 2 Jul 2016 12:22:29 +1200 Subject: [PATCH 8/9] Moved Assert files back --- Tests/Assert-HADC.ps1 => Assert-HADC.ps1 | 0 ...Assert-ParentChildDomains.ps1 => Assert-ParentChildDomains.ps1 | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename Tests/Assert-HADC.ps1 => Assert-HADC.ps1 (100%) rename Tests/Assert-ParentChildDomains.ps1 => Assert-ParentChildDomains.ps1 (100%) diff --git a/Tests/Assert-HADC.ps1 b/Assert-HADC.ps1 similarity index 100% rename from Tests/Assert-HADC.ps1 rename to Assert-HADC.ps1 diff --git a/Tests/Assert-ParentChildDomains.ps1 b/Assert-ParentChildDomains.ps1 similarity index 100% rename from Tests/Assert-ParentChildDomains.ps1 rename to Assert-ParentChildDomains.ps1 From 5ab03d2198e20b11084fe2de8bc5024ec2891156 Mon Sep 17 00:00:00 2001 From: Daniel Scott-Raynsford Date: Sat, 2 Jul 2016 12:22:35 +1200 Subject: [PATCH 9/9] Moved Assert files back --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 0a4ac2ca6..cb6726045 100644 --- a/README.md +++ b/README.md @@ -237,7 +237,6 @@ Setting an ODJ Request file path for a configuration that creates a computer acc ## Versions ### Unreleased -* Moved Assert_*.ps1 files to Tests folder. ### 2.12.0.0 * xADDomainController: Customer identified two cases of incorrect variables being called in Verbose output messages. Corrected.