Create Local Users and Password Policies via PowerShell

Was this helpful?

Local accounts are the cockroaches of enterprise identity: even when you think you’ve eliminated them,
they reappear in the dark corners of “temporary” servers, lab boxes promoted to prod, and appliances with a
Windows brain under the hood.

The pain shows up at 2 a.m. when a service account expires, an installer silently created a local admin,
or a security scan asks why PasswordLastSet is “Never.” PowerShell can fix this—if you use it like an operator,
not like a person copy-pasting snippets into a live server with sweaty hands.

What you are really managing (and why it keeps biting you)

A “local user” sounds simple: a username and a password stored on a Windows machine. But operationally,
you are managing three overlapping systems:

  • The account object (enabled/disabled, password state, group membership, profile, rights).
  • The authentication policy surface (password length, complexity, lockout threshold, expiration).
  • The blast radius (where that credential works and what it can do).

Local users are a reliability hazard because they are sticky. They outlive projects.
They survive migrations. They don’t show up in your central IAM dashboard. And when you have more than a handful
of servers, “we’ll remember to rotate that password” becomes a bedtime story you tell auditors.

The job isn’t “create a user.” The job is: create a user that is predictable, discoverable, governed, and reversible.
Predictable means consistent naming, consistent group membership, consistent password rules. Discoverable means
you can inventory it and explain why it exists. Governed means policies are enforced (or at least measured). Reversible
means you can disable it cleanly without breaking the server.

If you’re thinking “this is overkill for local users,” congratulations: you’ve never had to respond to a lateral movement
investigation where the attacker’s favorite credential was a local admin password shared across 700 machines.

Facts and historical context you can use in arguments

Engineers love facts they can deploy in meetings. Here are a few that matter when the room starts arguing about “just a local account.”

  1. Windows has had local accounts since NT—and a lot of security defaults were designed when networks were smaller and flatter.
    Legacy still shapes today’s behavior.
  2. The “Local Users and Groups” MMC snap-in didn’t always exist on all SKUs (notably absent in some editions), which is why
    scripts and net.exe became the lingua franca of admin automation.
  3. Password policy is not per-user by default. On standalone machines it’s largely a machine-level policy; on domain-joined systems,
    domain policy normally wins, unless fine-grained policies or local overrides are in play.
  4. PowerShell’s LocalAccounts module (cmdlets like New-LocalUser) arrived relatively late compared to the age of Windows admin.
    Older scripts use ADSI or net user because they had to.
  5. Account lockout policy is a reliability lever, not only a security lever. Too strict and you DoS yourself with fat-fingered services;
    too lax and brute force becomes a hobby.
  6. “Password never expires” is not just a checkbox; it’s a statement that you’ve accepted unbounded credential lifetime.
    That needs compensating controls (rotation system, LAPS, vault checkout, etc.).
  7. Group membership is the real permission model for most admins. The user object is boring; the groups are where the fire is.
  8. Windows maintains multiple sources of truth for security settings (local security policy, registry-backed policy, domain policy, GPO processing).
    Your script can “set” something and still lose at the next refresh.
  9. Built-in accounts behave differently. “Administrator” and “Guest” have special handling and historical baggage; renaming doesn’t remove identity.

Core tools: LocalAccounts, ADSI, net.exe, secedit

There are four practical ways to manage local users and password policy in Windows automation. You’ll use all of them
depending on OS version, remoting constraints, and how deep you need to go.

1) PowerShell LocalAccounts module

Cmdlets like Get-LocalUser, New-LocalUser, Add-LocalGroupMember are readable and safe-ish.
They’re great for modern Windows Server builds and Windows 10/11. They also make errors legible.

2) ADSI (WinNT provider)

Old but dependable. Works when LocalAccounts isn’t available. Also useful when you need compatibility in mixed fleets.
Downside: not as friendly; you can injure yourself with one-liners and not notice until production starts limping.

3) net.exe (net user, net localgroup, net accounts)

The cockroach survival tool: it’s there, it works, and it’s supported basically everywhere.
It’s also text-based, which means parsing outputs can be annoying. Still: for policy inspection and quick checks,
it’s brutally effective.

