Commands / Unlock-sqmSqlLogin

Unlock-sqmSqlLogin

Security sqmSQLTool v1.9.105+ · Unlock ✓ -WhatIf supported  ·  ✓ idempotent (no-op if already unlocked)
Covers both standard ways a SQL-Auth login ends up locked (is_locked = 1 under CHECK_POLICY = ON, enforced via the Windows account-lockout policy) after too many bad password attempts: unlocking it without touching the password — whether you know the current one or not — and a real password reset for when it's genuinely lost. T-SQL has no standalone ALTER LOGIN ... UNLOCK; UNLOCK only works paired with PASSWORD =. Without -NewPassword, this function instead uses the documented CHECK_POLICY = OFF / = ON toggle, which clears the lockout flag without touching the password hash at all. Neither code path restates CHECK_POLICY/CHECK_EXPIRATION in its ALTER LOGIN statement, so SQL Server leaves both exactly as they were — and the function re-reads sys.sql_logins afterward to confirm that, rather than trusting "no exception thrown" as proof nothing drifted.
ⓘ  Why the toggle instead of just re-supplying the same password? ALTER LOGIN ... WITH PASSWORD = 'same-as-before' UNLOCK would technically work if you know the password, but it re-hashes it and is one more place the plaintext has to pass through. The CHECK_POLICY toggle unlocks the account regardless of whether the caller knows the password at all — it's the only option when the password is unknown but shouldn't be reset.
⚠  -MustChange (only valid together with -NewPassword) maps to SQL Server's MUST_CHANGE clause, which itself requires CHECK_EXPIRATION = ON on the login. This function checks that up front and fails with a clear message naming the login and instance instead of letting SQL Server's own error surface unexplained.

Parameters

ParameterTypeRequiredDefaultNotes
-SqlInstancestring[]Optional$env:COMPUTERNAMEPipeline-capable. One login name applied across one or more instances.
-SqlCredentialPSCredentialOptional, SQL credential for the connection.
-LoginstringRequired, SQL-Auth login to unlock.
-NewPasswordSecureStringOptional, Without it: unlock only, password untouched. With it: real reset via PASSWORD = @newpwd UNLOCK, passed as a query parameter and never logged.
-MustChangeswitchSwitch$falseAdds MUST_CHANGE. Requires -NewPassword and CHECK_EXPIRATION = ON on the login, or the function throws before touching anything.
-ContinueOnErrorswitchSwitch$falseWith multiple -SqlInstance values, keep going after a per-instance failure instead of stopping.
-EnableExceptionswitchSwitch$falseThrow immediately (takes precedence over -ContinueOnError).
-WhatIf / -ConfirmswitchSwitch, Standard ShouldProcess support (ConfirmImpact 'Medium').

Execution Flow

START Read sys.sql_logins + LOGINPROPERTY (IsLocked, IsPolicyChecked, IsExpirationChecked) Login found? NO Status = NotFound no SQL-Auth login by that name YES -MustChange set, but CHECK_EXPIRATION = OFF? YES throws before anything runs MUST_CHANGE needs CHECK_EXPIRATION NO Not locked AND no -NewPassword given? YES Status = AlreadyUnlocked no action taken NO -WhatIf / Confirm declined? YES → Status = WhatIfSkipped, no action taken NO -NewPassword provided? NO YES ALTER LOGIN ... CHECK_POLICY = OFF; ALTER LOGIN ... CHECK_POLICY = ON; password hash untouched ALTER LOGIN ... PASSWORD = @newpwd UNLOCK [, MUST_CHANGE] parameterized, never logged Re-read sys.sql_logins (verify, do not trust "no exception") Still locked, OR CHECK_POLICY/CHECK_EXPIRATION changed from before? YES throws Status = Failed NO Status = Success

Return Value

One PSCustomObject per instance: SqlInstance, Login, WasLocked, PasswordChanged, CheckPolicy, CheckExpiration, IsLockedNow, Status (Success / AlreadyUnlocked / NotFound / WhatIfSkipped / Failed), Message.

Examples

Unlock only — password stays exactly as it was, known or not
Unlock-sqmSqlLogin -SqlInstance SQL01 -Login app_user
Password genuinely lost — real reset, forced change at next login
$newPw = Read-Host -AsSecureString 'New password'
Unlock-sqmSqlLogin -SqlInstance SQL01 -Login app_user -NewPassword $newPw -MustChange
Same login, unlocked across every AlwaysOn replica in one call
"SQL01","SQL02","SQL03" | Unlock-sqmSqlLogin -Login app_user -ContinueOnError -Confirm:$false
Preview only — nothing is changed
Unlock-sqmSqlLogin -SqlInstance SQL01 -Login app_user -WhatIf