Skip to content

Commit

Permalink
xADUser: Add ChangePasswordAtLogon Parameter (#371)
Browse files Browse the repository at this point in the history
- Changes to xADUser
  - Added ChangePasswordAtLogon property (issue #246).
  • Loading branch information
X-Guardian authored and johlju committed Jun 15, 2019
1 parent 33208e1 commit 5cd649a
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 21 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
49 changes: 49 additions & 0 deletions DSCResources/MSFT_xADUser/MSFT_xADUser.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -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'; }
)
Expand Down Expand Up @@ -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()]
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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()]
Expand Down Expand Up @@ -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()]
Expand Down Expand Up @@ -1282,6 +1310,16 @@ function Assert-Parameters
[System.Boolean]
$Enabled = $true,

[Parameter()]
[ValidateNotNull()]
[System.Boolean]
$ChangePasswordAtLogon,

[Parameter()]
[ValidateNotNull()]
[System.Boolean]
$PasswordNeverExpires,

[Parameter(ValueFromRemainingArguments)]
$IgnoredArguments
)
Expand All @@ -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.
Expand Down
1 change: 1 addition & 0 deletions DSCResources/MSFT_xADUser/MSFT_xADUser.schema.mof
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
35 changes: 18 additions & 17 deletions DSCResources/MSFT_xADUser/en-US/MSFT_xADUser.strings.psd1
Original file line number Diff line number Diff line change
@@ -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.
'@
37 changes: 33 additions & 4 deletions Tests/Unit/MSFT_xADUser.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -644,6 +668,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
}
Expand Down

0 comments on commit 5cd649a

Please sign in to comment.