A set of PowerShell scripts for administering Active Directory Certificate Services (AD CS / ADCS) from the command line. Built for Windows PKI administrators who need to manage certificate templates and process certificate requests at scale, without clicking through the Certificate Templates MMC snap-in or the Certification Authority console.
Currently includes:
- Bulk certificate template validity updates — useful for rolling out the CA/Browser Forum SC-081 validity reductions (200 days from March 2026, 100 days from March 2027, 47 days from March 2029) across many templates at once.
- Batch CSR submission to an Enterprise CA via
certreq.exe, with resume-safe CSV tracking of request IDs and automated retrieval of issued certificates.
No AD PowerShell module dependency. Works on Windows PowerShell 5.1 and PowerShell 7+.
| Script | Description |
|---|---|
Set-ADCSTemplateValidity.ps1 |
Bulk-update the validity period (and optionally the renewal overlap period) on one or more certificate templates, with wildcard name matching. |
Submit-CertificateRequests.ps1 |
Batch-submit .req/.csr/.txt files to an ADCS CA via certreq.exe, track request IDs in a CSV, and later retrieve the issued certificates. |
Modifies the pKIExpirationPeriod (and optionally pKIOverlapPeriod) attribute on ADCS certificate templates in Active Directory. Supports wildcard template name matching so you can update many templates in one go.
The CA/Browser Forum (ballot SC-081, passed April 2025) mandates a phased reduction of the maximum validity period for publicly-trusted TLS server certificates. While ADCS is typically used for internal PKI, many organizations mirror these limits on their internal CAs to keep templates aligned with industry best practice (and to be ready if any templates ever feed into publicly-trusted chains).
| Effective date | Maximum validity (TLS server certs) |
|---|---|
| 15 March 2026 (current) | 200 days |
| 15 March 2027 | 100 days |
| 15 March 2029 | 47 days |
As the cadence tightens, manually adjusting every template through the Certificate Templates MMC snap-in becomes painful. This script lets you update dozens of templates in seconds:
# March 2026 rollover: drop TLS templates to 200 days
.\Set-ADCSTemplateValidity.ps1 -TemplateName "*Web*","*TLS*" -ValidityPeriod 200 -ValidityPeriodUnit Days -WhatIfClient authentication, code signing, S/MIME, and other non-TLS templates are not covered by SC-081 and can keep longer validity periods. Use targeted wildcards to avoid changing those.
- Wildcard matching on template CN (e.g.
User*,*Web*,*VPN*) - Human-readable durations (
Years,Months,Weeks,Days,Hours) -WhatIf/-Confirmsupport withConfirmImpact = 'High'- No AD PowerShell module required - uses
System.DirectoryServicesdirectly - Skips templates already set to the requested value (byte-array compare)
- Auto-increments
msPKI-Template-Minor-Revisionso CAs detect the change - Deduplication when multiple patterns match the same template
- Summary output with counts of Modified / Already set / Skipped / Errors
- Pipeline-friendly output as
PSCustomObjectper template
- Windows PowerShell 5.1 or PowerShell 7+
- A domain-joined machine (or use
-Serverto target a specific DC) - Permissions to modify certificate templates (typically Enterprise Admin or delegated rights on the
CN=Certificate Templatescontainer in the Configuration naming context)
| Parameter | Type | Required | Description |
|---|---|---|---|
-TemplateName |
string[] |
Yes | One or more template CN names. Supports LDAP wildcards (*, ?). |
-ValidityPeriod |
int (1-9999) |
Yes | Numeric value for the new validity period. |
-ValidityPeriodUnit |
Years / Months / Weeks / Days / Hours |
Yes | Unit for -ValidityPeriod. AD uses 365 days/year and 30 days/month. |
-OverlapPeriod |
int (1-9999) |
No | Numeric value for the renewal overlap period. |
-OverlapPeriodUnit |
Years / Months / Weeks / Days / Hours |
No | Unit for -OverlapPeriod. Required if -OverlapPeriod is set. |
-Server |
string |
No | Target a specific domain controller (not a CA server) for the LDAP connection, e.g. dc01.domain.com. |
-WhatIf |
switch | No | Preview changes without making them. |
-Confirm |
switch | No | Prompt before each change. |
Preview which templates would be changed (WhatIf):
.\Set-ADCSTemplateValidity.ps1 -TemplateName "Web*" -ValidityPeriod 2 -ValidityPeriodUnit Years -WhatIfSet validity and overlap on multiple wildcard patterns:
.\Set-ADCSTemplateValidity.ps1 `
-TemplateName "User*","Computer*" `
-ValidityPeriod 1 -ValidityPeriodUnit Years `
-OverlapPeriod 6 -OverlapPeriodUnit WeeksSet validity on a single exact template, target a specific DC, skip confirmation:
.\Set-ADCSTemplateValidity.ps1 `
-TemplateName "WebServer" `
-ValidityPeriod 365 -ValidityPeriodUnit Days `
-Server dc01.domain.com `
-Confirm:$falsePreview all templates matching a pattern and capture the output:
$report = .\Set-ADCSTemplateValidity.ps1 -TemplateName "*" -ValidityPeriod 1 -ValidityPeriodUnit Years -WhatIf
$report | Format-Table -AutoSizeEach matched template produces a PSCustomObject with:
| Property | Description |
|---|---|
TemplateName |
Template CN |
DisplayName |
Template display name |
PreviousValidity |
Current validity period (human-readable) |
NewValidity |
Requested new validity period |
PreviousOverlap |
Current overlap period (human-readable) |
NewOverlap |
Requested new overlap period, or (unchanged) |
Status |
Modified, Already set, Skipped, or Error: <message> |
A color-coded summary is printed at the end:
--- Summary ---
Total matched : 12
Modified : 7
Already set : 3
Skipped : 2
Errors : 0
Run 'certutil -pulse' on CA server(s) to refresh.
- After modifying templates, run
certutil -pulseon each CA server for changes to be picked up immediately. Otherwise AD replication + CA cache refresh will eventually apply them. - The
-Serverparameter is for a domain controller, not the CA server. Certificate templates are AD objects stored in the forest's Configuration naming context. - Changes replicate forest-wide from the Configuration NC. Allow normal AD replication time.
- The script increments
msPKI-Template-Minor-Revisionon each change so issuing CAs detect the update. -WhatIftemplates show asSkippedin the output (they would have been modified but weren't due to the WhatIf flag).
- Connects to
RootDSEto resolve the Configuration naming context. - Searches
CN=Certificate Templates,CN=Public Key Services,CN=Services,<ConfigNC>for templates matching the pattern(s) using an LDAP filter. - For each match, decodes the current
pKIExpirationPeriod/pKIOverlapPeriod(8-byte little-endian negative FILETIME ticks). - Compares against the requested value; skips if already equal.
- Writes the new byte array via
DirectoryEntry.InvokeSet()and callsSetInfo()to commit.
Batch-submits certificate signing requests (.req / .csr / .txt) from a folder to an ADCS CA using certreq.exe, tracks each submission's request ID in a CSV file, and can later retrieve the issued certificates.
- Batch submit all request files in a folder in one run
- CSV tracking file records request ID, submit time, status, error messages per file
- Resume-safe - files already present in the tracking CSV are skipped on re-run
- Retrieve mode picks up previously-submitted
Pendingrequests and pulls issued.cerfiles - Both mode submits then retrieves in a single invocation
- Connectivity pre-check via
certutil -pingbefore any submissions - Per-run timestamped log file (
CertBatch_yyyyMMdd_HHmmss.log) -WhatIf/-Confirmsupport- Handles empty files, missing request IDs, denied requests gracefully
- Windows with
certreq.exeandcertutil.exeavailable (standard on Windows) - Windows PowerShell 5.1 or PowerShell 7+
- Permissions to submit to the target CA and template
- Network connectivity to the CA
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
-InputPath |
string |
Yes | Folder containing .req / .csr / .txt request files. |
|
-CAConfig |
string |
Yes | CA configuration string, e.g. CA01.domain.com\Contoso Issuing CA 1. |
|
-CertificateTemplate |
string |
Yes | Certificate template name (the CN, not the display name). | |
-TrackingFile |
string |
No | .\CertTracking.csv |
CSV file used to track request IDs and statuses across runs. |
-OutputFolder |
string |
No | .\Certificates |
Folder where issued .cer files are saved (one per request, named after the request file). |
-Mode |
Submit / Retrieve / Both |
No | Submit |
Submit = submit new requests only; Retrieve = pull certs for previously-pending requests; Both = do both. |
-WhatIf |
switch | No | Preview without submitting/retrieving. | |
-Confirm |
switch | No | Prompt before each action. |
Submit all CSRs in a folder:
.\Submit-CertificateRequests.ps1 `
-InputPath "C:\CSRs" `
-CAConfig "CA01.domain.com\Contoso Issuing CA 1" `
-CertificateTemplate "WebServer" `
-Mode SubmitRetrieve any issued certificates for previously-pending requests:
.\Submit-CertificateRequests.ps1 `
-InputPath "C:\CSRs" `
-CAConfig "CA01.domain.com\Contoso Issuing CA 1" `
-CertificateTemplate "WebServer" `
-Mode RetrieveSubmit and retrieve in one go:
.\Submit-CertificateRequests.ps1 `
-InputPath "C:\CSRs" `
-CAConfig "CA01.domain.com\Contoso Issuing CA 1" `
-CertificateTemplate "WebServer" `
-Mode BothPreview what would be submitted:
.\Submit-CertificateRequests.ps1 `
-InputPath "C:\CSRs" `
-CAConfig "CA01.domain.com\Contoso Issuing CA 1" `
-CertificateTemplate "WebServer" `
-Mode Submit -WhatIf| Column | Description |
|---|---|
RequestFile |
Full path of the source .req/.csr/.txt file |
RequestID |
Numeric request ID assigned by the CA |
SubmitTime |
ISO-8601 submission timestamp |
Status |
Issued, Pending, Denied, Error, or Unknown |
OutputCertFile |
Full path where the issued .cer is saved |
LastCheckTime |
ISO-8601 timestamp of last status check |
ErrorMessage |
Error output from certreq if the submission or retrieval failed |
- The tracking CSV is the source of truth for resume behavior. Deleting it will cause the script to re-submit all files (and the CA may issue duplicates).
Pendingtypically means the CA requires manager approval. Re-run inRetrievemode after approval to pull the issued cert.- Issued
.cerfiles are named after the source request file (e.g.server1.req->server1.cer). - Empty request files are skipped with a warning.
- A timestamped log file is created in the working directory for each run.
Issues and pull requests welcome.