DSCResources/MSFT_xJeaEndpoint/MSFT_xJeaEndpoint.psm1
#requires -version 5 #region Helper # Internal function to throw terminating error with specified errorCategory, errorId and errorMessage function New-TerminatingError { param ( [Parameter(Mandatory)] [String]$errorId, [Parameter(Mandatory)] [String]$errorMessage, [Parameter(Mandatory)] [System.Management.Automation.ErrorCategory]$errorCategory ) $exception = New-Object System.InvalidOperationException $errorMessage $errorRecord = New-Object System.Management.Automation.ErrorRecord $exception, $errorId, $errorCategory, $null throw $errorRecord } function Get-JeaDir { Join-Path $env:ProgramFiles 'Jea' } function Get-JeaToolKitDir { Join-Path (Get-JeaDir) 'Toolkit' } function Get-JeaUtilDir { Join-Path (Get-JeaDir) 'Util' } function Get-JeaStartupScriptDir { Join-Path (Get-JeaDir) 'StartupScript' } function Get-JeaActivityDir { Join-Path (Get-JeaDir) 'Activity' } <# .Synopsis Creates a random password. .DESCRIPTION Creates a random password by generating a array of characters and passing it to Get-Random .EXAMPLE PS> New-RandomPassword g0dIDojsRGcV .EXAMPLE PS> New-RandomPassword -Length 3 dyN .EXAMPLE PS> New-RandomPassword -Length 30 -UseSpecialCharacters r5Lhs1K9n*joZl$u^NDO&TkWvPCf2c #> function New-RandomPassword { [CmdletBinding()] [OutputType([String])] Param ( # Length of the password [Parameter(Mandatory=$False, Position=0)] [ValidateRange(12, 127)] $Length=12, # Includes the characters !@#$%^&*-+ in the password [switch]$UseSpecialCharacters ) [char[]]$allowedCharacters = ([Char]'a'..[char]'z') + ([char]'A'..[char]'Z') + ([byte][char]'0'..[byte][char]'9') if ($UseSpecialCharacters) { foreach ($c in '!','@','#','$','%','^','&','*','-','+') { $allowedCharacters += [char]$c } } $characters = 1..$Length | % { $characterIndex = Get-Random -Minimum 0 -Maximum $allowedCharacters.Count $allowedCharacters[$characterIndex] } return (-join $characters) } Set-Alias New-RandomString New-RandomPassword <# .Synopsis Create a User Account with a random name .DESCRIPTION .EXAMPLE Example of how to use this cmdlet .EXAMPLE Another example of how to use this cmdlet #> function New-JeaSessionAccount { [CmdletBinding(DefaultParameterSetName='UserNamePrefix')] Param ( #******************************************************************************** # A random username is generated. This parameter gives you the opportunity to # to prefix that name with a string to help identify the account [Parameter(Mandatory=$false, ParameterSetName='UserNamePrefix')] [ValidateLength(0,12)] $UserNamePrefix = 'JSA-', [Parameter(Mandatory=$true, ParameterSetName='UserName')] $UserName, [string] $ComputerName = $Env:COMPUTERNAME, [string] $Description = "JEA Service Account created by $($env:UserDomain + '\' + $ENV:Username) at $(Get-Date)", [string[]] $Group='Administrators' ) if (!($PSCmdlet.ParameterSetName -eq 'UserName')) { $MaxWindowsUserLength = 20 $Templength = $MaxWindowsUserLength - $UserNamePrefix.Length $UserName = $UserNamePrefix + (New-RandomString -Length $TempLength) } $Computer = [ADSI]"WinNT://$COMPUTERNAME,Computer" Write-Verbose "New [JeaSessionAccount]$UserName" $User = $Computer.Create('User', $UserName) $password = New-RandomPassword -Length 127 -UseSpecialCharacters $cred = new-object pscredential $username, (convertto-securestring $password -asplaintext -force) $null = $( $User.SetPassword($password) $User.SetInfo() $User.FullName = 'Jea Session Account' $User.SetInfo() $User.Description = $Description $User.SetInfo() $User.UserFlags = 64 # ADS_UF_PASSWD_CANT_CHANGE http://msdn.microsoft.com/en-us/library/aa772300(v=vs.85).aspx $User.SetInfo() ) foreach ($item in $group) { Write-Verbose " [JeaSessionAccount]$UserName ADD [Group]$item" $gobj = [ADSI]"WinNT://$COMPUTERNAME/$item,group" $null = $gobj.add("WinNT://$ComputerName/$UserName") } return $cred } function Remove-JeaAccount { Param ( [Parameter(Mandatory=$true)] $UserName, [string] $ComputerName = $Env:COMPUTERNAME ) $Computer = [ADSI]"WinNT://$COMPUTERNAME,Computer" $computer.delete("user",$userName) } function Test-JeaSessionAccount { [CmdletBinding()] [OutputType([Boolean])] Param ( [Parameter(Mandatory=$true, Position=0)] $UserName, $ComputerName = $Env:COMPUTERNAME ) $oldDebugPrefernce = $DebugPreference; $oldVerbosePreference = $VerbosePreference $DebugPreference = $VerbosePreference = 'SilentlyContinue' $user = Get-CimInstance -Query "select * from Win32_UserAccount Where Name=""$UserName"" AND LocalAccount=""TRUE""" -ComputerName $ComputerName -Verbose:0 $DebugPreference = $oldDebugPrefernce; $VerbosePreference = $oldVerbosePreference return [Boolean]$user } function Reset-JeaSessionAccountPassword { [CmdletBinding()] Param ( #******************************************************************************** # A random username is generated. This parameter gives you the opportunity to # to prefix that name with a string to help identify the account [Parameter(Mandatory=$true, Position=0)] $UserName, $ComputerName = $Env:COMPUTERNAME ) $user = [ADSI]"WinNT://$computerName/$username,user" $password = New-RandomPassword -Length 127 -UseSpecialCharacters $null = $user.setpassword($password) $null = $user.SetInfo() $cred = new-object pscredential $username, (convertto-securestring $password -asplaintext -force) return $cred } function New-InitializationFile { [CmdletBinding()] [Alias()] [OutputType([int])] Param ( [Parameter(Mandatory=$true, Position=0)] $Path, [Parameter(Position=1)] $Toolkit ) Write-Verbose "New [InitializationFile]$path" ""> $path $toolkitNames = @() foreach ($t in $toolkit) { $toolkitNames += ("$t" +'-Toolkit') } @" <# This is a auto-generated JEA startup file Generated At: $(Get-date) Generated On: $(hostname) Generated By: $($env:UserDomain + '\' + $env:UserName) #> function whoami {`$PSSenderInfo} `$Init = (Join-Path `$env:ProgramFiles 'Jea\Util\Initialize-Toolkit.ps1') . `$Init -toolkit $($Toolkitnames -join ',') "@ > $path } $ResetScript = @' #This resets the passwords for all the RUnas [char[]]$pwChars = ([Char]'a'..[char]'z') + ([char]'A'..[char]'Z') + ([byte][char]'0'..[byte][char]'9') foreach ($c in '!','@','#','$','%','^','&','*','-','+') { $pwChars += [char]$c } $endpoint = Get-PSSessionConfiguration |where {$_.RunAsUser -like 'JSA-*'} if ($endpoint) { foreach ($ep in $endpoint) { $user = [ADSI]"WinNT://$($env:computerName)/$($ep.RunAsUser),user" $password = (( $pwChars |Get-Random -Count 127 | % {[char]$_}) -join '') $null = $user.setpassword($password) $null = $user.SetInfo() $cred = new-object pscredential ($ep.RunAsUser), (convertto-securestring $password -asplaintext -force) Set-PSSessionConfiguration -Name $($ep.Name) -RunAsCredential $cred -Force } Restart-Service Winrm -Force } '@ $RestartWinRM = @' "WinRM Restart start: $((get-date).GetDateTimeFormats()[112])" >> 'c:\program files\Jea\Activity\WinRMRestart.txt' While ((Get-DscLocalConfigurationManager).LocalConfigurationManagerState -ne 'Ready') { Start-Sleep -Seconds 5 } Restart-Service winrm -Force "WinRM Restarted at : $((get-date).GetDateTimeFormats()[112])" >> 'c:\program files\Jea\Activity\WinRMRestart.txt' '@ function Assert-ScheduledScripts { $UtilDir = Get-JeaUtilDir if (!(Test-Path $UtilDir)) { Write-Verbose "New [JeaDirectory]$utildir" mkdir -Force -Path $UtilDir } $ResetPS1 = Join-Path $UtilDir 'ScheduledPasswordReset.ps1' if (!(Test-Path $ResetPs1) -or (cat $resetPS1 -Delimiter "None") -ne $resetscript) { Write-Verbose 'Reset [JeaScheduledPasswordResetScript]' $resetScript > $ResetPS1 } $RestartWinRMPS1 = Join-Path $UtilDir 'RestartWinrm.ps1' if (!(Test-Path $restartWinRMPS1) -or (Get-Content $restartWinRMPS1 -Delimiter 'None') -ne $RestartWinRM) { Write-Verbose 'Reset [JeaScheduledPasswordreStartWinRMScript]' $RestartWinRM > $restartWinRMPS1 } } function Assert-ScheduledTasks { param( [Parameter()] $cred ) $UtilDir = Get-JeaUtilDir $wd = "`"$UtilDir`"" $ResetPS1 = Join-Path $UtilDir 'ScheduledPasswordReset.ps1' $file = "`"$ResetPS1`"" $Arguments = "-nologo -executionPolicy ByPass -NoProfile -NonInteractive -file $file -WorkingDirectory $wd -WindowStyle hidden" $s = Get-ScheduledTask -TaskPath \Microsoft\Windows\Jea\ -TaskName ResetJeaSessionAccountPasswords -ErrorAction SilentlyContinue Write-Verbose "Test [JEAScheduledTask]ResetJeaSessionAccountPasswords $([Bool]$s)" if (!($s) -or $s.Actions.Count -ne 1 -or $s.Actions[0].Execute -ne 'PowerShell.exe' -or $s.Actions[0].Arguments -ne $Arguments) { if ($s) {Unregister-ScheduledTask -TaskPath \Microsoft\Windows\Jea\ -TaskName ResetJeaSessionAccountPasswords -ErrorAction SilentlyContinue } $action = New-ScheduledTaskAction -Execute PowerShell.exe -Argument $arguments $trigger = New-ScheduledTaskTrigger -Daily -At 1:00 if (!$Cred) { Write-Verbose 'Register [JEAScheduledTask]ResetJeaSessionAccountPasswords' $cred = Reset-JeaSessionAccountPassword -UserName JeaSchTaskAccount } $setting = New-ScheduledTaskSettingsSet -MultipleInstances IgnoreNew -StartWhenAvailable Write-Verbose 'Register [JEAScheduledTask]ResetJeaSessionAccountPasswords' Register-ScheduledTask -TaskName ResetJeaSessionAccountPasswords -Action $action ` -TaskPath \Microsoft\Windows\Jea\ -Description 'Reset Jea Session Account Passwords' ` -Settings $setting ` -Trigger $trigger ` -User $cred.UserName -Password $cred.GetNetworkCredential().Password } $RestartWinRMPS1 = Join-Path $UtilDir 'RestartWinrm.ps1' $file = "`"$RestartWinrmPS1`"" $Arguments = "-nologo -executionPolicy ByPass -NoProfile -NonInteractive -file $file -WorkingDirectory $wd -WindowStyle hidden" $s = Get-ScheduledTask -TaskPath \Microsoft\Windows\Jea\ -TaskName RestartWinRM -ErrorAction SilentlyContinue Write-Verbose "Test [JEAScheduledTask]RestartWinRM $([Bool]$s)" if (!($s) -or $s.Actions.Count -ne 1 -or $s.Actions[0].Execute -ne 'PowerShell.exe' -or $s.Actions[0].Arguments -ne $Arguments) { if ($s) {Unregister-ScheduledTask -TaskPath \Microsoft\Windows\Jea\ -TaskName RestartWinRM -ErrorAction SilentlyContinue } $action = New-ScheduledTaskAction -Execute PowerShell.exe -Argument $arguments if (!$Cred) { Write-Verbose 'Register [JEAScheduledTask]JeaSchTaskAccount' $cred = Reset-JeaSessionAccountPassword -UserName JeaSchTaskAccount } $settings = New-ScheduledTaskSettingsSet -MultipleInstances IgnoreNew Write-Verbose 'Register [JEAScheduledTask]RestartWinRM' Register-ScheduledTask -TaskName RestartWinRM -Action $action ` -TaskPath \Microsoft\Windows\Jea\ -Description 'Restart WinRM after current DSC cycle is complete' ` -Settings $settings ` -User $cred.UserName -Password $cred.GetNetworkCredential().Password } } function Assert-JeaAdmin { try { $cred = $null if (!(Test-JeaSessionAccount -UserName JeaSchTaskAccount)) { Write-Verbose 'New [JeaScheduledTaskAccount]' $cred = New-JeaSessionAccount -UserName JeaSchTaskAccount -Group Administrators -Description "This is a special Jea account to run the ResetJeaSessionAccountPasswords Scheduled task" } Assert-ScheduledScripts Assert-ScheduledTasks $cred }catch { write-verbose "ERROR: $($_|fl * -force|out-string)" throw } } #endregion function Get-TargetResource { [CmdletBinding()] [OutputType([System.Collections.Hashtable])] param ( [parameter(Mandatory = $true)] [System.String] $Name ) try { $endpoint = Get-PSSessionConfiguration -Name $Name -ErrorAction SilentlyContinue -Verbose:0 if ($endpoint) { $returnValue = @{ Name = [System.String]$Name Toolkit = [System.String[]]"TODO: GET TOOLKITS" Ensure = [System.String]'Present' SecurityDescriptorSddl = [System.String]$endpoint.SecurityDescriptorSddl Group = [String[]]$( "TODO: Get Groups" ) } } else { $returnValue = @{ Name = [System.String]$Name Ensure = [System.String]'Absent' } } $returnValue }catch { write-Debug "ERROR: $($_|fl * -force|out-string)" New-TerminatingError -errorId 'SetJeaEndpointFailed' -errorMessage $_.Exception -errorCategory InvalidOperation } } function Set-TargetResource { [CmdletBinding()] param ( [parameter(Mandatory = $true)] [System.String] $Name, [System.String[]] $Toolkit, [System.String] $SecurityDescriptorSddl='O:NSG:BAD:P(A;;GA;;;BA)(A;;GA;;;RM)S:P(AU;FA;GA;;;WD)(AU;SA;GXGW;;;WD)', [System.String[]] $Group = @('Administrators'), [System.Boolean] $CleanAll, [ValidateSet('Present','Absent')] [System.String] $Ensure = 'Present' ) try { write-Verbose "$((get-date).GetDateTimeFormats()[112]) Start Setting [EndPoint]$Name" if ($CleanAll) { #region Remove non-default endpoints Write-Verbose 'Remove [JeaEndpoints]*' $defaultEndpoints = 'microsoft.powershell','microsoft.powershell.workflow','microsoft.powershell32','microsoft.windows.servermanagerworkflows' Get-PSSessionConfiguration -Verbose:0 |where Name -NotIn $defaultEndpoints | % { Write-Verbose "Remove [PSEndpoint]$($_.Name)" Unregister-PSSessionConfiguration -NoServiceRestart -Force -Verbose:0 -Name $_.Name } #endregion #region Remove the JSA UserAccounts Write-Verbose 'Remove [JSAUserAccount]*' [ADSI]$Server="WinNT://$($Env:COMPUTERNAME)" foreach ($account in (Get-CimInstance -Query 'select * from Win32_UserAccount Where Name Like "JSA-%" AND LocalAccount="TRUE"').Name) { Write-Verbose "Remove [JSAUserAccount]$account" $server.Delete('user',$account) } if (Get-CimInstance -Query 'select * from Win32_UserAccount Where Name = "JeaSchTaskAccount" AND LocalAccount="TRUE"') { Write-Verbose "Remove [JSAUserAccount]JeaSchTaskAccount" $server.Delete('user','JeaSchTaskAccount') } #endregion #region remove StartupFiles if (test-path (Get-JeaStartupScriptDir)) { Write-Verbose 'Remove [JeaStartupfile]*' Remove-Item (Join-Path (Get-JeaStartupScriptDir) *) -Recurse -Verbose } #endregion #region Remove JeaScheduled Tasks write-verbose "Remove [ScheduleTask]RestartWinRM" Unregister-ScheduledTask -TaskPath \Microsoft\Windows\Jea\ -TaskName RestartWinRM -ErrorAction Ignore write-verbose "Remove [ScheduleTask]ResetJeaSessionAccountPasswords" Unregister-ScheduledTask -TaskPath \Microsoft\Windows\Jea\ -TaskName ResetJeaSessionAccountPasswords -ErrorAction Ignore #endregion } else { Assert-JeaAdmin $endPointName = $Name $endPoint = Get-PSSessionConfiguration -Name $Name -Verbose:0 -ErrorAction SilentlyContinue $account = "JSA-$Name" if ($Ensure -AND !($endPoint)) { #region ACCOUNT Write-Verbose "Test [JeaSessionAccount]$account" if (Test-JeaSessionAccount -UserName $account) { Write-Verbose " [JeaSessionAccount]$account = `$true; Reset-JeaSessionAccountPassword" $cred = Reset-JeaSessionAccountPassword -UserName $account }else { Write-Verbose "New [JeaSessionAccount]$account" $cred = New-JeaSessionAccount -UserName $account -Description 'PowerShell Session Acount' -Group $group } #endregion #region InitializationFile $startupfile = Join-Path (Get-JeaStartupScriptDir) "Initialize-$($Name).ps1" Write-Verbose "New [JeaStartupFile]$StartupFile -> [Jeatoolkit]$Toolkit" New-InitializationFile -Path $StartupFile -Toolkit $Toolkit #endregion #region EndPoint Write-Verbose "New [JeaEndPoint]$Name" try { # Set the following preference so the functions inside Unregister-PSSessionConfig doesn't get these settings $oldDebugPrefernce = $DebugPreference; $oldVerbosePreference = $VerbosePreference $DebugPreference = $VerbosePreference = 'SilentlyContinue' $null = Register-PSSessionConfiguration -Name $Name -StartupScript $startupfile -RunAsCredential $cred -NoServiceRestart -Verbose:0 -Force -SecurityDescriptorSddl $SecurityDescriptorSddl # Reset the following preference to older values $DebugPreference = $oldDebugPrefernce; $VerbosePreference = $oldVerbosePreference Start-ScheduledTask -TaskName RestartWinRM -TaskPath \Microsoft\Windows\Jea\ } catch { write-Debug "ERROR: $($_|fl * -force|out-string)" New-TerminatingError -errorId 'JeaEndpointConfigurationFailed' -errorMessage $_.Exception -errorCategory InvalidOperation } #endregion }elseif (!($Ensure) -And $EndPoint) { Write-Verbose "Unregister [JeaEndpoint]$Name" Unregister-PSSessionConfiguration -Name $Name -Force -Verbose:0 Start-ScheduledTask -TaskName RestartWinRM -TaskPath \Microsoft\Windows\Jea\ if (Test-JeaSessionAccount -UserName $account) { [ADSI]$Server="WinNT://$($Env:COMPUTERNAME)" $server.Delete('user',$account) } } } }catch { write-Debug "ERROR: $($_|fl * -force|out-string)" New-TerminatingError -errorId 'SetJeaEndpointFailed' -errorMessage $_.Exception -errorCategory InvalidOperation }finally { write-Verbose "$((get-date).GetDateTimeFormats()[112]) Done Setting [EndPoint]$Name" } } function Test-TargetResource { [CmdletBinding()] [OutputType([System.Boolean])] param ( [parameter(Mandatory = $true)] [System.String] $Name, [System.String[]] $Toolkit, [System.String] $SecurityDescriptorSddl, [System.String[]] $Group, [System.Boolean] $CleanAll, [ValidateSet('Present','Absent')] [System.String] $Ensure = 'Present' ) try { if ($Cleanall) { return $false }else { Write-Verbose "Test [JeaEndPoint]$name" $endPoint = Get-PSSessionConfiguration -Name $Name -Verbose:0 -ErrorAction Stop $namePresentMessage = " [JeaEndPoint]$name Present" Write-Verbose -Message $namePresentMessage if ($Ensure -eq 'Absent') { return $false } else { Write-Verbose ' TODO: Check for Toolkits, StartupScript and UserAccount' return $true } } }catch [Microsoft.PowerShell.Commands.WriteErrorException] { Write-Verbose " [JeaEndPoint]$Name Absent" return ($Ensure -eq 'Absent') } } Export-ModuleMember -Function *-TargetResource |