4) secedit (local security policy export/import)

When you need to view or apply machine-level security options in bulk, secedit can export policy to an INF-style file.
You can version-control that file, diff it, and apply it. It’s not pretty, but it’s honest.

One paraphrased idea that’s guided operations for decades: paraphrased idea “Hope is not a strategy” — Ed Yourdon.
Local accounts are where hope goes to retire. Treat them like any other production surface.

Operator principles: decide first, then script

Scripts that manage users fail for boring reasons: the account already exists, the machine is domain-joined, the password policy
is enforced by GPO, WinRM is half-configured, or the operator lacks local admin. So don’t start by writing code. Start by deciding:

  • Intent: Why does this local account exist? Human break-glass? App service? Vendor support?
  • Scope: Which machines? Which OU? Which environments?
  • Authority: Who owns the credential lifecycle? A vault? A team? A platform service?
  • Controls: Expiration, lockout, audit, membership restrictions, deny interactive logon where possible.
  • Reversibility: What’s the disable procedure? What alerts fire if it disappears?

And yes, you can automate all of it. The trap is automating the wrong assumptions faster.

Joke #1: If your “temporary local admin” is older than your coffee machine, it’s not temporary—it’s a fossil with privileges.

Practical tasks (commands, outputs, decisions)

The fastest way to get good at this is to do real, inspectable operations and tie each output to a decision.
Below are tasks you’ll actually run in production. Commands are shown as if executed from a shell on a host.
Adapt hostnames and paths to your environment.

Task 1: Confirm the OS and build (because cmdlet availability is not a vibe)

cr0x@server:~$ powershell -NoProfile -Command "Get-ComputerInfo | Select-Object WindowsProductName, WindowsVersion, OsBuildNumber"
WindowsProductName WindowsVersion OsBuildNumber
----------------- -------------- -------------
Windows Server 2022 Datacenter 21H2           20348

What it means: You’re on a modern server build; LocalAccounts module is usually present.
Decision: Prefer Get-LocalUser/New-LocalUser over ADSI/net parsing.

Task 2: Check whether the LocalAccounts module is available

cr0x@server:~$ powershell -NoProfile -Command "Get-Module -ListAvailable Microsoft.PowerShell.LocalAccounts | Select-Object Name, Version"
Name                               Version
----                               -------
Microsoft.PowerShell.LocalAccounts 1.0.0.0

What it means: Cmdlets exist locally.
Decision: Use them for account CRUD; fall back to ADSI only for edge cases.

Task 3: Inventory existing local users and their password flags

cr0x@server:~$ powershell -NoProfile -Command "Get-LocalUser | Select-Object Name, Enabled, PasswordRequired, PasswordExpires, LastLogon | Format-Table -Auto"
Name               Enabled PasswordRequired PasswordExpires LastLogon
----               ------- ---------------- -------------- ---------
Administrator      False   True             False
DefaultAccount     False   False            False
Guest              False   False            False
svc_backup         True    True             True           1/21/2026 3:12:09 AM
vendor_support     True    True             False          12/11/2025 10:44:18 AM

What it means: You already have at least two non-built-in accounts; one does not expire.
Decision: Decide whether vendor_support should be time-boxed or disabled when not in use. “Never expires” needs a compensating control.

Task 4: Create a new local user safely (no plaintext password in your history)

cr0x@server:~$ powershell -NoProfile -Command "$pw=Read-Host -AsSecureString 'Enter password'; New-LocalUser -Name 'svc_app' -Password $pw -Description 'App service account' -PasswordNeverExpires:$false -UserMayNotChangePassword:$true"
Enter password: ********

What it means: Account created; password captured as a SecureString.
Decision: Immediately set group membership and rights; do not leave a service account lying around without minimal permissions.

Task 5: Verify the account properties you actually care about

cr0x@server:~$ powershell -NoProfile -Command "Get-LocalUser -Name 'svc_app' | Select-Object Name, Enabled, UserMayChangePassword, PasswordExpires, PasswordLastSet | Format-List"
Name                  : svc_app
Enabled               : True
UserMayChangePassword : False
PasswordExpires       : True
PasswordLastSet       : 2/5/2026 1:03:27 PM

