Skip to main content
Version: 0.18

Active Directory Certificate Services

The Windows agent can enroll and renew from an enterprise CA as the computer account. TokenTimer does not create the CA or the certificate template, and it does not store CA credentials. The private key stays on the host.

Supported builds

AD CS enrollment needs an agent that declares adcs-enroll-v1. The 0.18.0 agent does. Run it on a domain-joined Windows Server member, not on a domain controller.

On Certificate operations, open Renewals. Renewal profiles is first. AD CS issuers is further down on the same page. Creating an issuer or a renewal profile does not issue a certificate by itself. You still need the agent enrollment rule, a pinned CA certificate, an attached profile with automation on, and an online Windows agent that can claim the job.

Operator action: install the agent on a domain-joined Windows member that can reach the CA. The service runs as LocalSystem and authenticates with Kerberos as DOMAIN\HOST$. Grant Read and Enroll on the template to that computer account, or to a group that contains it. Do not install the agent on a domain controller. That placement is not qualified.

Only the Windows Server versions and display languages named as qualified in the release notes are supported. Windows Server 2016 and earlier, two-tier hierarchies with an offline root, and agents on a domain controller are outside the 0.18.0 qualification. Real elapsed 24-hour retention and 14-day pending timers are not waited out in qualification.

The transport is DCOM. The member needs outbound TCP 135 and the CA's dynamic RPC range. Blocking TCP 135 makes the submit fail before the CA records a request. The agent then retries the same CSR. Linux agents, and any host that cannot reach the CA over DCOM, use ACME instead. On Windows, that ACME path is a windows-iis target.

Certificate template​

The template name you type in the issuer wizard is the template common name already published on the CA.

Operator action:

  1. On the CA, open Certificate Templates (certtmpl.msc).
  2. Copy the Template name column, not the display name. A display name such as "Web Server" can hide a template name such as WebServer.
  3. Use a template the CA publishes, schema version 2 or later. EC keys need schema version 3 or later. The profile offers P-256 and P-384. The built-in Web Server template is often schema version 1. Duplicate it, publish the copy, and type that copy's template name.
  4. Publish that template on the issuing CA.
  5. Grant Read and Enroll to the agent computer account (DOMAIN\HOST$), or to a group that contains it.

The dashboard does not list the CA's template catalog. Type the same template name the CA publishes. That name must also appear, with the same CA, in the agent enrollment rule below. A pair that is not in the rule is rejected with issuer_not_allowlisted before the CA is contacted.

Preflight refuses the template, before any submit, with ADCS_TEMPLATE_UNSAFE when any of these are true:

  • The extended key usage is not exactly Server Authentication (1.3.6.1.5.5.7.3.1). Client Authentication, Any Purpose, Smart Card Logon, Certificate Request Agent, or a missing EKU all fail. A CA will sometimes issue a certificate that also carries Client Authentication. Preflight does not treat that as safe.
  • The application policy is not Server Authentication only.
  • The template is schema version 1.
  • The agent cannot read the template from the CA, for example because no template of that name is published there. The reason is template_unreadable.
  • The requested key is smaller than the template minimum. On schema version 2, an EC key is refused. On schema version 3 or later, a key algorithm other than the template's algorithm is refused.

CA config​

caConfig is DNS host\CA name.

  • The host is the CA computer's DNS name.
  • The CA name is the name shown in the Certification Authority console. It can differ from the Windows computer name.

Use that exact string in the issuer wizard and in the agent enrollment rule. Example: ca01.corp.example.com\Corp Issuing CA.

The CA name may use letters, digits, spaces and . _ ( ) -, up to 64 characters. It is the part after the backslash, so it carries neither the server name nor a backslash.

Preflight reads two CA settings and refuses with ADCS_CA_CONFIG_UNSAFE when either check fails. If the computer account cannot read either value, the check stays refused. An enrollment rule can set operatorAttested to true for that CA after you have verified the flags yourself. The issuer wizard still requires both checks to be safe before you can pin a certificate. The settings are:

  • policy\EditFlags must not include EDITF_ATTRIBUTESUBJECTALTNAME2. The agent never sends a SAN: request attribute.
  • CA\InterfaceFlags must include IF_ENFORCEENCRYPTICERTREQUEST (0x200), so certificate requests stay encrypted.

Enrollment rules​

On the agent, config.json lists the only CA and template pairs that host may contact. Operator action: merge this object into C:\ProgramData\TokenTimerAgent\state\config.json. Edit the file in place. Do not replace the whole file, or you can drop agentId. Then restart the service:

