Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

xADUser: Add ChangePasswordAtLogon Parameter #371

Merged
merged 4 commits into from
Jun 15, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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