What it means: This account cannot change its own password, and expiration is enabled.
Decision: Put password rotation under a controlled mechanism (vault + scheduled rotation, or managed local admin solution), not “someone will remember.”

Task 6: Add the account to a local group (and verify)

cr0x@server:~$ powershell -NoProfile -Command "Add-LocalGroupMember -Group 'Users' -Member 'svc_app'; Get-LocalGroupMember -Group 'Users' | Select-Object Name | Sort-Object Name | Select-Object -First 10"
Name
----
BUILTIN\Administrator
NT AUTHORITY\INTERACTIVE
NT AUTHORITY\Authenticated Users
SERVER01\svc_app

What it means: The account is a standard user, not an admin.
Decision: Keep it that way unless you can prove the app needs admin. “Needs admin” is often a synonym for “no one tested permissions.”

Task 7: Audit local Administrators membership (the group that ruins your week)

cr0x@server:~$ powershell -NoProfile -Command "Get-LocalGroupMember -Group 'Administrators' | Select-Object ObjectClass, Name, PrincipalSource | Format-Table -Auto"
ObjectClass Name                          PrincipalSource
----------- ----                          ---------------
User        SERVER01\Administrator         Local
Group       CONTOSO\Domain Admins          ActiveDirectory
User        SERVER01\vendor_support        Local

What it means: A vendor local user is a local admin. That’s a high-risk credential by definition.
Decision: If vendor access is needed, move to time-bound access (enable/disable on demand), rotate password after use, or remove from Administrators.

Task 8: Disable an account without deleting it (reversibility is a feature)

cr0x@server:~$ powershell -NoProfile -Command "Disable-LocalUser -Name 'vendor_support'; Get-LocalUser -Name 'vendor_support' | Select-Object Name, Enabled"
Name           Enabled
----           -------
vendor_support False

What it means: Login is blocked, but the account and SID remain.
Decision: Prefer disable over delete when you’re unsure about dependencies; deleting can orphan ACLs and break scheduled tasks silently.

Task 9: Find where an account is referenced (scheduled tasks are repeat offenders)

cr0x@server:~$ powershell -NoProfile -Command "Get-ScheduledTask | Where-Object {$_.Principal.UserId -match 'svc_app|vendor_support'} | Select-Object TaskName, State, @{n='UserId';e={$_.Principal.UserId}} | Format-Table -Auto"
TaskName           State  UserId
--------           -----  ------
NightlyBackup      Ready  SERVER01\svc_app

What it means: svc_app is used by a scheduled task; disabling it would break that job.
Decision: Treat the account as part of an application dependency graph. Rotate carefully; test task execution after changes.

Task 10: Inspect effective password and lockout policy quickly (net accounts)

cr0x@server:~$ powershell -NoProfile -Command "net accounts"
Force user logoff how long after time expires?:       Never
Minimum password age (days):                          1
Maximum password age (days):                          60
Minimum password length:                              14
Length of password history maintained:                24
Lockout threshold:                                    5
Lockout duration (minutes):                           15
Lockout observation window (minutes):                 15
Computer role:                                        WORKSTATION
The command completed successfully.

What it means: This machine expects 14-char passwords, expires at 60 days, locks out after 5 failures.
Decision: Service accounts with interactive logon risk should be constrained; if they must exist, ensure you won’t trigger lockouts with bad stored credentials.

Task 11: Change password policy settings (where allowed) with net accounts

cr0x@server:~$ powershell -NoProfile -Command "net accounts /minpwlen:16 /maxpwage:45 /uniquepw:24 /lockoutthreshold:5 /lockoutduration:15 /lockoutwindow:15"
The command completed successfully.

What it means: Local policy updated—unless overridden by domain GPO.
Decision: On domain-joined machines, treat this as a diagnostic tool, not a policy enforcement mechanism. If GPO overwrites it, your “change” will evaporate.

Task 12: Verify domain join status (policy source matters)

cr0x@server:~$ powershell -NoProfile -Command "(Get-CimInstance Win32_ComputerSystem).PartOfDomain"
True

What it means: Domain policies likely apply.
Decision: Do not assume local password policy commands will stick; check resultant policy and GPO precedence.

