Rotating Active Directory and Windows credentials
How Qorionix changes a Windows local password and an AD service account password over WinRM, what it cannot confirm afterwards, and what breaks.
Two rotation engines cover the Microsoft estate: windows_local for a local account on a Windows server, and ad_service_account for a domain service account in Active Directory. Both post a SOAP request to a WinRM endpoint. Neither can confirm afterwards that the new password works, and there is a larger problem with both that you should read before anything else.
These are also the two accounts in a typical small business estate that the most other things depend on, so the blast radius of getting it wrong is larger here than anywhere else in the module.
Prerequisites
Common to both engines:
- WinRM must be enabled and reachable from Qorionix. These engines speak the WinRM SOAP protocol over HTTP or HTTPS. They do not use SMB, RPC or LDAP, and a firewall that permits RDP but not WinRM will not do.
- WinRM must accept Basic authentication. Both engines authenticate with HTTP Basic, sending the account name and its current password in the request header. If your WinRM listener is configured to accept only Kerberos or Negotiate, rotation will fail at authentication.
- The credential must be a
passwordtype attached to a target of the right platform:windowsfor a local account,active_directoryfor a domain account. - The current password in the vault must be the password the machine expects right now. The engine authenticates as the account before it changes anything. A stale vault entry produces an authentication failure, not a rotation.
The port mismatch that catches people
The windows platform defaults its target port to 5985, which is the conventional HTTP WinRM port. But when no endpoint is given in metadata, the engine builds one on 5986, the HTTPS port:
windows_local https://<hostname>:5986/wsman
ad_service_account https://<domain_controller>:5986/wsmanThose two numbers disagree on purpose — the default target port is the common one, the fallback endpoint is the safe one — and the way to stop it mattering is to set winrm_endpoint explicitly in the target's metadata and not rely on either. Write out the whole URL, scheme and path included.
Windows local accounts (`windows_local`)
How it works, step by step:
Qorionix generates 18 random bytes
Cryptographically random, encoded into a password. You do not choose it and you will not see it.
It authenticates to WinRM as the account being rotated
HTTP Basic, using the current secret from the vault. The account changes its own password, so no separate administrator credential is needed — and no separate administrator credential is possible on this engine.
It sends one command
A hand-built SOAP request that runs
powershell -Command "net user <account> '<new password>'"on the server. The client timeout is 20 seconds.It treats any HTTP response below 400 as success
And stores the new password in the vault. There is no attempt to sign in again with the new password.
What to do about that, concretely. After the first rotation of any Windows local account, sign in with the stored credential by hand, or run a proxied RDP session to it, and confirm it works. If it does not, the vault holds a password the machine never accepted: reset the account at the console and update the vault entry. Once you have confirmed one rotation on a given server, subsequent ones are very likely to behave the same way — the failure mode is a rejected password policy or a broken command, and both are properties of the server that do not change between rotations.
There is a second reason to be careful here, and it is fair that you know it: this engine has no automated tests at all. No mock test, no live test. Every other engine in the module has at least one. That is a gap in our coverage rather than a statement about whether it works, and it is why the manual confirmation above is not optional advice.
windows_local: host and account are required
windows_local request: %w
windows_local do: %w
windows_local http %d: %swindows_local do: wraps the network failure — no route, refused connection, TLS problem, timeout. windows_local http 401: means WinRM rejected the credential, usually a stale vault entry or Basic authentication being disabled on the listener. windows_local http 500: with a SOAP fault body usually means WinRM accepted the request and the command inside it failed.
Active Directory service accounts (`ad_service_account`)
Structurally the same approach, aimed at a domain controller rather than a member server. There is no LDAP dependency, and that is a deliberate choice: driving the change through PowerShell on the domain controller means the change follows exactly the path a Windows administrator would take by hand, with the directory's own validation applied, rather than Qorionix writing to the directory over a protocol of its own.
metadata["domain_controller"]names the domain controller to run the change on. If it is absent, the target's hostname is used. Name it explicitly — the hostname on anactive_directorytarget is often the domain rather than a specific controller, and the change has to land on a machine.metadata["winrm_endpoint"]overrides the endpoint. The fallback ishttps://<domain_controller>:5986/wsman.- The account being rotated needs the right to reset its own password through
Set-ADAccountPassword. This is not the default for an ordinary service account and is the most common reason a first attempt fails. - The RSAT ActiveDirectory PowerShell module must be present on the domain controller. The command imports it. On a domain controller it usually is present; on a management jump box it often is not.
- The client timeout is 25 seconds.
The change itself generates 20 random bytes and runs, over WinRM:
Import-Module ActiveDirectory; Set-ADAccountPassword -Identity <account>
-Reset -NewPassword (ConvertTo-SecureString "<new password>" -AsPlainText -Force)Note -Reset. The password is reset rather than changed with knowledge of the old one, which means the account's own minimum-password-age policy does not block the operation — but the domain's complexity and history policies still apply, and a rejection by either is the failure this engine is least able to report clearly, for the same status-code reason as the Windows engine.
This engine has no automated tests either. Confirm the first rotation of each service account by hand.
ad_service_account: account is required
ad_service_account: domain_controller or host required
ad request: %w
ad do: %w
ad http %d: %sWhat the connection test can and cannot tell you here
Neither platform has a real verifier, so a connection test on a windows or active_directory target is a TCP probe. It tells you something is listening on the port. It does not tell you the password is right. The result says so in those terms rather than showing a green tick:
%s is listening on port %d. Qorionix can rotate this credential but cannot sign in to check it beforehand, so it will be stored unverified.
And on the credential itself, after a rotation, the summary reads Last rotated %s; next %s. followed by Qorionix changed the password but could not sign in with the new one to confirm it. That sentence is accurate on these two platforms and you should read it as the caution it is.
What breaks: services that cached the old password
This is the part that costs people a night. A Windows password is not only used by people typing it. It is stored, by Windows, in several places that no rotation engine can see or update.
| What holds a copy | What happens on rotation |
|---|---|
| A Windows service configured to log on as the account | Keeps using the saved password. It carries on running until it is restarted or the machine reboots — then it fails to start, with event 7000 or 7038 in the system log. |
| A scheduled task with stored credentials | Fails at its next scheduled run. Often silently, because nobody is watching a nightly task that has always worked. |
| An IIS application pool identity | Continues on the existing worker process, then fails when the pool recycles — which by default happens on a timer, so the failure arrives hours later with no obvious cause. |
| A mapped drive, a saved credential in Credential Manager, an RDP saved password | Fails at next use, and repeatedly retries. |
| Another server's configuration file, an application's connection string, a script | Fails at next use. Qorionix has no way to know these exist. |
The practical procedure, and it is boring on purpose:
Establish what uses the account before you rotate it
Search your servers for services and scheduled tasks running as it. If the answer is 'we do not know', that is itself a finding, and rotating is not the next step — enumerating is. The privileged account discovery scan can help with the inventory: Discovering privileged accounts.
Prefer accounts nothing depends on
A named administrator account used by a person for interactive work is an excellent rotation candidate: nothing caches it, and the person does not need to know it because they will reach the machine through a proxied session. A service account that runs three things is a project rather than a task.
Rotate outside working hours the first time
Not because rotation is slow — it takes seconds — but because if something was caching the password, you want to be there when it stops.
Confirm the new password by hand
Sign in with the stored credential, or open a proxied RDP session. On these two platforms this is the only proof available that the rotation did what it reported.
Where an application genuinely needs a password, consider not rotating that account
An honest answer. A service account whose password is in three configuration files is not made safer by a rotation that breaks it and gets switched off in week two. Store it in the vault, restrict who can reach it, leave the interval off, and fix the architecture when you can.
What to do next
- Read the rotation overview if you have not — particularly the sections on there being no retry and on how a failure is recorded.
- Set up a proxied RDP session so your administrators never need the Windows password in the first place.
- Start with Linux instead if you have the option — that engine proves its own work.