Restart-Service TokenTimerAgent
{
"adcs": {
"enrollmentRules": [
{
"caConfig": "ca01.corp.example.com\\Corp Issuing CA",
"template": "TokenTimerWebServer",
"dnsScopes": ["example.com"],
"keyAlgorithms": ["rsa"],
"operatorAttested": false
}
]
}
}

template is the template common name from Certificate template: letters, digits and . _ -, up to 64 characters, no spaces. It is not the display name. Replace example.com with the approved DNS scope and select the key algorithms allowed by that template. CA, template, DNS scope and key algorithm must match one complete rule. A CA/template-only entry permits read-only preflight; it cannot authorize enrollment. Creating an issuer does not add this local rule.

The certificate DNS name you put on the renewal profile must already be a target the control plane has for that agent. Operator action: add it to declaredTargetSelectors and restart the agent. The next heartbeat publishes the name. An empty list does not clear the stored names. An older agent that does not send selectors on heartbeat still publishes them only at registration. See Capability declaration.

For an existing IIS certificate, enable windowsDiscovery.enabled with windowsDiscovery.stores: ["My"] (or the actual store), restart, and wait until Certificates shows that site's certificate. Declaring a target name does not create inventory. The site, HTTPS binding, port and SNI must already exist.

Complete the install runbook's signed no-op dry run first. Keep execution.enabled: true and local execution.dryRun: true for Run check and the post-pin check. AD CS preflight is a real read-only job, handled separately from the local dry-run refusal for enrollment; a signed preflight with mode: dry_run is blocked. It can cache public CA/template metadata but does not enroll or bind. Change local execution.dryRun to false, restart and confirm a fresh heartbeat only when ready for enrollment. Follow the guided AD CS workflow for the complete sequence.

Create an issuer​

On Certificate operations, open Renewals. Renewal profiles is first. AD CS issuers is further down on the same page. Open that section, then New issuer. Confirming the CA certificate requires a workspace admin. Creating the issuer does not issue a certificate.

  1. Issuer name. Type a name you will recognize later. This step does not save anything. Choose Continue.
  2. Check the CA. Choose Agent that reads the CA, and type the CA config and Template from the enrollment rule. Choose the agent that will enroll the certificate. Run check asks that agent to read the CA and stores the CA certificate and template OID on it. It does not issue a certificate. A different agent cannot enroll until it has run the same check. Wait until the wizard reports the verdict. Both the template check and the CA check must be safe. If the check stays waiting, confirm the agent is online, that its enrollment rule names this CA and template, and that its build advertises AD CS preflight.
  3. Choose a certificate. When both checks are safe, choose Use this certificate on the CA certificate to pin. Compare its certificate/key fingerprint with the CA certificate obtained independently through your trusted CA administration channel. Do not pin solely because it is the newest. Closing the wizard before the check finishes, or closing a safe check without pinning, discards the draft.

Enrollment is pinned to the selected CA public key. A later CA certificate renewal with that same key does not require a new pin. After you pin the certificate, open Renewals → AD CS issuers and choose Check again on that issuer (then return to the setup guide): renewal starts only from a check taken after the pin. A check that comes back unsafe is not pinned. That issuer stays hidden until Show N unsafe issuers is checked.

Renewal profile​

Renewal profiles is the shared list, and it is the first section on Renewals. ACME profiles appear there after an issuance. AD CS issuers, further down the same page, only prepares a private CA. A renewal profile does not choose the template. The template stays on the issuer.

On Renewals, open Renewal profiles, then New AD CS profile. Confirm an issuer in AD CS issuers first. The wizard lists only issuers that already have a pinned CA certificate.

  1. Issuer. Name the profile and select an issuer that already has a pinned CA certificate. Issuers without one are not listed.
  2. Deployment. Enter the certificate DNS name, any extra DNS names, the IIS site and port, an optional SNI hostname, the certificate store, the key, and how many days before expiry renewal should start. Defaults are site Default Web Site, port 443, store My, RSA 2048, and lead time 30 days. ECDSA key sizes are 256 and 384.
  3. Review, then Create profile.

The certificate name must be a DNS name an active agent already declares as a target selector. Add the name to declaredTargetSelectors and restart the agent. The next heartbeat publishes it. Otherwise the profile can still be saved, and the renewal sweep skips that certificate. It does not create an enrollment. Store My is LocalMachine\My.