Task 13: Export local security policy to a file for diffing

cr0x@server:~$ powershell -NoProfile -Command "secedit /export /cfg C:\Windows\Temp\secpol.cfg /areas SECURITYPOLICY"
The task has completed successfully.
See log %windir%\security\logs\scesrv.log for detail information.

What it means: You now have a text representation of many local security settings.
Decision: Diff this file across servers to find configuration drift. Drift is often the real bug.

Task 14: Extract password policy values from the exported config

cr0x@server:~$ powershell -NoProfile -Command "Select-String -Path C:\Windows\Temp\secpol.cfg -Pattern 'MinimumPasswordLength|MaximumPasswordAge|PasswordComplexity|LockoutBadCount' | ForEach-Object {$_.Line}"
MinimumPasswordLength = 14
MaximumPasswordAge = 60
PasswordComplexity = 1
LockoutBadCount = 5

What it means: Complexity is enabled (1), min length 14, max age 60, lockout at 5.
Decision: If scanners complain, you can prove what’s configured. If configs differ across nodes in a cluster, fix consistency first.

Task 15: Set “Password never expires” for a specific local user (with a reason, not a shrug)

cr0x@server:~$ powershell -NoProfile -Command "Set-LocalUser -Name 'svc_backup' -PasswordNeverExpires $true; Get-LocalUser -Name 'svc_backup' | Select-Object Name, PasswordExpires"
Name       PasswordExpires
----       --------------
svc_backup False

What it means: The account will no longer expire.
Decision: Only do this when you have reliable rotation elsewhere (vault-managed, scheduled update + service restart plan, or a managed solution). Otherwise you’re trading outages for breaches.

Task 16: Find stale local users (last logon older than N days)

cr0x@server:~$ powershell -NoProfile -Command "$cutoff=(Get-Date).AddDays(-90); Get-LocalUser | Where-Object {$_.Enabled -and $_.LastLogon -and $_.LastLogon -lt $cutoff} | Select-Object Name, LastLogon | Sort-Object LastLogon"
Name           LastLogon
----           ---------
svc_legacy     10/2/2025 11:18:44 AM
vendor_support 11/3/2025 8:20:01 AM

What it means: Enabled accounts haven’t logged on in 90 days.
Decision: Candidates for disable, but first check scheduled tasks, services, and “run as” configurations. Stale does not always mean unused—just often.

Task 17: Check Windows services for “Log On As” dependencies

cr0x@server:~$ powershell -NoProfile -Command "Get-CimInstance Win32_Service | Where-Object {$_.StartName -match 'svc_app|svc_backup'} | Select-Object Name, State, StartName | Format-Table -Auto"
Name           State   StartName
----           -----   ---------
AppWorker      Running .\svc_app
BackupAgent    Running .\svc_backup

What it means: These accounts are tied to running services.
Decision: Any password change requires a coordinated update of service credentials (and ideally a restart window). Otherwise, enjoy your self-inflicted incident.

Task 18: Export local users inventory to CSV for centralized review

cr0x@server:~$ powershell -NoProfile -Command "Get-LocalUser | Select-Object Name, Enabled, PasswordExpires, PasswordLastSet, LastLogon, Description | Export-Csv -NoTypeInformation C:\Windows\Temp\local-users.csv; Get-Item C:\Windows\Temp\local-users.csv | Select-Object Name, Length"
Name            Length
----            ------
local-users.csv 924

What it means: You now have a small artifact you can ingest into your inventory pipeline.
Decision: Baseline “known good” and alert on changes. Account drift is a signal, not background noise.

Password policy reality on Windows (local vs domain)

Password policy is where good intentions go to die quietly. The key detail: on a standalone host,
the “password policy” is mostly one machine-level policy applied to local accounts. On a domain-joined host,
domain policy typically controls what’s enforced, and local tweaks may not matter.

What you can reliably control locally

  • Local user existence and state: create, disable, rename (careful), set description, set flags.
  • Local group membership: who is in Administrators, Remote Desktop Users, etc.
  • Some machine-level password/lockout settings on standalone or where GPO allows it.
  • Per-user password expiration flag (PasswordNeverExpires) on local accounts.

