SharedResources/Src/InstallPrimaryHeadNode/xPSDesiredStateConfiguration/DSCResources/MSFT_xUserResource/MSFT_xUserResource.psm1 (970 lines of code) (raw):
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingUserNameAndPassWordParams", "")] # To be removed when username/password changed to a credential
param ()
# A global variable that contains localized messages.
data LocalizedData
{
# culture="en-US"
ConvertFrom-StringData @'
UserWithName=User: {0}
RemoveOperation=Remove
AddOperation=Add
SetOperation=Set
ConfigurationStarted=Configuration of user {0} started.
ConfigurationCompleted=Configuration of user {0} completed successfully.
UserCreated=User {0} created successfully.
UserUpdated=User {0} properties updated successfully.
UserRemoved=User {0} removed successfully.
NoConfigurationRequired=User {0} exists on this node with the desired properties. No action required.
NoConfigurationRequiredUserDoesNotExist=User {0} does not exist on this node. No action required.
InvalidUserName=The name {0} cannot be used. Names may not consist entirely of periods and/or spaces, or contain these characters: {1}
UserExists=A user with the name {0} exists.
UserDoesNotExist=A user with the name {0} does not exist.
PropertyMismatch=The value of the {0} property is expected to be {1} but it is {2}.
PasswordPropertyMismatch=The value of the {0} property does not match.
AllUserPropertisMatch=All {0} {1} properties match.
ConnectionError = There could be a possible connection error while trying to use the System.DirectoryServices API's.
MultipleMatches = There could be a possible multiple matches exception while trying to use the System.DirectoryServices API's.
'@
}
# Commented-out until more languages are supported
# Import-LocalizedData LocalizedData -FileName MSFT_xUserResource.strings.psd1
Import-Module "$PSScriptRoot\..\CommonResourceHelper.psm1"
if (-not (Test-IsNanoServer))
{
Add-Type -AssemblyName 'System.DirectoryServices.AccountManagement'
}
<#
.SYNOPSIS
The Get-TargetResource cmdlet.
#>
function Get-TargetResource
{
[OutputType([Hashtable])]
param
(
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[System.String]
$UserName
)
if (Test-IsNanoServer)
{
Get-TargetResourceOnNanoServer @PSBoundParameters
}
else
{
Get-TargetResourceOnFullSKU @PSBoundParameters
}
}
<#
.SYNOPSIS
The Set-TargetResource cmdlet.
#>
function Set-TargetResource
{
[CmdletBInding(SupportsShouldProcess = $true)]
param
(
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[System.String]
$UserName,
[ValidateSet("Present", "Absent")]
[System.String]
$Ensure = "Present",
[System.String]
$FullName,
[System.String]
$Description,
[ValidateNotNullOrEmpty()]
[System.Management.Automation.PSCredential]
$Password,
[System.Boolean]
$Disabled,
[System.Boolean]
$PasswordNeverExpires,
[System.Boolean]
$PasswordChangeRequired,
[System.Boolean]
$PasswordChangeNotAllowed
)
if (Test-IsNanoServer)
{
Set-TargetResourceOnNanoServer @PSBoundParameters
}
else
{
Set-TargetResourceOnFullSKU @PSBoundParameters
}
}
<#
.SYNOPSIS
The Test-TargetResource cmdlet is used to validate if the resource is in a state as expected in the instance document.
#>
function Test-TargetResource
{
[OutputType([Boolean])]
param
(
[parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[System.String]
$UserName,
[ValidateSet("Present", "Absent")]
[System.String]
$Ensure = "Present",
[System.String]
$FullName,
[System.String]
$Description,
[ValidateNotNullOrEmpty()]
[System.Management.Automation.PSCredential]
$Password,
[System.Boolean]
$Disabled,
[System.Boolean]
$PasswordNeverExpires,
[System.Boolean]
$PasswordChangeRequired,
[System.Boolean]
$PasswordChangeNotAllowed
)
if (Test-IsNanoServer)
{
Test-TargetResourceOnNanoServer @PSBoundParameters
}
else
{
Test-TargetResourceOnFullSKU @PSBoundParameters
}
}
<#
.SYNOPSIS
The Get-TargetResource cmdlet on a full server.
#>
function Get-TargetResourceOnFullSKU
{
param
(
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[System.String]
$UserName
)
Set-StrictMode -Version Latest
ValidateUserName -UserName $UserName
# Try to find a user by a name.
$principalContext = New-Object System.DirectoryServices.AccountManagement.PrincipalContext -ArgumentList ([System.DirectoryServices.AccountManagement.ContextType]::Machine)
try
{
$user = [System.DirectoryServices.AccountManagement.UserPrincipal]::FindByIdentity($principalContext, $UserName);
if($user -ne $null)
{
# The user is found. Return all user properties and Ensure="Present".
$returnValue = @{
UserName = $user.Name;
Ensure = "Present";
FullName = $user.DisplayName;
Description = $user.Description;
Disabled = -not $user.Enabled;
PasswordNeverExpires = $user.PasswordNeverExpires;
PasswordChangeRequired = $null;
PasswordChangeNotAllowed = $user.UserCannotChangePassword;
}
return $returnValue;
}
# The user is not found. Return Ensure=Absent.
return @{
UserName = $UserName;
Ensure = "Absent";
}
}
catch
{
ThrowExceptionDueToDirectoryServicesError -ErrorId "MultipleMatches" -ErrorMessage ($LocalizedData.MultipleMatches + $_)
}
finally
{
if($user -ne $null)
{
$user.Dispose();
}
$principalContext.Dispose();
}
}
<#
.SYNOPSIS
The Set-TargetResource cmdlet on a full server.
#>
function Set-TargetResourceOnFullSKU
{
[CmdletBinding(SupportsShouldProcess = $true)]
param
(
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[System.String]
$UserName,
[ValidateSet("Present", "Absent")]
[System.String]
$Ensure = "Present",
[System.String]
$FullName,
[System.String]
$Description,
[ValidateNotNullOrEmpty()]
[System.Management.Automation.PSCredential]
$Password,
[System.Boolean]
$Disabled,
[System.Boolean]
$PasswordNeverExpires,
[System.Boolean]
$PasswordChangeRequired,
[System.Boolean]
$PasswordChangeNotAllowed
)
Set-StrictMode -Version Latest
Write-Verbose -Message ($LocalizedData.ConfigurationStarted -f $UserName)
ValidateUserName -UserName $UserName
# Try to find a user by a name.
$principalContext = New-Object System.DirectoryServices.AccountManagement.PrincipalContext -ArgumentList ([System.DirectoryServices.AccountManagement.ContextType]::Machine)
try
{
$user = [System.DirectoryServices.AccountManagement.UserPrincipal]::FindByIdentity($principalContext, $UserName);
if($Ensure -eq "Present")
{
# Ensure is set to "Present".
$whatIfShouldProcess = $true;
$userExists = $false;
$saveChanges = $false;
if($user -eq $null)
{
# A user does not exist. Check WhatIf for adding a user.
$whatIfShouldProcess = $pscmdlet.ShouldProcess($LocalizedData.UserWithName -f $UserName, $LocalizedData.AddOperation);
}
else
{
# A user exists.
$userExists = $true;
# Check WhatIf for setting a user.
$whatIfShouldProcess = $pscmdlet.ShouldProcess($LocalizedData.UserWithName -f $UserName, $LocalizedData.SetOperation);
}
if($whatIfShouldProcess)
{
if(-not $userExists)
{
# The user with the provided name does not exist. Add a new user.
$user = New-Object System.DirectoryServices.AccountManagement.UserPrincipal -ArgumentList $principalContext
$user.Name = $UserName;
$saveChanges = $true;
}
# Set user properties.
if($PSBoundParameters.ContainsKey('FullName') -and (-not $userExists -or $FullName -ne $user.DisplayName))
{
$user.DisplayName = $FullName;
$saveChanges = $true;
}
else
{
if(-not $userExists)
{
# For a newly created user, set the DisplayName property to an empty string. By default DisplayName is set to user's name.
$user.DisplayName = [String]::Empty;
}
}
if($PSBoundParameters.ContainsKey('Description') -and (-not $userExists -or $Description -ne $user.Description))
{
$user.Description = $Description;
$saveChanges = $true;
}
# Password. Set the password regardless of the state of the user.
if($PSBoundParameters.ContainsKey('Password'))
{
$user.SetPassword($Password.GetNetworkCredential().Password);
$saveChanges = $true;
}
if($PSBoundParameters.ContainsKey('Disabled') -and (-not $userExists -or $Disabled -eq $user.Enabled))
{
$user.Enabled = -not $Disabled;
$saveChanges = $true;
}
if($PSBoundParameters.ContainsKey('PasswordNeverExpires') -and (-not $userExists -or $PasswordNeverExpires -ne $user.PasswordNeverExpires))
{
$user.PasswordNeverExpires = $PasswordNeverExpires;
$saveChanges = $true;
}
if($PSBoundParameters.ContainsKey('PasswordChangeRequired'))
{
if($PasswordChangeRequired)
{
# Expire the password. This will force the user to change the password at the next logon.
$user.ExpirePasswordNow();
$saveChanges = $true;
}
}
if($PSBoundParameters.ContainsKey('PasswordChangeNotAllowed') -and (-not $userExists -or $PasswordChangeNotAllowed -ne $user.UserCannotChangePassword))
{
$user.UserCannotChangePassword = $PasswordChangeNotAllowed;
$saveChanges = $true;
}
if($saveChanges)
{
$user.Save();
# Send an operation success verbose message.
if($userExists)
{
Write-Verbose -Message ($LocalizedData.UserUpdated -f $UserName)
}
else
{
Write-Verbose -Message ($LocalizedData.UserCreated -f $UserName)
}
}
else
{
Write-Verbose -Message ($LocalizedData.NoConfigurationRequired -f $UserName)
}
}
}
else
{
# Ensure is set to "Absent".
if($user -ne $null)
{
# The user exists.
if($pscmdlet.ShouldProcess($LocalizedData.UserWithName -f $UserName, $LocalizedData.RemoveOperation))
{
# Remove the user by the provided name.
$user.Delete();
}
Write-Verbose -Message ($LocalizedData.UserRemoved -f $UserName)
}
else
{
Write-Verbose -Message ($LocalizedData.NoConfigurationRequiredUserDoesNotExist -f $UserName)
}
}
}
catch
{
ThrowExceptionDueToDirectoryServicesError -ErrorId "MultipleMatches" -ErrorMessage ($LocalizedData.MultipleMatches + $_)
}
finally
{
if($user -ne $null)
{
$user.Dispose();
}
$principalContext.Dispose();
}
Write-Verbose -Message ($LocalizedData.ConfigurationCompleted -f $UserName)
}
<#
.SYNOPSIS
The Test-TargetResource cmdlet on a full server.
#>
function Test-TargetResourceOnFullSKU
{
param
(
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[System.String]
$UserName,
[ValidateSet("Present", "Absent")]
[System.String]
$Ensure = "Present",
[System.String]
$FullName,
[System.String]
$Description,
[ValidateNotNullOrEmpty()]
[System.Management.Automation.PSCredential]
$Password,
[System.Boolean]
$Disabled,
[System.Boolean]
$PasswordNeverExpires,
[System.Boolean]
$PasswordChangeRequired,
[System.Boolean]
$PasswordChangeNotAllowed
)
Set-StrictMode -Version Latest
ValidateUserName -UserName $UserName
# Try to find a user by a name.
$principalContext = New-Object System.DirectoryServices.AccountManagement.PrincipalContext -ArgumentList ([System.DirectoryServices.AccountManagement.ContextType]::Machine)
try
{
$user = [System.DirectoryServices.AccountManagement.UserPrincipal]::FindByIdentity($principalContext, $UserName);
if($user -eq $null)
{
# A user with the provided name does not exist.
Write-Log -Message ($LocalizedData.UserDoesNotExist -f $UserName)
if($Ensure -eq "Absent")
{
return $true;
}
else
{
return $false;
}
}
# A user with the provided name exists.
Write-Log -Message ($LocalizedData.UserExists -f $UserName)
# Validate separate properties.
if($Ensure -eq "Absent")
{
Write-Log -Message ($LocalizedData.PropertyMismatch -f "Ensure", "Absent", "Present")
return $false; # The Ensure property does not match. Return $false;
}
if($PSBoundParameters.ContainsKey('FullName') -and $FullName -ne $user.DisplayName)
{
Write-Log -Message ($LocalizedData.PropertyMismatch -f "FullName", $FullName, $user.DisplayName)
return $false; # The FullName property does not match. Return $false;
}
if($PSBoundParameters.ContainsKey('Description') -and $Description -ne $user.Description)
{
Write-Log -Message ($LocalizedData.PropertyMismatch -f "Description", $Description, $user.Description)
return $false; # The Description property does not match. Return $false;
}
# Password
if($PSBoundParameters.ContainsKey('Password'))
{
if(-not $principalContext.ValidateCredentials($UserName, $Password.GetNetworkCredential().Password))
{
Write-Log -Message ($LocalizedData.PasswordPropertyMismatch -f "Password")
return $false; # The Password property does not match. Return $false;
}
}
if($PSBoundParameters.ContainsKey('Disabled') -and $Disabled -eq $user.Enabled)
{
Write-Log -Message ($LocalizedData.PropertyMismatch -f "Disabled", $Disabled, $user.Enabled)
return $false; # The Disabled property does not match. Return $false;
}
if($PSBoundParameters.ContainsKey('PasswordNeverExpires') -and $PasswordNeverExpires -ne $user.PasswordNeverExpires)
{
Write-Log -Message ($LocalizedData.PropertyMismatch -f "PasswordNeverExpires", $PasswordNeverExpires, $user.PasswordNeverExpires)
return $false; # The PasswordNeverExpires property does not match. Return $false;
}
if($PSBoundParameters.ContainsKey('PasswordChangeNotAllowed') -and $PasswordChangeNotAllowed -ne $user.UserCannotChangePassword)
{
Write-Log -Message ($LocalizedData.PropertyMismatch -f "PasswordChangeNotAllowed", $PasswordChangeNotAllowed, $user.UserCannotChangePassword)
return $false; # The PasswordChangeNotAllowed property does not match. Return $false;
}
}
catch
{
ThrowExceptionDueToDirectoryServicesError -ErrorId "ConnectionError" -ErrorMessage ($LocalizedData.ConnectionError + $_)
}
finally
{
if($user -ne $null)
{
$user.Dispose();
}
$principalContext.Dispose();
}
# All properties match. Return $true.
Write-Log -Message ($LocalizedData.AllUserPropertisMatch -f "User", $UserName)
return $true;
}
<#
.Synopsys
The Get-TargetResource cmdlet.
#>
function Get-TargetResourceOnNanoServer
{
param
(
[parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[System.String]
$UserName
)
Set-StrictMode -Version Latest
ValidateUserName -UserName $UserName
# Try to find a user by a name.
try
{
[Microsoft.PowerShell.Commands.LocalUser] $user = Get-LocalUser -Name $UserName -ErrorAction Stop
}
catch [System.Exception]
{
if ($_.CategoryInfo.ToString().Contains('UserNotFoundException'))
{
# The user is not found. Return Ensure=Absent.
return @{
UserName = $UserName;
Ensure = "Absent";
}
}
Throw-TerminatingError -ErrorRecord $_
}
# The user is found. Return all user properties and Ensure="Present".
$returnValue = @{
UserName = $user.Name;
Ensure = "Present";
FullName = $user.FullName;
Description = $user.Description;
Disabled = -not $user.Enabled;
PasswordChangeRequired = $null;
PasswordChangeNotAllowed = -not $user.UserMayChangePassword;
}
if ($user.PasswordExpires)
{
$returnValue.Add('PasswordNeverExpires', $false)
}
else
{
$returnValue.Add('PasswordNeverExpires', $true)
}
return $returnValue;
}
<#
.SYNOPSIS
The Set-TargetResource cmdlet on a Nano server.
#>
function Set-TargetResourceOnNanoServer
{
[CmdletBinding(SupportsShouldProcess = $true)]
param
(
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[System.String]
$UserName,
[ValidateSet("Present", "Absent")]
[System.String]
$Ensure = "Present",
[System.String]
$FullName,
[System.String]
$Description,
[ValidateNotNullOrEmpty()]
[System.Management.Automation.PSCredential]
$Password,
[System.Boolean]
$Disabled,
[System.Boolean]
$PasswordNeverExpires,
[System.Boolean]
$PasswordChangeRequired,
[System.Boolean]
$PasswordChangeNotAllowed
)
Set-StrictMode -Version Latest
Write-Verbose -Message ($LocalizedData.ConfigurationStarted -f $UserName)
ValidateUserName -UserName $UserName
## Try to find a user by a name.
[bool] $userExists = $false
try
{
[Microsoft.PowerShell.Commands.LocalUser] $user = Get-LocalUser -Name $UserName -ErrorAction Stop
$userExists = $true;
}
catch [System.Exception]
{
if ($_.CategoryInfo.ToString().Contains('UserNotFoundException'))
{
# The user is not found.
Write-Log -Message ($LocalizedData.UserDoesNotExist -f $UserName)
}
else
{
Throw-TerminatingError -ErrorRecord $_
}
}
if($Ensure -eq "Present")
{
# Ensure is set to "Present".
if(-not $userExists)
{
# The user with the provided name does not exist. Add a new user.
New-LocalUser -Name $UserName -NoPassword
Write-Verbose -Message ($LocalizedData.UserCreated -f $UserName)
}
# Set user properties.
if($PSBoundParameters.ContainsKey('FullName'))
{
if (-not $userExists -or $FullName -ne $user.FullName)
{
if ($FullName -eq $null)
{
Set-LocalUser -Name $UserName -FullName ([String]::Empty)
}
else
{
Set-LocalUser -Name $UserName -FullName $FullName
}
}
}
else
{
if (-not $userExists)
{
# For a newly created user, set the DisplayName property to an empty string. By default DisplayName is set to user's name.
Set-LocalUser -Name $UserName -FullName ([String]::Empty)
}
}
if($PSBoundParameters.ContainsKey('Description') -and (-not $userExists -or $Description -ne $user.Description))
{
if ($Description -eq $null)
{
Set-LocalUser -Name $UserName -Description ([String]::Empty)
}
else
{
Set-LocalUser -Name $UserName -Description $Description
}
}
# Password. Set the password regardless of the state of the user.
if($PSBoundParameters.ContainsKey('Password'))
{
Set-LocalUser -Name $UserName -Password $Password.Password
}
if($PSBoundParameters.ContainsKey('Disabled') -and (-not $userExists -or $Disabled -eq $user.Enabled))
{
if ($Disabled)
{
Disable-LocalUser -Name $UserName
}
else
{
Enable-LocalUser -Name $UserName
}
}
$existingUserPasswordNeverExpires = (($userExists) -and ($user.PasswordExpires -eq $null))
if($PSBoundParameters.ContainsKey('PasswordNeverExpires') -and (-not $userExists -or ($PasswordNeverExpires -ne $existingUserPasswordNeverExpires)))
{
Set-LocalUser -Name $UserName -PasswordNeverExpires:$passwordNeverExpires
}
if($PSBoundParameters.ContainsKey('PasswordChangeRequired') -and ($PasswordChangeRequired))
{
Set-LocalUser -Name $UserName -PasswordChangeableDate ([datetime]::Now)
}
# NOTE: The parameter name and the property name have opposite meaning.
[bool] $expected = -not $PasswordChangeNotAllowed
[bool] $actual = $expected
if($userExists) {
$actual = $user.UserMayChangePassword
}
if($PSBoundParameters.ContainsKey('PasswordChangeNotAllowed') -and (-not $userExists -or $expected -ne $actual))
{
Set-LocalUser -Name $UserName -UserMayChangePassword $expected
}
}
else
{
# Ensure is set to "Absent".
if($userExists)
{
# The user exists.
Remove-LocalUser -Name $UserName
Write-Verbose -Message ($LocalizedData.UserRemoved -f $UserName)
}
else
{
Write-Verbose -Message ($LocalizedData.NoConfigurationRequiredUserDoesNotExist -f $UserName)
}
}
Write-Verbose -Message ($LocalizedData.ConfigurationCompleted -f $UserName)
}
<#
.SYNOPSIS
The Test-TargetResource cmdlet on a Nano server.
#>
function Test-TargetResourceOnNanoServer
{
param
(
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[System.String]
$UserName,
[ValidateSet("Present", "Absent")]
[System.String]
$Ensure = "Present",
[System.String]
$FullName,
[System.String]
$Description,
[ValidateNotNullOrEmpty()]
[System.Management.Automation.PSCredential]
$Password,
[System.Boolean]
$Disabled,
[System.Boolean]
$PasswordNeverExpires,
[System.Boolean]
$PasswordChangeRequired,
[System.Boolean]
$PasswordChangeNotAllowed
)
Set-StrictMode -Version Latest
ValidateUserName -UserName $UserName
# Try to find a user by a name.
try
{
[Microsoft.PowerShell.Commands.LocalUser] $user = Get-LocalUser -Name $UserName -ErrorAction Stop
}
catch [System.Exception]
{
if ($_.CategoryInfo.ToString().Contains('UserNotFoundException'))
{
# The user is not found. Return Ensure=Absent.
if($Ensure -eq "Absent")
{
return $true
}
else
{
return $false
}
}
Throw-TerminatingError -ErrorRecord $_
}
# A user with the provided name exists.
Write-Log -Message ($LocalizedData.UserExists -f $UserName)
# Validate separate properties.
if($Ensure -eq "Absent")
{
Write-Log -Message ($LocalizedData.PropertyMismatch -f "Ensure", "Absent", "Present")
return $false; # The Ensure property does not match. Return $false;
}
if($PSBoundParameters.ContainsKey('FullName') -and $FullName -ne $user.FullName)
{
Write-Log -Message ($LocalizedData.PropertyMismatch -f "FullName", $FullName, $user.FullName)
return $false; # The FullName property does not match. Return $false;
}
if($PSBoundParameters.ContainsKey('Description') -and $Description -ne $user.Description)
{
Write-Log -Message ($LocalizedData.PropertyMismatch -f "Description", $Description, $user.Description)
return $false; # The Description property does not match. Return $false;
}
if($PSBoundParameters.ContainsKey('Password'))
{
if(-not (ValidateCredentialsOnNanoServer -UserName $UserName -Password $Password.Password))
{
Write-Log -Message ($LocalizedData.PasswordPropertyMismatch -f "Password")
return $false; # The Password property does not match. Return $false;
}
}
if($PSBoundParameters.ContainsKey('Disabled') -and $Disabled -eq $user.Enabled)
{
Write-Log -Message ($LocalizedData.PropertyMismatch -f "Disabled", $Disabled, $user.Enabled)
return $false; # The Disabled property does not match. Return $false;
}
$existingUserPasswordNeverExpires = ($user.PasswordExpires -eq $null)
if($PSBoundParameters.ContainsKey('PasswordNeverExpires') -and $PasswordNeverExpires -ne $existingUserPasswordNeverExpires)
{
Write-Log -Message ($LocalizedData.PropertyMismatch -f "PasswordNeverExpires", $PasswordNeverExpires, $existingUserPasswordNeverExpires)
return $false; # The PasswordNeverExpires property does not match. Return $false;
}
if($PSBoundParameters.ContainsKey('PasswordChangeNotAllowed') -and $PasswordChangeNotAllowed -ne (-not $user.UserMayChangePassword))
{
Write-Log -Message ($LocalizedData.PropertyMismatch -f "PasswordChangeNotAllowed", $PasswordChangeNotAllowed, (-not $user.UserMayChangePassword))
return $false; # The PasswordChangeNotAllowed property does not match. Return $false;
}
# All properties match. Return $true.
Write-Log -Message ($LocalizedData.AllUserPropertisMatch -f "User", $UserName)
return $true;
}
<#
.SYNOPSIS
Validates the User name for invalid charecters.
#>
function ValidateUserName
{
param
(
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[System.String]
$UserName
)
# Check if the name consists of only periods and/or white spaces.
$wrongName = $true;
for($i = 0; $i -lt $UserName.Length; $i++)
{
if(-not [Char]::IsWhiteSpace($UserName, $i) -and $UserName[$i] -ne '.')
{
$wrongName = $false;
break;
}
}
$invalidChars = @('\','/','"','[',']',':','|','<','>','+','=',';',',','?','*','@')
if($wrongName)
{
ThrowInvalidArgumentError -ErrorId "UserNameHasOnlyWhiteSpacesAndDots" -ErrorMessage ($LocalizedData.InvalidUserName -f $UserName, [string]::Join(" ", $invalidChars))
}
if($UserName.IndexOfAny($invalidChars) -ne -1)
{
ThrowInvalidArgumentError -ErrorId "UserNameHasInvalidCharachter" -ErrorMessage ($LocalizedData.InvalidUserName -f $UserName, [string]::Join(" ", $invalidChars))
}
}
<#
.SYNOPSIS
Throws an argument error.
#>
function ThrowInvalidArgumentError
{
[CmdletBinding()]
param
(
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[System.String]
$ErrorId,
[parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[System.String]
$ErrorMessage
)
$errorCategory=[System.Management.Automation.ErrorCategory]::InvalidArgument
$exception = New-Object System.ArgumentException $ErrorMessage;
$errorRecord = New-Object System.Management.Automation.ErrorRecord $exception, $ErrorId, $errorCategory, $null
throw $errorRecord
}
function ThrowExceptionDueToDirectoryServicesError
{
[CmdletBinding()]
param
(
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[System.String]
$ErrorId,
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[System.String]
$ErrorMessage
)
$errorCategory = [System.Management.Automation.ErrorCategory]::ConnectionError
$exception = New-Object System.ArgumentException $ErrorMessage
$errorRecord = New-Object System.Management.Automation.ErrorRecord $exception, $ErrorId, $errorCategory, $null
throw $errorRecord
}
function Throw-TerminatingError
{
param(
[string] $Message,
[System.Management.Automation.ErrorRecord] $ErrorRecord
)
if ($ErrorRecord -ne $null)
{
$exception = new-object "System.InvalidOperationException" $Message,$ErrorRecord.Exception
}
else
{
$exception = new-object "System.InvalidOperationException" $Message
}
$errorRecord = New-Object System.Management.Automation.ErrorRecord $exception,"MachineStateIncorrect","InvalidOperation",$null
throw $errorRecord
}
<#
.SYNOPSIS
Writes either to Verbose or ShouldProcess channel.
#>
function Write-Log
{
[CmdletBinding(SupportsShouldProcess=$true)]
param
(
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[System.String]
$Message
)
if ($PSCmdlet.ShouldProcess($Message, $null, $null))
{
Write-Verbose $Message
}
}
<#
.SYNOPSIS
Validates the local user's credentials on the local machine.
#>
function ValidateCredentialsOnNanoServer
{
param
(
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[System.String]
$UserName,
[ValidateNotNullOrEmpty()]
[securestring]
$Password
)
$source = @'
[Flags]
private enum LogonType
{
Logon32LogonInteractive = 2,
Logon32LogonNetwork,
Logon32LogonBatch,
Logon32LogonService,
Logon32LogonUnlock,
Logon32LogonNetworkCleartext,
Logon32LogonNewCredentials
}
[Flags]
private enum LogonProvider
{
Logon32ProviderDefault = 0,
Logon32ProviderWinnt35,
Logon32ProviderWinnt40,
Logon32ProviderWinnt50
}
[DllImport("api-ms-win-security-logon-l1-1-1.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern Boolean LogonUser(
String lpszUserName,
String lpszDomain,
IntPtr lpszPassword,
LogonType dwLogonType,
LogonProvider dwLogonProvider,
out IntPtr phToken
);
[DllImport("api-ms-win-core-handle-l1-1-0.dll",
EntryPoint = "CloseHandle", SetLastError = true,
CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
internal static extern bool CloseHandle(IntPtr handle);
public static bool ValidateCredentials(string username, SecureString password)
{
IntPtr tokenHandle = IntPtr.Zero;
IntPtr unmanagedPassword = IntPtr.Zero;
unmanagedPassword = SecureStringMarshal.SecureStringToCoTaskMemUnicode(password);
try
{
return LogonUser(
username,
null,
unmanagedPassword,
LogonType.Logon32LogonInteractive,
LogonProvider.Logon32ProviderDefault,
out tokenHandle);
}
catch
{
return false;
}
finally
{
if (tokenHandle != IntPtr.Zero)
{
CloseHandle(tokenHandle);
}
if (unmanagedPassword != IntPtr.Zero) {
Marshal.ZeroFreeCoTaskMemUnicode(unmanagedPassword);
}
unmanagedPassword = IntPtr.Zero;
}
}
'@
Add-Type -PassThru -Namespace Microsoft.Windows.DesiredStateConfiguration.NanoServer.UserResource `
-Name CredentialsValidationTool -MemberDefinition $source -Using System.Security -ReferencedAssemblies System.Security.SecureString.dll | Out-Null
return [Microsoft.Windows.DesiredStateConfiguration.NanoServer.UserResource.CredentialsValidationTool]::ValidateCredentials($UserName, $Password)
}
Export-ModuleMember -Function *-TargetResource