Creating the profile does not issue a certificate by itself. On Attach, if inventory already has that DNS name, choose Attach and enable automation. When that registration is already managed, attaching stops the current period and starts a new one with this profile. If nothing matches, use the inventory steps below. Do not attach a different certificate. The profile still targets the name you typed, and the other certificate stays in inventory under its old name. Profile details list the certificates that use the profile. The renewal sweep enrolls when the profile is attached with automation on, the certificate is inside the lead time, an agent that declares the name is online, and that agent has passed the CA check for the issuer. If certificate operations are paused for the workspace, no renewal job is created.

A renewal of the IIS binding keeps that binding's TLS flags. UNSUPPORTED_BINDING_SETTINGS means a binding setting the agent cannot carry over. See What happens on renewal.

Attach the profile​

Operator action: open the certificate in the inventory. In Certificate management, the Renewal column shows the assigned profile.

If the registration is Managing, choose Stop managing. On the stopped registration, select this profile. Track without automatic renewal leaves automation off. Then choose Start managing again. Selecting the profile turns automation on.

Renewal runs when the certificate is inside the lead time and an eligible agent can claim the job. The renewal badge should read Auto-renews. See Renew certificates automatically.

Pending approval​

A request the CA accepts and holds is Awaiting CA approval. The enrollment, under AD CS issuers, shows the RequestId. The job ends. A later continuation polls that same request. Nothing is resubmitted while it is pending.

A request the CA has not accepted yet can be removed with Cancel enrollment. That does not contact the CA. Wait if an attempt is still running.

Uncertain submission​

If the agent restarts after it has started certreq -submit and before it has a trustworthy result, the enrollment is Submission uncertain. It is not resubmitted.

Operator action: supply the RequestId from the CA and choose Reconcile, or choose Abandon and then Free the key. Reconciling requires a workspace admin. A supplied RequestId is accepted only when a retrieve returns a certificate for the journaled key. Abandon deletes the key on the host. A certificate the CA issues later for that request has no key.

The CA database is not readable by the computer account unless you grant CA Read. That grant exposes every row, not only this computer's requests. Leave it unset unless you accept that trade-off. Without it, reconciliation is the RequestId you supply, or a retrieve of this computer's own requests. A request the CA issues immediately has no recorded RequestId. Find it on the CA by the certificate's serial number or thumbprint. Only pending and uncertain enrollments need a RequestId, and it must be between 1 and 2147483647.

HRESULTWhenWhat it means
0x80094014 CERTSRV_E_ADMIN_DENIED_REQUESTsubmit or retrieveA CA manager denied the request.
0x80094012 CERTSRV_E_TEMPLATE_DENIEDsubmitThe computer account does not have Enroll on the template.
0x80094800 CERTSRV_E_UNSUPPORTED_CERT_TYPEsubmitThe template is missing or not published on that CA.
0x80094811 CERTSRV_E_KEY_LENGTHsubmitThe key does not meet the template.
0x80094003 CERTSRV_E_BAD_REQUESTSTATUSsubmitThe key algorithm is one the template excludes.
0x80070005 E_ACCESSDENIEDretrieveThat RequestId belongs to another requester.
0x80094004 CERTSRV_E_PROPERTY_EMPTYretrieveRetrieval did not establish a certificate for this RequestId. This code alone does not prove deletion or denial. Verify the request on the configured CA and preserve the key.
0x800706BA RPC_S_SERVER_UNAVAILABLEsubmit, no response fileThe CA never received the request. The same CSR is retried.
0x800706BF RPC_S_CALL_FAILED_DNEsubmit, no response fileSame as unreachable: nothing was submitted.

Any other result after submit has started is uncertain. Do not resubmit it. Outcomes come from the CA-signed response and the process exit code. Console text is localized and is not used.

Version floor

The bounded uncertain-retrieval behavior below requires Core 0.18.0 or later. Older deployments do not expose this reconciliation state.

For a request already recorded as pending, the frozen pendingTimeout bounds polling after the original CA acceptance; the default is 14 days. If retrieval is still uncertain at that deadline, the enrollment becomes Submission uncertain with ADCS_RETRIEVAL_RECONCILIATION_REQUIRED. Automatic polling stops; the RequestId, journal, CSR and key are preserved. Inspect the CA, then reconcile the existing RequestId or explicitly abandon the enrollment. Ordinary pending expiry still uses a signed continuation to clean up the exact unaccepted key. An uncertain submit has no proven acceptance time and does not start that timer. A missing CA row or an HRESULT alone does not authorize deleting a key.