What will surprise you if you don’t test

  • Domain GPO overwrite: you set min length to 16 locally, GPO says 14, and after policy refresh you’re back to 14.
    Your script “worked” and still lost.
  • Different enforcement for different credential types: service logons, scheduled tasks, RDP logons, and SMB access can fail differently.
    The account exists, but authentication context changes the failure mode.
  • Lockouts from cached old passwords: you rotate a password, but a service keeps trying the old one every minute and locks you out.

Joke #2: Password policies are like umbrellas—everyone agrees they’re necessary until you ask them to carry one in the rain.

Strong opinion: stop “manually managing” local admin passwords

If your environment is large enough to justify a script, it’s large enough to justify centralized management.
For admin-grade local accounts, you want unique passwords per host and automatic rotation. Humans are inconsistent,
and attackers love consistency.

If you must have local admins for break-glass, use an operational runbook: controlled checkout, short validity, rotate after use,
and monitor membership changes. The goal is not “perfect.” The goal is “not embarrassing.”

Three corporate mini-stories from the trenches

Incident caused by a wrong assumption: “Local policy applies everywhere”

A platform team rolled out a PowerShell job to “harden” standalone Windows boxes used for a manufacturing line.
The script set minimum password length and lockout threshold using net accounts. It ran green. Everyone moved on.

A month later, a different team domain-joined half of those hosts to enable centralized patching and inventory.
No one told the platform team because the join was “just a quick improvement.”

Then the line started failing in a new way: a service account that had been stable for years began locking out.
The service ran under a local user whose password had been rotated by a human on one machine and copied “for convenience” to the rest.
On the newly domain-joined nodes, a stricter domain lockout policy kicked in. The service retried with an old password, locked itself out,
and the line paused.

The postmortem had one sentence that mattered: they assumed local policy was authoritative.
The fix was also one sentence: detect domain join status, measure resultant policy, and treat password rotation as a system, not a ritual.

Optimization that backfired: “Let’s rotate everything nightly”

A security initiative demanded frequent password rotation. The team doing the work wanted to be helpful, so they built a nightly rotation job.
It updated local service account passwords and tried to “repair” affected services by setting the new credentials and restarting them.

For a week it looked brilliant. Password age graphs went down. Compliance dashboards went up. The team got invited to more meetings,
which is never a reward but often mistaken for one.

Then came Patch Tuesday. Several servers rebooted slower than usual, and the rotation job overlapped with service startup.
Half-up services got their credentials updated mid-start, then restarted again. Some services didn’t like being restarted while still initializing.
A few went into a failed state and stayed there. Monitoring lit up like a holiday display.

The backfire wasn’t the rotation itself; it was the coupling: password changes and restarts were happening during an unstable boot window,
without per-application health checks.
They fixed it by adding maintenance windows, using service-specific validation, and rotating less frequently but more safely.
Compliance accepted the trade because outages are also a security problem.

Boring but correct practice that saved the day: “Inventory first, then change”

Another team had a habit that felt painfully slow: before changing any local accounts, they ran an inventory script that exported:
local users, group membership, service logon identities, and scheduled tasks principals. Every change ticket included the diff.

When a vendor demanded a local admin “for troubleshooting,” the team created a dedicated account, disabled by default, and documented its
required group membership and expiration behavior. They enabled it only during vendor sessions.

Months later, an internal incident response scan flagged that account as “admin present.” The knee-jerk reaction from a different group
was to delete it immediately. The inventory diff saved them: it showed the account was disabled and had not logged in since the last vendor session.
It also listed three scheduled tasks that referenced the username—but those tasks were disabled too, tied to an old engagement.

The team disabled the scheduled tasks permanently, removed the account from Administrators, and kept it disabled as an identity placeholder
until the contract ended. No outages, no drama, no surprise access denial at the worst possible time. Boring won.

Fast diagnosis playbook

When “local user/password policy stuff” breaks, it usually breaks in one of three places: identity state, policy enforcement, or dependency usage.
This is how you find the bottleneck quickly without performing interpretive dance in the Event Viewer.

