From 6c359ac241416b0ba3ede7d676ef4ccf63928ae6 Mon Sep 17 00:00:00 2001 From: Simon Heather Date: Fri, 14 Jun 2019 00:11:35 +0100 Subject: [PATCH 1/3] xADUser: Add ChangePasswordAtLogon parameter --- DSCResources/MSFT_xADUser/MSFT_xADUser.psm1 | 49 +++++++++++++++++++ .../MSFT_xADUser/MSFT_xADUser.schema.mof | 1 + .../en-US/MSFT_xADUser.strings.psd1 | 35 ++++++------- Tests/Unit/MSFT_xADUser.Tests.ps1 | 7 ++- 4 files changed, 74 insertions(+), 18 deletions(-) diff --git a/DSCResources/MSFT_xADUser/MSFT_xADUser.psm1 b/DSCResources/MSFT_xADUser/MSFT_xADUser.psm1 index 6805ee1dc..b15ceb1d4 100644 --- a/DSCResources/MSFT_xADUser/MSFT_xADUser.psm1 +++ b/DSCResources/MSFT_xADUser/MSFT_xADUser.psm1 @@ -51,6 +51,7 @@ $adPropertyMap = @( @{ Parameter = 'Manager'; } @{ Parameter = 'PasswordNeverExpires'; UseCmdletParameter = $true; } @{ Parameter = 'CannotChangePassword'; UseCmdletParameter = $true; } + @{ Parameter = 'ChangePasswordAtLogon'; UseCmdletParameter = $true; } @{ Parameter = 'TrustedForDelegation'; UseCmdletParameter = $true; } @{ Parameter = 'ServicePrincipalNames'; } ) @@ -306,6 +307,12 @@ function Get-TargetResource [System.Boolean] $CannotChangePassword, + # Specifies whether the account password must be changed during the next logon attempt + [Parameter()] + [ValidateNotNull()] + [System.Boolean] + $ChangePasswordAtLogon, + # Specifies whether the password of an account can expire [Parameter()] [ValidateNotNull()] @@ -415,6 +422,15 @@ function Get-TargetResource elseif (($property.Parameter) -eq 'ServicePrincipalNames') { $targetResource['ServicePrincipalNames'] = [System.String[]]$adUser.ServicePrincipalNames } + elseif (($property.Parameter) -eq 'ChangePasswordAtLogon') { + if ($adUser.pwdlastset -eq 0) + { + $targetResource['ChangePasswordAtLogon'] = $true + } + else { + $targetResource['ChangePasswordAtLogon'] = $false + } + } elseif ($property.ADProperty) { # The AD property name is different to the function parameter to use this @@ -681,6 +697,12 @@ function Test-TargetResource [System.Boolean] $CannotChangePassword, + # Specifies whether the account password must be changed during the next logon attempt + [Parameter()] + [ValidateNotNull()] + [System.Boolean] + $ChangePasswordAtLogon, + # Specifies whether the password of an account can expire [Parameter()] [ValidateNotNull()] @@ -1055,6 +1077,12 @@ function Set-TargetResource [System.Boolean] $CannotChangePassword, + # Specifies whether the account password must be changed during the next logon attempt + [Parameter()] + [ValidateNotNull()] + [System.Boolean] + $ChangePasswordAtLogon, + # Specifies whether the password of an account can expire [Parameter()] [ValidateNotNull()] @@ -1282,6 +1310,16 @@ function Assert-Parameters [System.Boolean] $Enabled = $true, + [Parameter()] + [ValidateNotNull()] + [System.Boolean] + $ChangePasswordAtLogon, + + [Parameter()] + [ValidateNotNull()] + [System.Boolean] + $PasswordNeverExpires, + [Parameter(ValueFromRemainingArguments)] $IgnoredArguments ) @@ -1296,6 +1334,17 @@ function Assert-Parameters ThrowInvalidArgumentError @throwInvalidArgumentErrorParams; } + # ChangePasswordAtLogon cannot be set for an account that also has PasswordNeverExpires set + if ($PSBoundParameters.ContainsKey('ChangePasswordAtLogon') -and $PSBoundParameters['ChangePasswordAtLogon'] -eq $true -and + $PSBoundParameters.ContainsKey('PasswordNeverExpires') -and $PSBoundParameters['PasswordNeverExpires'] -eq $true) + { + $throwInvalidArgumentErrorParams = @{ + ErrorId = 'xADUser_ChangePasswordParameterConflict' + ErrorMessage = $script:localizedData.ChangePasswordParameterConflictError + } + ThrowInvalidArgumentError @throwInvalidArgumentErrorParams + } + } #end function Assert-Parameters # Internal function to test the validity of a user's password. diff --git a/DSCResources/MSFT_xADUser/MSFT_xADUser.schema.mof b/DSCResources/MSFT_xADUser/MSFT_xADUser.schema.mof index df8e2ac51..1d0f7aaab 100644 --- a/DSCResources/MSFT_xADUser/MSFT_xADUser.schema.mof +++ b/DSCResources/MSFT_xADUser/MSFT_xADUser.schema.mof @@ -42,6 +42,7 @@ class MSFT_xADUser : OMI_BaseResource [Write, Description("Specifies the user's manager specified as a Distinguished Name (ldapDisplayName 'manager')")] String Manager; [Write, Description("Specifies if the account is enabled (default True)")] Boolean Enabled; [Write, Description("Specifies whether the account password can be changed")] Boolean CannotChangePassword; + [Write, Description("Specifies whether the account password must be changed during the next logon attempt. This cannot be set to true if the PasswordNeverExpires property is also set to true")] Boolean ChangePasswordAtLogon; [Write, Description("Specifies whether the password of an account can expire")] Boolean PasswordNeverExpires; [Write, Description("Specifies the Active Directory Domain Services instance to use to perform the task.")] String DomainController; [Write, Description("Specifies the user account credentials to use to perform this task"), EmbeddedInstance("MSFT_Credential")] String DomainAdministratorCredential; diff --git a/DSCResources/MSFT_xADUser/en-US/MSFT_xADUser.strings.psd1 b/DSCResources/MSFT_xADUser/en-US/MSFT_xADUser.strings.psd1 index 6ff12c8cd..6a1c2b9ae 100644 --- a/DSCResources/MSFT_xADUser/en-US/MSFT_xADUser.strings.psd1 +++ b/DSCResources/MSFT_xADUser/en-US/MSFT_xADUser.strings.psd1 @@ -1,20 +1,21 @@ # culture="en-US" ConvertFrom-StringData @' - 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}'. - SettingADUserPassword = Setting Active Directory user password. - UpdatingADUserProperty = Updating user property '{0}' with/to '{1}'. - RemovingADUserProperty = Removing user property '{0}' with '{1}'. - MovingADUser = Moving user from '{0}' to '{1}'. - RenamingADUser = Renaming user from '{0}' to '{1}'. - RestoringUser = Attempting to restore the user object {0} from the recycle bin. + RetrievingADUserError = Error looking up Active Directory user '{0}' ({0}@{1}). + PasswordParameterConflictError = Parameter '{0}' cannot be set to '{1}' when the '{2}' parameter is specified. + ChangePasswordParameterConflictError = Parameter 'ChangePasswordAtLogon' cannot be set to 'true' when Parameter 'PasswordNeverExpires' is also set to 'true'. + 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}'. + SettingADUserPassword = Setting Active Directory user password. + UpdatingADUserProperty = Updating user property '{0}' with/to '{1}'. + RemovingADUserProperty = Removing user property '{0}' with '{1}'. + MovingADUser = Moving user from '{0}' to '{1}'. + RenamingADUser = Renaming user from '{0}' to '{1}'. + RestoringUser = Attempting to restore the user object {0} from the recycle bin. '@ diff --git a/Tests/Unit/MSFT_xADUser.Tests.ps1 b/Tests/Unit/MSFT_xADUser.Tests.ps1 index 6953b0343..f801cbd84 100644 --- a/Tests/Unit/MSFT_xADUser.Tests.ps1 +++ b/Tests/Unit/MSFT_xADUser.Tests.ps1 @@ -65,7 +65,7 @@ try 'EmailAddress', 'EmployeeID', 'EmployeeNumber', 'HomeDirectory', 'HomeDrive', 'HomePage', 'ProfilePath', 'LogonScript', 'Notes', 'OfficePhone', 'MobilePhone', 'Fax', 'Pager', 'IPPhone', 'HomePhone', 'CommonName' ) - $testBooleanProperties = @('PasswordNeverExpires', 'CannotChangePassword', 'TrustedForDelegation', 'Enabled'); + $testBooleanProperties = @('PasswordNeverExpires', 'CannotChangePassword', 'ChangePasswordAtLogon', 'TrustedForDelegation', 'Enabled'); $testArrayProperties = @('ServicePrincipalNames') #region Function Get-TargetResource @@ -644,6 +644,11 @@ try It "Does not throw when 'TrustedForDelegation' is specified" { { Assert-Parameters -TrustedForDelegation $true } | Should Not Throw } + + It "Should throw the correct error when 'PasswordNeverExpires' and 'ChangePasswordAtLogon' are specified" { + { Assert-Parameters -PasswordNeverExpires $true -ChangePasswordAtLogon $true } | ` + Should -Throw $script:localizedData.ChangePasswordParameterConflictError + } } #endregion } From 5e635474625c389174f90dd85e87951d1ecf7347 Mon Sep 17 00:00:00 2001 From: Simon Heather Date: Fri, 14 Jun 2019 00:35:52 +0100 Subject: [PATCH 2/3] Add boolean property = false tests --- Tests/Unit/MSFT_xADUser.Tests.ps1 | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/Tests/Unit/MSFT_xADUser.Tests.ps1 b/Tests/Unit/MSFT_xADUser.Tests.ps1 index f801cbd84..4368e6f0e 100644 --- a/Tests/Unit/MSFT_xADUser.Tests.ps1 +++ b/Tests/Unit/MSFT_xADUser.Tests.ps1 @@ -269,12 +269,11 @@ try foreach ($testParameter in $testBooleanProperties) { - It "Passes when user account '$testParameter' matches AD account property" { + It "Should Pass when user account '$testParameter' is true and matches AD account property" { $testParameterValue = $true $testValidPresentParams = $testPresentParams.Clone() $testValidPresentParams[$testParameter] = $testParameterValue $validADUser = $testPresentParams.Clone() - $invalidADUser = $testPresentParams.Clone() Mock -CommandName Get-TargetResource -MockWith { $validADUser[$testParameter] = $testParameterValue return $validADUser @@ -283,11 +282,36 @@ try Test-TargetResource @testValidPresentParams | Should Be $true } - It "Fails when user account '$testParameter' does not match AD account property value" { + It "Should fail when user account '$testParameter' is true and does not match AD account property value" { $testParameterValue = $true $testValidPresentParams = $testPresentParams.Clone() $testValidPresentParams[$testParameter] = $testParameterValue + $invalidADUser = $testPresentParams.Clone() + Mock -CommandName Get-TargetResource -MockWith { + $invalidADUser[$testParameter] = -not $testParameterValue + return $invalidADUser + } + + Test-TargetResource @testValidPresentParams | Should Be $false + } + + It "Should pass when user account '$testParameter' is false matches AD account property" { + $testParameterValue = $false + $testValidPresentParams = $testPresentParams.Clone() + $testValidPresentParams[$testParameter] = $testParameterValue $validADUser = $testPresentParams.Clone() + Mock -CommandName Get-TargetResource -MockWith { + $validADUser[$testParameter] = $testParameterValue + return $validADUser + } + + Test-TargetResource @testValidPresentParams | Should Be $true + } + + It "Should fail when user account '$testParameter' is false and does not match AD account property value" { + $testParameterValue = $false + $testValidPresentParams = $testPresentParams.Clone() + $testValidPresentParams[$testParameter] = $testParameterValue $invalidADUser = $testPresentParams.Clone() Mock -CommandName Get-TargetResource -MockWith { $invalidADUser[$testParameter] = -not $testParameterValue From f10e5b4b1e7b578fc7eab2b9ab62e32457ee2088 Mon Sep 17 00:00:00 2001 From: Simon Heather Date: Fri, 14 Jun 2019 01:01:10 +0100 Subject: [PATCH 3/3] Update CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b9a4870c3..2ff6df571 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -56,6 +56,7 @@ - Changes to xADUser - Change the description of the property RestoreFromRecycleBin. - Added ServicePrincipalNames property ([issue #153](https://github.com/PowerShell/xActiveDirectory/issues/153)). + - Added ChangePasswordAtLogon property ([issue #246](https://github.com/PowerShell/xActiveDirectory/issues/246)). - Changes to xADDomainController - Change the `#Requires` statement in the Examples to require the correct module.