Interrupted enrollment​

First verify that the API and production CertOps worker are running. For Compose, use the installation's existing files and environment with docker compose ps api worker-certops and docker compose logs --since 15m worker-certops. Start the complete stack with docker compose up -d, without adding a manual Node scheduler. Helm installations use the CertOps CronJob. The default sweep runs once a minute; each enrollment's frozen polling interval still applies.

An installing enrollment can resume its incomplete installation through a signed continuation. A job marked orphaned_unknown_effect requires a human decision. Preserve the configuration, credential, journal and private key. Inspect the CA request and current IIS certificate, then send POST /api/v1/workspaces/:id/certops/enrollments/:enrollmentId/recover with the exact orphanedJobId, acknowledgeUnknownEffect: true, and a printable reason of up to 500 characters. This requires certops.issuer.manage; it does not permit changing the frozen issuer, names, target or key. Inspect the new continuation and served TLS certificate before considering recovery complete. Never delete the journal or key to force a fresh submit.

For an explicit install_failed result, correct the installation problem and send an empty JSON object to POST /api/v1/workspaces/:id/certops/enrollments/:enrollmentId/retry-installation. This reuses the issued certificate and protected key, subject to workspace pause and approval policy. It cannot recreate a missing key.

CA key change​

A later CA renewal that keeps the same key needs no action. A renewal that uses a new key fails pending enrollments with ADCS_CA_KEY_CHANGED. New enrollments wait until an admin confirms the new key. The CA and template stay the same.

Operator action: on that issuer, choose Check again, then choose Use this certificate for the new CA certificate.

Revocation​

The agent checks the issued certificate before it installs anything. That check runs as the agent service account, the computer account. The member must be able to reach the CA's CRL or OCSP as that account. An interactive sign-in on the same host can fail an LDAP CRL lookup even when the agent succeeds. A temporary outage waits and retries. An outage that lasts until the check expires does not install the certificate.

Change lifecycle to revoked records revocation in TokenTimer. It does not contact the CA. Revoking the certificate on the CA is a separate action in the Certification Authority console.

Per-enrollment timing and certificate policy​

The public POST /api/v1/workspaces/:workspaceId/certops/enrollments request accepts four optional policy fields. Values are frozen with the selected issuer, agent, target and key; retries and continuations retain that snapshot.

  • pollInterval: integer seconds from 60 to 86400, default 900.
  • pendingTimeout: integer seconds from 3600 to 7776000, default 1209600 (14 days).
  • minimumRemainingValidity: integer seconds from 0 to 31536000, default 0. An issued certificate must remain valid beyond this interval when the agent validates it.
  • requireLaterNotAfter: boolean, default false. When enabled, the incoming certificate must expire later than the certificate currently installed on the target.

Omitting these fields retains the existing defaults. Explicit nulls, strings, fractional numbers and values outside these bounds are rejected before an enrollment or job is created. These options do not bypass issuer confirmation, local enrollment rules, native certificate validation or approval policy. A certificate that fails the frozen validity policy is refused before acceptance; its unaccepted key is removed and the outgoing IIS binding remains in place.

Validation-deferral deadline​

When native revocation status cannot be obtained, the issued certificate stays uninstalled and its key remains protected in validation_deferred. The agent retries validation until a host-local deadline, then reports validation_expired and removes only that unaccepted key.

Set adcs.validationTimeoutSeconds in the agent's protected config.json to an integer from 300 through 86400. The default is 86400 (24 hours); the option permits shortening the window, not disabling or extending it. Nulls, strings, fractions and out-of-range values prevent agent startup. Restart TokenTimerAgent after editing it. This setting belongs to the host operator; an API enrollment or signed snapshot cannot override it.

Operator guidance: a short window can discard an otherwise valid issued certificate during a prolonged CRL outage. The deadline is recorded when validation first defers. Restarting the agent or restoring a longer timeout does not extend that enrollment's existing deadline. New enrollments use the new setting; installed or already accepted keys are preserved by installation-recovery custody checks.

Use an agent package that declares the qualified adcs-enroll-v1 capability. Keep its capability manifest intact. The customer host rechecks its current template and CA policy before generating a new key or resuming an unsubmitted request. Already submitted requests retain their frozen issuer and follow retrieval and validation.