First: confirm what you’re dealing with (standalone vs domain, cmdlets available)

  • Check domain join: (Get-CimInstance Win32_ComputerSystem).PartOfDomain
  • Check OS build: Get-ComputerInfo
  • Check LocalAccounts module: Get-Module -ListAvailable Microsoft.PowerShell.LocalAccounts

Why: It determines whether local policy changes are real, and which automation path is reliable.

Second: inspect the account object state

  • Is it enabled? Get-LocalUser -Name X | Select Enabled
  • Password flags: PasswordExpires, PasswordLastSet, UserMayChangePassword
  • Group membership: Get-LocalGroupMember -Group Administrators

Why: “Login failed” often means “disabled account” or “not in the right group,” not “wrong password.”

Third: identify what is using the credential

  • Services: Get-CimInstance Win32_Service | Where StartName -match X
  • Scheduled tasks: Get-ScheduledTask | Where Principal.UserId -match X

Why: Changing passwords is easy; changing all the consumers is where outages are born.

Fourth: validate policy enforcement (what is actually in effect)

  • net accounts for current local view
  • secedit /export to capture policy state and compare between hosts

Why: If GPO overwrites your changes, you need to adjust the source of truth, not the symptom.

Common mistakes: symptoms → root cause → fix

1) “My script set min password length, but it didn’t change”

Symptoms: net accounts shows old values after a while; security scan still reports previous settings.
Root cause: Domain GPO overrides local policy; or the box is governed by a security baseline.
Fix: Confirm domain join status, then adjust policy at the governing layer. Locally, treat changes as ephemeral unless you control GPO.

2) “Account created successfully, but logon fails”

Symptoms: User exists, is enabled, but cannot log on via RDP/SMB/interactive.
Root cause: Missing rights (e.g., not in Remote Desktop Users), local security policy denies logon,
or the account is forced to change password at next logon and cannot do so in that context.
Fix: Check group membership and local rights assignments; for service accounts, avoid interactive logon scenarios entirely.

3) “Service started failing after password rotation”

Symptoms: Service won’t start; Event logs show logon failure; scheduled task returns 0x52e (bad password).
Root cause: Password changed on the account but not updated for the service/task consumer; or restart didn’t happen.
Fix: Find all consumers (services + tasks), update credentials, then restart in a controlled window. Validate health after.

4) “Account keeps getting locked out”

Symptoms: Lockout events; repeated authentication failures; the account unlocks and locks again quickly.
Root cause: Some process is still using old credentials (service, task, mapped drive, cached credential).
Fix: Identify all consumers, update them, then reset lockout. If you can’t find it, temporarily disable the account and watch what breaks.

5) “Get-LocalUser says LastLogon is empty so I can’t tell if it’s used”

Symptoms: LastLogon is blank for some accounts even though you suspect activity.
Root cause: LastLogon isn’t always populated for all authentication types, and it’s not a full audit trail.
Fix: Use event logs/audit policy if you need definitive usage, and inventory dependencies (services/tasks) as a stronger signal.

6) “We renamed Administrator; attackers still find it”

Symptoms: Security tooling still identifies the built-in admin; audits still reference it.
Root cause: The built-in account is identified by SID (RID 500), not just the name.
Fix: Rename if policy requires, but also disable where possible, use LAPS/unique passwords, and monitor RID 500 behavior.

7) “Our automation ran, but some servers didn’t change”

Symptoms: Fleet has inconsistent local users/policies; some nodes have extra admins.
Root cause: Remoting failures, permission issues, or scripts that don’t validate outcomes (they assume success).
Fix: Make scripts idempotent and verify state after every action; export inventories and diff; fail the run when drift remains.

Checklists / step-by-step plan

Plan A: Create a local service account with sane defaults

  1. Inventory before change: export current local users and Administrators membership.
  2. Create the account with a secure password entry method (SecureString prompt or vault injection).
  3. Set flags intentionally: expiration behavior, user-can-change-password, enabled/disabled.
  4. Grant minimum group membership: start with Users; avoid Administrators.
  5. Bind dependencies: configure the service/task to use the account.
  6. Validate: start service, run task, check logs, confirm no lockouts.
  7. Document: description field on the user; add owner and purpose; record in inventory.

