Skip to content

CertIntel plugin for Posh-ACME

Requires Posh-ACME 4.7+ (4.x) and Windows PowerShell 5.1 or PowerShell 7. The plugin is the single CertIntel.ps1 file, Posh-ACME's native plugin format.

No separate JSON configuration is needed. Pass delegation details as plugin arguments and API keys as SecureString values. Posh-ACME saves the arguments for renewal and protects secure values using the account's encryption settings. It manages its own configuration files automatically.

Install

Install Posh-ACME first. Then download the plugin into a permanent folder outside the module directory, so module updates do not remove it. The release record points at the current version and carries each file's SHA-256, so nothing below hardcodes a version:

$release = Invoke-RestMethod 'https://api.certin.tel/api/v1/releases/plugin-posh-acme/latest.json'
$plugins = New-Item -ItemType Directory -Force "$env:LOCALAPPDATA\CertIntel\Posh-ACME\Plugins"
foreach ($name in 'CertIntel.ps1', 'Get-CertIntelIisCertificate.ps1') {
    $file = $release.files | Where-Object name -eq $name
    $path = Join-Path $plugins $name
    Invoke-WebRequest -Uri $file.url -OutFile $path
    if ((Get-FileHash $path -Algorithm SHA256).Hash -ne $file.sha256) { throw "SHA-256 mismatch: $name" }
}
"Installed CertIntel plugin $($release.version) to $plugins"

Posh-ACME finds the plugin through POSHACME_PLUGINS. Set it for the current session and persist it for future sessions, then import the module and confirm the plugin loads:

$env:POSHACME_PLUGINS = "$env:LOCALAPPDATA\CertIntel\Posh-ACME\Plugins"
[Environment]::SetEnvironmentVariable('POSHACME_PLUGINS', $env:POSHACME_PLUGINS, 'User')
Import-Module Posh-ACME -Force
Get-PAPlugin CertIntel -Params

For an all-users installation, use a folder such as $env:ProgramData\CertIntel\Posh-ACME\Plugins and persist the variable with the 'Machine' target from an elevated session. Scheduled renewal scripts must set POSHACME_PLUGINS before importing Posh-ACME. PowerShell 7 users on other platforms can substitute their own absolute paths. To upgrade later, run the download block again. Both scripts are Authenticode-signed; see Downloads for the signer thumbprint.

Create a delegation in CertIntel and its permanent CNAME at your DNS provider:

_acme-challenge.example.com -> YOUR-LABEL.dns01.certin.tel

The page that follows shows, under Use this delegation with… → Posh-ACME, the plugin arguments for your CertIntel deployment, ready to copy: the Delegation ID, CNAME target and API base. Keep the update secret (shown once) for the prompt. example.com and *.example.com share one mapping. Other names require separate exact mappings.

Staging issuance without JSON

Set-PAServer LE_STAGE
$pluginArgs = @{
    CertIntelDomain       = 'example.com'
    CertIntelDelegationId = '11111111-1111-1111-1111-111111111111'
    CertIntelCnameTarget  = 'YOUR-LABEL.dns01.certin.tel'
    CertIntelApiKey       = Read-Host 'CertIntel API key' -AsSecureString
}
New-PACertificate 'example.com','*.example.com' -Plugin CertIntel `
    -PluginArgs $pluginArgs -DnsSleep 60 -AcceptTOS -Contact 'you@example.com'

The API endpoint defaults to https://api.certin.tel/api/v1. To override it, add CertIntelApiBase = 'https://your-server.example/api/v1' to the hashtable; on a private deployment always set it. Each call prints the API it uses, and says when that is the default. An optional CertIntelLockDirectory sets where the plugin keeps its per-delegation lock files (default .certintel-locks under Posh-ACME's configuration root); it is required only when calling Add-DnsTxt directly outside Posh-ACME. Certificate hostnames must not contain underscores; the _acme-challenge record prefix does contain a required underscore.

This performs DNS validation and writes Posh-ACME's certificate files without installing them into a server. Confirm issuance and TXT cleanup. Increase -DnsSleep if needed for DNS propagation.

Multiple delegations

Use arrays in matching domain order:

$pluginArgs = @{
    CertIntelDomain = @('example.com', 'www.example.com')
    CertIntelDelegationId = @(
        '11111111-1111-1111-1111-111111111111'
        '22222222-2222-2222-2222-222222222222'
    )
    CertIntelCnameTarget = @('FIRST-LABEL.dns01.certin.tel', 'SECOND-LABEL.dns01.certin.tel')
    CertIntelApiKey = @(
        (Read-Host 'API key for example.com' -AsSecureString)
        (Read-Host 'API key for www.example.com' -AsSecureString)
    )
}
New-PACertificate 'example.com','*.example.com','www.example.com' -Plugin CertIntel `
    -PluginArgs $pluginArgs -DnsSleep 60 -AcceptTOS -Contact 'you@example.com'

CertIntelApiBase accepts one shared endpoint or one per domain. Keep secure keys in the top-level CertIntelApiKey argument; do not put them in nested hashtables or convert them to JSON.

Credential protection and vaults

On Windows, default DPAPI protection depends on the user and machine. Run renewals under the setup account. Linux/macOS users should enable Posh-ACME's alternative plugin encryption.

Posh-ACME 4.11+ can store its encryption key in a SecretManagement vault; API credentials remain encrypted in its order files. After installing SecretManagement and registering a writable vault:

$env:POSHACME_VAULT_NAME = 'YourRegisteredVault'
Set-PAAccount -UseAltPluginEncryption

For a new account, use New-PAAccount -AcceptTOS -Contact 'you@example.com' -UseAltPluginEncryption. Verify account sskey is VAULT and VaultGuid is populated. If alternative encryption was already enabled, follow the official migration steps. Renewals need noninteractive vault access. See the SecretManagement guide.

You can obtain the initial API key with Get-Secret if it returns a SecureString. Posh-ACME saves that value; it is not a live reference to the individual API-key secret. After rotating a key, update the selected order with the full argument set using Set-PAOrder -PluginArgs $pluginArgs.

See also the plugin development guide.

Renewal and production

With the staging order selected, test renewal using Submit-Renewal -Force. Saved plugin arguments are reused; no key prompt or JSON file editing is needed. For production, select Set-PAServer LE_PROD and create a production order using the same delegation arguments. A staging certificate is not publicly trusted.

A scheduled script should set POSHACME_PLUGINS, import Posh-ACME, select the intended server, then run Submit-Renewal -AllAccounts. Set any vault or custom POSHACME_HOME environment variables used during setup as well. Test the script under the actual renewal account.

Locks default to .certintel-locks in Posh-ACME's configuration root. Use one issuing client/host per delegation, with a consistent lock directory. Do not run the dedicated CertIntel agent concurrently against the same delegation. The API permits at most two TXT values and cannot protect against writers on other hosts.

Production and scheduled command examples

After the staging test, create the production order in the same PowerShell session (using the $pluginArgs from setup):

Set-PAServer LE_PROD
New-PACertificate 'example.com','*.example.com' -Plugin CertIntel `
    -PluginArgs $pluginArgs -DnsSleep 60 -AcceptTOS -Contact 'you@example.com'

For a Windows scheduled task, save the following as a script and run it with PowerShell under the same Windows account used for setup. The SetEnvironmentVariable line in Install already persists POSHACME_PLUGINS for that account (default %LOCALAPPDATA%\CertIntel\Posh-ACME\Plugins, or %ProgramData%\CertIntel\Posh-ACME\Plugins if you persisted it with the 'Machine' target), so the first line only matters if you installed the plugin somewhere else. Choose a regular schedule, such as twice daily:

$ErrorActionPreference = 'Stop'
if (-not $env:POSHACME_PLUGINS) { $env:POSHACME_PLUGINS = "$env:LOCALAPPDATA\CertIntel\Posh-ACME\Plugins" }
Import-Module Posh-ACME -MinimumVersion 4.7.0 -MaximumVersion 4.999.999 -Force
Set-PAServer LE_PROD
Submit-Renewal -AllAccounts

This renews certificate files when due. Configure a separate deployment step if your application needs certificate import, an IIS binding update or a reload.

Simple IIS certificate setup (Windows)

The download block in Install also placed Get-CertIntelIisCertificate.ps1 in the plugin folder. It is a one-shot helper for a single IIS site. It requests a production Let's Encrypt certificate through the CertIntel plugin, imports it into LocalMachine\My, and creates or updates the site's HTTPS binding (*:443:<hostname> with SNI). It does not create a renewal task, so you must renew and rebind before the certificate expires (for example with the scheduled script above plus your own binding update).

Run it in Windows PowerShell 5.1 as administrator after installing the plugin. Either edit the param block at the top (SiteName, Domain, Email, DelegationId, CnameTarget; leave ApiBase unless you run a private CertIntel) or pass the same names as parameters:

& "$env:POSHACME_PLUGINS\Get-CertIntelIisCertificate.ps1" -SiteName 'Default Web Site' `
    -Domain 'www.example.com' -Email 'you@example.com' `
    -DelegationId '11111111-1111-1111-1111-111111111111' `
    -CnameTarget 'YOUR-LABEL.dns01.certin.tel'

It prompts for the delegation update secret without echo (or accepts -ApiKey as a SecureString). It refuses to change IIS if the hostname is already bound to another site, if an existing binding is not All Unassigned/443/SNI/local store, or if issuance fails, and finishes with Done: https://<domain> is bound to IIS site '<site>' using certificate <thumbprint>.

Optional CertIntel reporting

The DNS plugin does not send check-ins or renewal reports. Use the separate Posh-ACME reporting wrappers if you want those reports. They accept -PluginDirectory for custom plugin discovery and -DirectoryUrl LE_PROD (or LE_STAGE) to select the CA. The issuance wrapper takes -Plugin CertIntel -PluginArgs $pluginArgs -DnsSleep 60. Schedule the renewal wrapper in place of the direct command above; it processes the current account's orders. Set the same account, configuration root and vault environment used at setup. Its write-scoped reporting key is separate from the DNS plugin's delegation credential.

Troubleshooting

Symptom Check
CertIntel is missing from Get-PAPlugin Set POSHACME_PLUGINS before importing Posh-ACME with -Force; verify the renewal account can read CertIntel.ps1.
No delegation matches Use the exact certificate name without *. or _acme-challenge.; subdomains need their own mapping.
API 401/403 Check the update secret, delegation status and API endpoint.
Saved secret cannot be decrypted Run as the setup account on the same Windows machine, or restore access to the configured alternative encryption key/vault.
DNS validation fails Verify the permanent CNAME and public TXT answers; increase -DnsSleep if necessary.
Two TXT values already exist Confirm no other client is issuing; remove stale values only after confirming no issuance is active.

See shared delegation ownership guidance.