Skip to content

Commit

Permalink
Merge version 1.3 into main
Browse files Browse the repository at this point in the history
Merge version 1.3 into main
  • Loading branch information
htcfreek committed Apr 16, 2023
2 parents 4af4c9b + 0ab8dd3 commit 905fdea
Show file tree
Hide file tree
Showing 11 changed files with 125 additions and 38 deletions.
18 changes: 12 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@

A PreOS-Package for Matrix42 Empirum to reset the LAPS password of a computer on reinstall.

The package works with Windows 10 (Build 19041 and higher) or Windows 11. Legacy Microsoft LAPS and Windows LAPS (at lest Windows 11 IP) are supported. An up to date Empirum WinPE environment (at least 1.8.12) and PowerShell 5.1 are required!
The package works with Windows 10 (Build 19041 and higher) or Windows 11. Legacy Microsoft LAPS and Windows LAPS are supported. An up to date Empirum WinPE environment (at least 1.8.12) and PowerShell 5.1 are required!

For more details about the LAPS modes and configuration see the [LAPS configuration table](#LAPS-configuration-requirements) below.
For more details about the LAPS modes, supported Operating Systems and configuration see the [LAPS configuration table](#LAPS-configuration-requirements) below.

The package has the Legacy LAPS PowerShell module from the Microsoft LAPS installer included. (Link to the installer: <https://www.microsoft.com/en-us/download/details.aspx?id=46899>)

Expand All @@ -22,9 +22,11 @@ The package has the Legacy LAPS PowerShell module from the Microsoft LAPS instal
- Automatic detection of the client's LAPS configuration based on GPOs, CSP policies and Registry values.
- Using the computer account credentials for password reset.
- Skipping package execution if the computer is not joined to Azure AD or a local domain.
- Skipping the password reset for Windows LAPS with Azure AD as target if already done by the system. ²
- LAPS can be defined as mandatory using a package variable. (See [package variables](#package-variables) for more details.)

_¹) Not supported in Windows LAPS with Azure AD as backup target, because of how LAPS works in this case. ([More information.](https://learn.microsoft.com/windows-server/identity/laps/laps-scenarios-azure-active-directory#rotate-the-password))_
_¹) Not supported in Windows LAPS with Azure AD as backup target, because of how LAPS works in this case. ([More information.](https://learn.microsoft.com/windows-server/identity/laps/laps-scenarios-azure-active-directory#rotate-the-password))_<br />
_²) Because the expiration time is stored locally on the machine it gets lost on reinstall and the reset should happens automatically. (There is a [package variable](#package-variables) to force the reset.)_

## Download and Usage

Expand All @@ -42,15 +44,19 @@ _¹) Not supported in Windows LAPS with Azure AD as backup target, because of ho
- **ResetImmediately : 0 (default) or 1**
<br />If set to 1 the password is reset immediately instead of changing the expiration time.
<br />(Enforced automatically in Azure AD environments, because changing the expiration time is not supported in this scenario.)
- **ForceResetForAzureTarget : 0 (default) or 1**
<br />If set to 1, for Windows LAPS with Azure AD as target the password is reset even if already done by the system. (Because the expiration time is stored local on the machine it gets lost on reinstall and the reset should happen automatically.)

### LAPS configuration requirements

Mode | Supported OS | Install requirements | Configuration requirements | ⚠ Important ⚠
------------ | ------------- | ------------- | ------------- | -------------
Legacy Microsoft LAPS | Up to the newest Windows version. | MS LAPS (AdmPwd) CSE | MS LAPS (AdmPwd) policies |
Windows LAPS | Windows 11 IP | built-in feature | Windows LAPS GPO/CSP/Registry values |
Windows LAPS in legacy MS LAPS emulation mode | Windows 11 IP | built-in feature | MS LAPS (AdmPwd) policies | - MS LAPS (AdmPwd) CSE must not be installed.<br />- Windows LAPS configuration must not be set.
Legacy Microsoft LAPS & Windows LAPS running parallel | Windows 11 IP | - MS LAPS (AdmPwd) CSE<br />- Windows LAPS as built-in feature. | - MS LAPS (AdmPwd) policies<br />- Windows LAPS GPO/CSP/Registry values | Both LAPS version have to manage different user accounts.
Windows LAPS | At least Windows 10&sup1; or Windows 11 21H2&sup1;. | built-in feature | Windows LAPS GPO/CSP/Registry values |
Windows LAPS in legacy MS LAPS emulation mode | At least Windows 10&sup1; or Windows 11 21H2&sup1;. | built-in feature | MS LAPS (AdmPwd) policies | - MS LAPS (AdmPwd) CSE must not be installed.<br />- Windows LAPS configuration must not be set.
Legacy Microsoft LAPS & Windows LAPS running parallel | At least Windows 10&sup1; or Windows 11 21H2&sup1;. | - MS LAPS (AdmPwd) CSE<br />- Windows LAPS as built-in feature. | - MS LAPS (AdmPwd) policies<br />- Windows LAPS GPO/CSP/Registry values | Both LAPS version have to manage different user accounts.

_&sup1; For Windows 10, Windows 11 21H1 and Windows 11 22H2 the Update from April 11 2023 is required._

## Support

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<#
Name: ResetLapsPassword
Version: 1.2
Version: 1.3
Developer: htcfreek (Heiko Horwedel)
Created at: 04.04.2023
Created at: 16.04.2023
Github URL: https://github.com/htcfreek/PreOS-ResetLapsPassword
Systems requirements:
Expand All @@ -12,10 +12,10 @@ Systems requirements:
- Legacy Microsoft LAPS and/or Windows LAPS
Description:
This package triggers the reset of the LAPS password for the client on which it is running.
The package triggers the reset of the LAPS password for the client on which it is running.
(On Azure environments only the immediate reset is supported.)
By default this package uses the credentials of the computer account/system account.
The package uses the credentials of the computer account/system account.
Package variables:
- IntuneSyncTimeout : 10 (default) or custom value.
Number of minutes to wait for the first Intune policy sync cycle.
Expand All @@ -24,6 +24,9 @@ Package variables:
- ResetImmediately : 0 (default) or 1
If set to 1 the password is reset immediately instead of changing the expiration time.
(Enforced automatically in Azure AD environments, because changing the expiration time is not supported in this scenario.)
- ForceResetForAzureTarget : 0 (default) or 1
If set to 1, for Windows LAPS with Azure AD as target the password is reset even if already done by the system.
(Because the expiration time is stored local on the machine it gets lost on reinstall and the reset should happens automatically.)
Exit codes:
0 : Script executed successful.
Expand Down Expand Up @@ -53,6 +56,7 @@ Changes (Date / Version / Author / Change):
2023-02-19 / 1.0 / htcfreek / Fix exception for missing LAPS user, comment improvement and first stable release.
2023-03-30 / 1.1 / htcfreek / Fix incorrect detection of missing Windows LAPS on unsupported systems with missing Legacy CSE.; Clean up PXE log in EMC.; Other log improvements (reboot, managed user).
2023-04-04 / 1.2 / htcfreek / Improved reboot behavior on pending Domain join reboot.; Adding a description of the log levels.
2023-04-16 / 1.3 / htcfreek / Fix detection of disabled state for Windows LAPS in Legacy Mode.; Add logging of "Force disabled" state.; Now the script can skip the reset on Windows LAPS with Azure AD as target, if already done.
#>

Expand Down Expand Up @@ -271,13 +275,15 @@ function Get-LegacyLapsState()
# Returns an object with the following members:
# - Installed : Yes=$true, No=$false (bool value)
# - Enabled : Yes=$true, No=$false (bool value)
# - ForceDisabled : Yes=$true, No=$false (bool value)
# - UserName : Name of managed user or empty if built-in Admin
# - UserExists : Yes=$true, No=$false, <Builtin Admin>=$true

# Initialize return object variable
$resultData = [PSCustomObject]@{
Installed = $false
Enabled = $false
ForceDisabled = $false
UserName = ""
UserDoesExist=$true
}
Expand All @@ -301,6 +307,11 @@ function Get-LegacyLapsState()
if ($regValue -eq 1) {
$resultData.Enabled = $true
}
else
{
# GPO is set to disabled.
$resultData.ForceDisabled = $true
}

# Get managed user (name) => Empty if default user or not configured.
if (Confirm-RegValueIsDefined -RegPath $regKey -RegValueName "AdminAccountName")
Expand All @@ -315,7 +326,7 @@ function Get-LegacyLapsState()
}

# Return results
return $resultData
return $resultData
}


Expand All @@ -327,6 +338,7 @@ function Get-WindowsLapsState([bool]$IsLegacyCSE)
# Returns an object with the following members:
# - Installed : Yes=$true, No=$false (bool value)
# - Enabled : Yes=$true, No=$false (bool value)
# - ForceDisabled : Yes=$true, No=$false (bool value)
# - LegacyEmulation : Yes=$true, No=$false (bool value)
# - ConfigSource (Possible values: "CSP", "GPO", "Local configuration", "Legacy LAPS")
# - TargetDirectory (Possible values: "Azure AD", "Active Directory")
Expand All @@ -337,6 +349,7 @@ function Get-WindowsLapsState([bool]$IsLegacyCSE)
$resultData = [PSCustomObject]@{
Installed = $true
Enabled = $false
ForceDisabled = $false
LegacyEmulation = $false
ConfigSource = "None"
TargetDirectory = "None"
Expand Down Expand Up @@ -394,6 +407,12 @@ function Get-WindowsLapsState([bool]$IsLegacyCSE)
}
}
}
else
{
# LAPS is explicit disabled.
$resultData.ForceDisabled = $true
$resultData.ConfigSource = "CSP"
}
}
elseif (Confirm-RegValueIsDefined -RegPath $regKeyPolicy -RegValueName "BackupDirectory")
{
Expand All @@ -416,6 +435,12 @@ function Get-WindowsLapsState([bool]$IsLegacyCSE)
}
}
}
else
{
# LAPS is explicit disabled.
$resultData.ForceDisabled = $true
$resultData.ConfigSource = "GPO"
}
}
elseif (Confirm-RegValueIsDefined -RegPath $regKeyLocal -RegValueName "BackupDirectory")
{
Expand All @@ -438,10 +463,16 @@ function Get-WindowsLapsState([bool]$IsLegacyCSE)
}
}
}
else
{
# LAPS is explicit disabled.
$resultData.ForceDisabled = $true
$resultData.ConfigSource = "Local configuration"
}
}
elseif ((Confirm-RegValueIsDefined -RegPath $regKeyLegacy -RegValueName "AdmPwdEnabled") -AND ($IsLegacyCSE -eq $false) -AND ($resultData.Installed -eq $true))
elseif ((Confirm-RegValueIsDefined -RegPath $regKeyLegacy -RegValueName "AdmPwdEnabled") -AND ($IsLegacyCSE -eq $false) -AND ($resultData.Installed -eq $true) -AND ($resultData.ForceDisabled -eq $false))
{
# Legacy Microsoft LAPS Policy (AdmPwd-Policy) - Legacy Emulation Mode. (Only if Legacy CSE is not installed and Windows LAPS is installed.)
# Legacy Microsoft LAPS Policy (AdmPwd-Policy) - Legacy Emulation Mode. (Only if Legacy CSE is not installed, Windows LAPS is installed and Windows LAPS is not force disabled.)
If ((Get-ItemPropertyValue -Path $regKeyLegacy -Name "AdmPwdEnabled") -eq 1)
{
$resultData.Enabled = $true
Expand All @@ -463,7 +494,7 @@ function Get-WindowsLapsState([bool]$IsLegacyCSE)
}

# Return results
return $resultData
return $resultData
}