Plan B: Enforce password and lockout policy on standalone servers

  1. Check whether the host is domain-joined. If yes, stop and go manage policy at GPO level.
  2. Use net accounts to read current settings; export with secedit for a diff artifact.
  3. Apply policy with net accounts (or a consistent security template approach).
  4. Re-read settings and confirm they stuck.
  5. Test account creation and password change against new rules (don’t discover enforcement during an outage).

Plan C: Safe disable/removal workflow for local accounts

  1. Identify dependencies (services, tasks, “run as” usage) before touching the account.
  2. Disable the account first; monitor for breakage.
  3. If no breakage, remove from privileged groups immediately (Administrators, Remote Desktop Users).
  4. After an observation period, consider deletion if you must—but keep evidence of what was removed.
  5. Update inventory and baseline so it doesn’t “grow back” unnoticed.

Plan D: Fleet-wide hygiene (the part people avoid)

  1. Standardize naming conventions (e.g., svc_* for services, bg_* for break-glass).
  2. Centralize inventory: gather local users, local admins, and password flags regularly.
  3. Alert on changes to Administrators membership and on new enabled local users.
  4. Rotate privileged credentials with a managed system; don’t create shared passwords across hosts.
  5. Run quarterly “stale account” reviews with dependency evidence attached, not vibes.

FAQ

1) Should I use New-LocalUser or net user?

Use New-LocalUser when available; it’s clearer and objects are easier to validate. Keep net user for compatibility and quick diagnostics.

2) Can I set password complexity per local user?

Not in the way people mean it. Complexity is typically part of machine/domain policy, not a per-user knob for local accounts.
You can set per-user expiration behavior (e.g., “never expires”), but complexity is enforced at policy level.

3) Why does my password policy change revert?

Because something higher-priority owns it—usually domain Group Policy. Local changes are overwritten during policy refresh.
Your script didn’t “fail”; it lost a governance fight.

4) What’s the safest way to handle local admin accounts?

Unique password per host, automatically rotated, and audited membership. If you’re still copying a single password between machines, stop.
That’s not “operations,” that’s “collectible incident.”

5) Should service accounts have expiring passwords?

If you can rotate safely with automation and update all consumers, yes—expiration is fine. If you can’t, forced expiration will create outages.
In that case, set “never expires” only with a real rotation mechanism and monitoring.

6) Why does disabling a local user sometimes not fix access?

Because access may be coming from a different principal (domain group, another local account, cached token) or because the real issue is
authorization (group membership) not authentication. Verify who is logging on and from where.

7) How do I prove what the password policy is on a host?

Use net accounts for the quick view, and secedit /export for a file you can attach to a ticket and diff across machines.
Proof beats arguments.

8) Is it okay to delete local accounts after decommissioning an app?

Usually, but disable first and check dependencies. Deleting can orphan ACLs and break tasks that no one remembered.
If you need a clean slate, capture an inventory snapshot before removal.

9) What’s the most common cause of local account lockouts after a password change?

A scheduled task or service still configured with the old password. The retries trigger lockouts.
Update consumers first where possible, or change password and consumer in one controlled window.

Conclusion: practical next steps

Local users are not inherently evil. They’re just unmanaged by default, and default is where reliability goes to get bruised.
If you want the short operational path: inventory, standardize, automate, verify, and keep a rollback plan.

Next steps you can do this week:

  • Run an inventory of Get-LocalUser and local Administrators membership across your server fleet; store the results centrally.
  • Pick a policy stance for local accounts: which ones are allowed, how they’re named, how they expire, and who owns them.
  • Implement an alert on changes to local Administrators membership and on creation of new enabled local users.
  • For any privileged local accounts, move to unique-per-host password management and rotation with an auditable workflow.
  • Write a runbook for password rotation that includes: dependency discovery, update steps, service restarts, and validation checks.

Do those, and the next time someone says “it’s just a local user,” you’ll have data, controls, and fewer surprise outages. That’s the whole game.

← Previous
Fix “The specified network name is no longer available” on SMB
Next →
UAC Explained: The One Level You Shouldn’t Use

Leave a comment