top of page
Search
Writer's pictureGabriel Delaney

Tackling Local Password Policies with PowerShell

Updated: Feb 4


Recently, a client posed a question: "Do you have a PowerShell script to set the local password policy on our workstations?" To which my initial response was no, but that didn't mean I couldn't create one. Less than an hour later, I had one.


Enter Set-LocalPasswordPolicy


I developed a function known as Set-LocalPasswordPolicy. The objective of this function is to adjust various settings related to the local password policy on a Windows system.



Function Set-LocalPasswordPolicy {
    <#
        .DESCRIPTION
        Sets the local password policy

        .SYNOPSIS
        Sets the local password policy

        .PARAMETER PasswordComplexity
        Specifies whether passwords must meet complexity requirements.

        .PARAMETER MinimumPasswordLength
        Specifies the minimum number of characters that passwords must contain.

        .PARAMETER MinimumPasswordAge
        Specifies the minimum number of days that passwords must be used before they can be changed.

        .PARAMETER MaximumPasswordAge
        Specifies the maximum number of days that passwords can be used before they must be changed.

        .PARAMETER PasswordHistorySize
        Specifies the number of new passwords that have to be associated with a user account before an old password can be reused.

        .PARAMETER LockoutBadCount
        Specifies the number of failed logon attempts that causes a user account to be locked out.

        .PARAMETER ResetLockoutCount
        Specifies the number of minutes that must elapse after a failed logon attempt before the failed logon attempt counter is reset to 0 bad logon attempts.

        .PARAMETER LockoutDuration
        Specifies the number of minutes a user account is locked out after the number of failed logon attempts specified by the LockoutBadCount parameter is exceeded.

        .EXAMPLE
        Set-LocalPasswordPolicy -PasswordComplexity $true -MinimumPasswordLength 8 -MinimumPasswordAge 1 -MaximumPasswordAge 90 -PasswordHistorySize 24 -LockoutBadCount 5 -ResetLockoutCount 15 -LockoutDuration 15

        .NOTES
        Author: Gabe Delaney
        Date: 05/23/2023
        Version: 1.0
        Name: Set-LocalPasswordPolicy

        Version History:
        1.0 - Initial release - 05/23/2023 - Gabe Delaney

    #>
    [CmdletBinding(SupportsShouldProcess=$true)]
    param (
        [Parameter(Mandatory=$true)]
        [boolean]$PasswordComplexity,
        [Parameter(Mandatory=$false)]
        [int]$MinimumPasswordLength,
        [Parameter(Mandatory=$false)]
        [int]$MinimumPasswordAge,
        [Parameter(Mandatory=$false)]
        [int]$MaximumPasswordAge,
        [Parameter(Mandatory=$false)]
        [int]$PasswordHistorySize,
        [Parameter(Mandatory=$false)]
        [int]$LockoutBadCount,
        [Parameter(Mandatory=$false)]
        [int]$ResetLockoutCount,
        [Parameter(Mandatory=$false)]
        [int]$LockoutDuration

    )
    Begin {
        # Check if the current user is elevated as admin
        $principal = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent())
        $is_admin = $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
        If (!$is_admin) {
            Write-Error "You must run this function as an administrator."
            Break

        }

        # Export the current local security policy to a file
        $outfile = "$Env:TEMP\secpol.cfg"
        secedit /export /cfg $outfile | Out-Null

        # Read the current local security policy file
        $sec_pol_cfg = Get-Content $outfile
    } Process {
        # If parameters are specified, update the local security policy file
        If ($PSCmdlet.ShouldProcess("$Env:COMPUTERNAME")) {
            Foreach ($key in $PSBoundParameters.Keys) {
                If (@([System.Management.Automation.Cmdlet]::CommonParameters,"Confirm") -contains $key) {
                    Continue

                }
                [int]$value = $PSBoundParameters[$key]
                $sec_pol_cfg = $sec_pol_cfg -replace ("$key = \d+", "$key = $value")

            }   
            # Write the updated local security policy file
            $sec_pol_cfg | Out-File $outfile -Force
            secedit /configure /db c:\windows\security\local.sdb /cfg $outfile /areas SECURITYPOLICY | Out-Null
            
            # Remove the local security policy file
            Remove-Item $outfile -Force | Out-Null

        }
    } End {
        Return 

    }
} 



The function offers a range of parameters to customize the policy settings. These include settings such as PasswordComplexity, MinimumPasswordLength, MinimumPasswordAge, MaximumPasswordAge, PasswordHistorySize, and several account lockout settings like LockoutBadCount, ResetLockoutCount, and LockoutDuration.


For instance, if you wanted to ensure password complexity, set a minimum password length of 8 characters, and specify that passwords must be at least 1 day old before they can be changed, you could do so using the command:






Setting lockout duration to 30 minutes:




Under the hood


PowerShell doesn't have a dedicated cmdlet or .NET method to directly alter the security policy. I mean, if it did, you probably wouldn't be here. It leverages `secedit.exe`. So, how does it work? The function uses `secedit.exe` to export the current local security policy to a temporary file. It then reads this file into a variable. The function manipulates the text of the exported policy file to adjust the settings based on the input parameters with some simple regex.


        $sec_pol_cfg = Get-Content $outfile
    } Process {
        # If parameters are specified, update the local security policy file
        If ($PSCmdlet.ShouldProcess("$Env:COMPUTERNAME")) {
            Foreach ($key in $PSBoundParameters.Keys) {
                If (@([System.Management.Automation.Cmdlet]::CommonParameters,"Confirm") -contains $key) {
                    Continue

                }
                [int]$value = $PSBoundParameters[$key]
                $sec_pol_cfg = $sec_pol_cfg -replace ("$key = \d+", "$key = $value")

            }   
...

After making the necessary adjustments, it uses `secedit.exe` again to import the updated policy back into the system, and the temporary policy file is deleted.


$sec_pol_cfg | Out-File $outfile -Force
secedit /configure /db c:\windows\security\local.sdb /cfg $outfile /areas SECURITYPOLICY | Out-Null

Conclusion


While I wouldn't recommend this as a first option in lieu of Group Policy or a true endpoint management solution, the Set-LocalPasswordPolicy function is an unconventional approach to modifying local password policy settings in a Windows environment. Though born out of necessity, it serves as an illuminating case study of the flexibility and power of PowerShell scripting.


To try it out for yourself, you can download the Set-LocalPasswordPolicy function from my GitHub repository.



2,324 views1 comment
Post: Blog2_Post

©2022 by thetolkienblackguy. Proudly created with Wix.com

bottom of page