Expand Down Expand Up @@ -492,8 +523,8 @@ function Get-LapsResetTasks([bool]$LapsIsMandatory)
$legacyLapsUser = if ([string]::IsNullOrWhiteSpace($legacyLapsProperties.UserName)) { "<Built-in Administrator>" } Else { $legacyLapsProperties.UserName };
$winLapsProperties = Get-WindowsLapsState -IsLegacyCSE $legacyLapsProperties.Installed;
$winLapsUser = if ([string]::IsNullOrWhiteSpace($winLapsProperties.UserName)) { "<Built-in Administrator>" } Else { $winLapsProperties.UserName };
WriteLogDebug "Legacy Microsoft LAPS: Installed = $(ConvertTo-YesNo $legacyLapsProperties.Installed), Enabled = $(ConvertTo-YesNo $legacyLapsProperties.Enabled), Managed user = $($legacyLapsUser)"
WriteLogDebug "Windows LAPS: Installed = $(ConvertTo-YesNo $winLapsProperties.Installed), Enabled = $(ConvertTo-YesNo $winLapsProperties.Enabled), Managed user = $($winLapsUser), Configuration source = $($winLapsProperties.ConfigSource), Target Directory = $($winLapsProperties.TargetDirectory), Legacy emulation mode = $(ConvertTo-YesNo $winLapsProperties.LegacyEmulation)"
WriteLogDebug "Legacy Microsoft LAPS: Installed = $(ConvertTo-YesNo $legacyLapsProperties.Installed), Enabled = $(ConvertTo-YesNo $legacyLapsProperties.Enabled), GPO is disabled = $(ConvertTo-YesNo $legacyLapsProperties.ForceDisabled), Managed user = $($legacyLapsUser)"
WriteLogDebug "Windows LAPS: Installed = $(ConvertTo-YesNo $winLapsProperties.Installed), Enabled = $(ConvertTo-YesNo $winLapsProperties.Enabled), Configuration set to disabled = $(ConvertTo-YesNo $winLapsProperties.ForceDisabled), Managed user = $($winLapsUser), Configuration source = $($winLapsProperties.ConfigSource), Target Directory = $($winLapsProperties.TargetDirectory), Legacy emulation mode = $(ConvertTo-YesNo $winLapsProperties.LegacyEmulation)"

# Checking results
if (($legacyLapsProperties.Enabled -eq $false) -AND ($winLapsProperties.Enabled -eq $false))
Expand Down Expand Up @@ -542,17 +573,18 @@ function Get-LapsResetTasks([bool]$LapsIsMandatory)
}


function Invoke-LapsResetCommands([PSCustomObject]$LapsResetTasks, [bool]$DoResetImmediately)
function Invoke-LapsResetCommands([PSCustomObject]$LapsResetTasks, [bool]$DoResetImmediately, [bool]$ForceResetForAzureTarget)
{
# Function: Invoke-LapsResetCommands
# The function invokes the commands to reset the LAPS passwords.
# Input parameter:
# - [PSCustomObject]$LapsResetTasks : Custom object generated by the function "Get-LapsResetTasks" containing the required reset information.
# - [bool]$DoResetImmediately : $true = Reset immediately.; $false = Only set expiration time.
# - [bool]$ForceResetForAzureTarget : $true = Reset anyway.; $false = Do not reset again if already done by system. (With Azure AD as Windows LAPS target.)

# Debug/Log information
WriteLogDebug "Starting reset sequence ..."
WriteLogDebug "Final reset task summary: $($LapsResetTasks -replace ';',',' -replace '@{','' -replace '}',',') DoResetImmediately=$($DoResetImmediately)"
WriteLogDebug "Final reset task summary: $($LapsResetTasks -replace ';',',' -replace '@{','' -replace '}',',') DoResetImmediately=$($DoResetImmediately), ForceResetForAzureTarget=$($ForceResetForAzureTarget)"
WriteLogDebug "Executing user account: $env:Username"
if ($DoResetImmediately) { WriteLogInfo "Immediate reset is enabled." } Else { WriteLogInfo "Immediate reset is disabled. - Only expiration time will be set." }

Expand All @@ -577,8 +609,30 @@ function Invoke-LapsResetCommands([PSCustomObject]$LapsResetTasks, [bool]$DoRese
ExitWithCodeMessage -errorCode 510 -errorMessage "ERROR: Failed to reset password for Windows LAPS user! - The user does not exist."
}

# We don't need special credentials here because the system account is allowed to reset the password.
Reset-LapsPassword
# If Azure is the target, the reset may been done by the system already. (This is because the expiration time is stored locally and the information gets lost on reinstall.)
# In this case we skip the reset if it is not forced by the package variable.
[bool] $isAzureExpTime = Confirm-RegValueIsDefined -RegPath "HKLM:\Software\Microsoft\Windows\CurrentVersion\LAPS\State" -RegValueName "AzurePasswordExpiryTime"
if ($LapsResetTasks.WinLapsIsAzureTarget -and $isAzureExpTime -and ($ForceResetForAzureTarget -eq $false))
{
[Int64] $regExpTime = Get-ItemPropertyValue -Path "HKLM:\Software\Microsoft\Windows\CurrentVersion\LAPS\State" -Name "AzurePasswordExpiryTime"
[string] $azureExpTime = [DateTime]::FromFileTimeUtc($regExpTime).ToLocalTime().ToString()
WriteLogDebug "The password is already reset and saved to Azure AD. New expiration time: $($azureExpTime) - The reset command is skipped."
}
else
{
# Logging of special information.
if ($LapsResetTasks.WinLapsIsAzureTarget -and $ForceResetForAzureTarget)
{
WriteLogInfo "WARNING: The reset for Azure AD environments is enforced by the package variable! - This is not recommended."
}
elseif ($LapsResetTasks.WinLapsIsAzureTarget -and ($isAzureExpTime -eq $false))
{
WriteLogDebug "Windows LAPS (with Azure AD as target) has not reset the password yet. - The script triggers the reset now."
}

# We don't need special credentials here because the system account is allowed to reset the password.
Reset-LapsPassword
}
}
Else
{
Expand Down Expand Up @@ -657,6 +711,7 @@ function Main() {
[int]$varIntuneSyncTimeout = ReadEmpirumVariable -varName ResetLapsPassword.IntuneSyncTimeout -defaultValue 10
[bool]$varLapsIsMandatory = ReadEmpirumVariable -varName ResetLapsPassword.LapsIsMandatory -isBoolean -defaultValue 0
[bool]$varLapsResetImmediately = ReadEmpirumVariable -varName ResetLapsPassword.ResetImmediately -isBoolean -defaultValue 0
[bool]$varForceResetForAzureTarget = ReadEmpirumVariable -varName ResetLapsPassword.ForceResetForAzureTarget -isBoolean -defaultValue 0

# Update device configuration and check if device is joined to an AD
$isDeviceADJoined = Update-ClientMgmtConfiguration -IntuneSyncTimeout $varIntuneSyncTimeout
Expand Down Expand Up @@ -686,7 +741,7 @@ function Main() {
}

# Invoke LAPS reset tasks
Invoke-LapsResetCommands -LapsResetTasks $lapsResetTasks -DoResetImmediately $varLapsResetImmediately
Invoke-LapsResetCommands -LapsResetTasks $lapsResetTasks -DoResetImmediately $varLapsResetImmediately -ForceResetForAzureTarget $varForceResetForAzureTarget

WriteLogDebug "Finished ResetLapsPassword package.";
}
Expand Down
Loading

0 comments on commit 905fdea

Please sign in to comment.