Automating wildcard SSL certificates with win-acme and Azure DNS

How to prepare for shorter 45-day SSL certificate lifetimes by automating wildcard TLS renewals on Windows with win-acme, Azure DNS CNAME delegation, managed identity and optional Key Vault storage.

Technically, these are TLS certificates. “SSL certificate” remains the phrase most people search for, so this guide uses both terms.

For years, certificate administration was treated as an annual task. Someone ordered a certificate, copied a PFX file to a few servers, updated a spreadsheet, and hoped the reminder email arrived before the next outage.

That operating model is disappearing.

Public TLS certificate validity has already started shrinking. The CA/Browser Forum schedule reduced the maximum from roughly one year to 200 days on March 15, 2026. It falls to 100 days on March 15, 2027 and reaches 47 days on March 15, 2029.

Let’s Encrypt has separately announced a plan for its default classic profile to issue 45-day certificates from February 16, 2028. That distinction matters: “45-day SSL certificates” is a useful search phrase, but 45 days is not the universal industry maximum. The CA/Browser Forum endpoint is 47 days; Let’s Encrypt’s default profile is planned to become 45 days.

Either way, the operational conclusion is the same. Manual renewal is no longer a credible production process.

Certificate issue dateMaximum public TLS validity
Before March 15, 2026398 days
March 15, 2026 to March 14, 2027200 days
March 15, 2027 to March 14, 2029100 days
March 15, 2029 onward47 days

The CA/Browser Forum ballot also reduces how long certificate authorities may reuse domain-control validation data. Shorter certificates are not just about renewing more often. They force organisations to make validation, issuance, storage, deployment and monitoring reliably repeatable.

This guide shows one practical way to do that for a mixed DNS estate using Windows, win-acme, Azure DNS, managed identity and optional Azure Key Vault storage.

The problem

The environment contained six domains:

DomainAuthoritative DNS provider
stand.co.ukAzure DNS
mygoggles.comAzure DNS
cupsandshare.comAzure DNS
owlmag.comFasthosts
notedyears.co.ukFasthosts
dryerpaint.comFasthosts

Every domain needed a wildcard certificate for applications built with different technology stacks. The ACME client would run on Windows. Some zones already lived in Azure DNS, while others remained at a registrar whose DNS platform did not have a native win-acme plugin available for this design.

There were several obvious options:

  • migrate all six authoritative DNS zones to Azure;
  • continue creating TXT records manually;
  • write and maintain a custom registrar API integration;
  • run a public acme-dns service;
  • or automate only the small part of DNS needed for certificate validation.

The last option produced the cleanest security and operating model.

The design in one sentence

Create one validation-only Azure DNS zone, permanently point each domain’s _acme-challenge name to it with a CNAME, and allow win-acme to create only temporary TXT records in that isolated zone using an Azure managed identity.

Architecture diagram showing win-acme requesting a certificate, source DNS zones delegating ACME challenge names to Azure DNS, and the certificate being stored in Key Vault, PFX, PEM or the Windows certificate store
One Windows certificate host, one isolated Azure validation zone, and one permanent CNAME per domain.

This is not a nameserver migration. The production DNS for websites, email, SPF, DKIM, MX, APIs and other services stays where it is. Only the ACME validation name is delegated.

It is also not a custom DNS server. Azure operates the authoritative DNS infrastructure. There is no VM listening on port 53, no DNS database to back up, and no public DNS API for you to patch.

The four components people often confuse

ACME terminology becomes confusing when a reseller provides an account ID, MAC ID, MAC key and server URL. The easiest way to understand the design is to separate four responsibilities.

Sequence diagram showing win-acme, the ACME certificate authority, Azure DNS and certificate storage working together during DNS-01 validation and certificate issuance
win-acme requests and stores the certificate, the ACME CA validates domain control, Azure DNS publishes the temporary TXT token, and Key Vault or file storage receives the result.

1. win-acme is the ACME client

wacs.exe runs in your environment. It generates the private key, creates the certificate order, responds to the validation challenge, downloads the issued certificate, stores it, and records how to renew it.

2. Let’s Encrypt, Sectigo and DigiCert are certificate authorities

The ACME server accepts the order, produces a challenge, checks public DNS, and signs the certificate after validation succeeds.

win-acme defaults to Let’s Encrypt unless you configure another ACME directory with --baseuri.

3. Azure DNS publishes the DNS-01 token

Azure DNS is not issuing the certificate. It temporarily serves the TXT value that proves control of the requested domain.

4. Key Vault, PFX, PEM or the Windows certificate store receives the result

Storage and deployment are separate from validation. Azure Key Vault is useful when Azure services or deployment pipelines need a central certificate location, but it is optional.

The complete flow is therefore:

win-acme -> requests certificate -> ACME certificate authority
win-acme -> creates TXT token -> Azure DNS validation zone
ACME CA -> queries public DNS -> follows CNAME and reads TXT
win-acme -> stores certificate -> Key Vault / PFX / PEM / Windows store

Why wildcard certificates force DNS-01

An HTTP-01 challenge can validate explicit names such as app.example.com, but it cannot validate:

*.example.com

Wildcard identifiers require DNS-01. The certificate authority asks for a TXT record below:

_acme-challenge.example.com

Also remember that a wildcard does not cover the root domain. A certificate containing only *.example.com covers api.example.com, but not example.com itself. To cover both, request both identifiers:

example.com,*.example.com

The apex and wildcard authorisations can produce two TXT values at the same owner name. An automated DNS plugin must preserve both values during validation rather than replacing one with the other.

Why CNAME delegation is useful

Suppose owlmag.com remains at Fasthosts, but the automation identity is allowed to modify only an Azure validation zone.

Create this record once at Fasthosts:

_acme-challenge.owlmag.com. CNAME owlmag-com.acme-validation.stand.co.uk.

During issuance, win-acme creates this temporary Azure DNS record:

owlmag-com.acme-validation.stand.co.uk. TXT "<ACME challenge token>"

When the certificate authority queries _acme-challenge.owlmag.com, public DNS returns the CNAME. A compatible validator follows that CNAME and reads the TXT value from Azure.

win-acme calls this DNS substitution. Its AllowDnsSubstitution setting is enabled by default, and the DNS validation documentation explains that the same technique can be used with any compatible DNS plugin, not only with acme-dns.

This creates a strong security boundary:

  • win-acme does not receive permission to edit production DNS zones;
  • the managed identity can change only one validation zone;
  • the source DNS providers remain unchanged;
  • each domain requires only one permanent CNAME;
  • temporary TXT records are created and removed automatically.

This is one architecture, not the only architecture

A good certificate automation design should explain when not to use its own pattern.

Decision tree comparing manual DNS, registrar plugins, custom DNS scripts, Azure DNS CNAME delegation and self-hosted acme-dns for certificate automation
Choose the smallest automation boundary that fits your DNS estate and security policy.

Use the registrar’s native API when the provider has a stable API and a native win-acme plugin. Scope the credential as narrowly as the provider allows.

Migrate all DNS when centralisation has broader value. Moving every authoritative zone to Azure, Cloudflare, Route 53 or another API-capable provider can simplify long-term DNS operations, but it also turns certificate automation into a full DNS migration project.

Use a custom win-acme DNS script when the registrar has an API but no plugin. This is flexible, but your team owns authentication, retries, error handling, secret rotation, record cleanup and future API changes.

Run acme-dns when cloud neutrality is more important than managed infrastructure. It is designed for this class of problem, but self-hosting it means operating security-sensitive public infrastructure: authoritative UDP and TCP DNS on port 53, an update API, TLS, persistent storage, monitoring, backup, patching and availability.

Use manual DNS only as a proof of concept. Manual TXT records are useful for testing an ACME account, CA entitlement, wildcard order and output format. They are not an automated renewal strategy, especially as certificates move toward 45 days.

Prerequisites

For the implementation below, you need:

  • an Azure subscription;
  • one public parent domain already hosted in Azure DNS, such as stand.co.uk;
  • a Windows Azure VM on which win-acme will run;
  • administrative access to that VM;
  • permission to create one CNAME in each source zone;
  • a public ACME account, or commercial EAB credentials when required;
  • Azure Key Vault only when you want central certificate storage;
  • sufficient commercial wildcard entitlements or slots when using a paid CA service.

Start with one domain. Confirm the complete path from CNAME to TXT to certificate issuance before onboarding the rest.

Build the validation boundary

1. Create one Azure DNS child zone

Use a child of a public domain you already own and host in Azure. In this example:

Parent zone:     stand.co.uk
Validation zone: acme-validation.stand.co.uk

In the Azure portal, open DNS zones, select stand.co.uk, select + Child zone, enter acme-validation, and create the zone. Azure creates the child zone and adds the corresponding NS delegation in the parent.

Verify it from PowerShell:

Resolve-DnsName `
    -Name "acme-validation.stand.co.uk" `
    -Type SOA `
    -Server 1.1.1.1

The response should identify an Azure DNS authoritative server.

You do not need an A record, a public application endpoint, a VM dedicated to DNS, or inbound ports 53, 80 or 443 for this zone.

2. Add one permanent CNAME per certificate domain

Use a unique target label for each source domain. Unique labels make permissions, logs and troubleshooting clearer.

Certificate domainPermanent CNAME ownerAzure validation target
stand.co.uk_acme-challenge.stand.co.ukstand-co-uk.acme-validation.stand.co.uk
mygoggles.com_acme-challenge.mygoggles.commygoggles-com.acme-validation.stand.co.uk
cupsandshare.com_acme-challenge.cupsandshare.comcupsandshare-com.acme-validation.stand.co.uk
owlmag.com_acme-challenge.owlmag.comowlmag-com.acme-validation.stand.co.uk
notedyears.co.uk_acme-challenge.notedyears.co.uknotedyears-co-uk.acme-validation.stand.co.uk
dryerpaint.com_acme-challenge.dryerpaint.comdryerpaint-com.acme-validation.stand.co.uk

At a DNS provider that expects relative names, the owlmag.com record typically looks like this:

Type:   CNAME
Name:   _acme-challenge
Target: owlmag-com.acme-validation.stand.co.uk.
TTL:    300, or the lowest sensible value available

Delete old _acme-challenge TXT records before creating the CNAME. DNS standards do not permit a CNAME to coexist with TXT, A, AAAA or other record types at the same owner name.

Do not create the target TXT values yourself. Empty Azure target names are expected until win-acme starts validation.

Verify every source record publicly:

$Domains = @(
    "stand.co.uk",
    "mygoggles.com",
    "cupsandshare.com",
    "owlmag.com",
    "notedyears.co.uk",
    "dryerpaint.com"
)

foreach ($Domain in $Domains) {
    Write-Host "`nChecking $Domain" -ForegroundColor Cyan

    Resolve-DnsName `
        -Name "_acme-challenge.$Domain" `
        -Type CNAME `
        -Server 1.1.1.1 `
        -DnsOnly
}

Do not continue until each result points to the intended Azure validation target.

3. Enable managed identity on the Windows VM

A managed identity lets win-acme obtain Microsoft Entra tokens without storing an Azure client secret on disk, in a script, or in Task Scheduler.

For one long-lived certificate VM, a system-assigned identity is straightforward:

Azure VM -> Identity -> System assigned -> On -> Save

4. Grant access only to the validation zone

Open the validation zone IAM blade and assign:

Role:   DNS Zone Contributor
Member: The win-acme VM managed identity
Scope:  acme-validation.stand.co.uk only

DNS Zone Contributor can manage DNS zones and record sets but cannot control who has access to them. Scoping it to the isolated validation zone is the key least-privilege decision in this design.

A CLI equivalent is:

$VmResourceGroup  = "rg-cert-automation"
$VmName           = "vm-win-acme"
$DnsResourceGroup = "rg-public-dns"
$ValidationZone   = "acme-validation.stand.co.uk"

$PrincipalId = az vm identity assign `
    --resource-group $VmResourceGroup `
    --name $VmName `
    --query principalId `
    --output tsv

$DnsZoneId = az network dns zone show `
    --resource-group $DnsResourceGroup `
    --name $ValidationZone `
    --query id `
    --output tsv

az role assignment create `
    --assignee-object-id $PrincipalId `
    --assignee-principal-type ServicePrincipal `
    --role "DNS Zone Contributor" `
    --scope $DnsZoneId

Allow several minutes for Azure RBAC propagation before testing.

Install win-acme and the Azure plugins

Download release binaries from the official win-acme releases page. Do not clone the repository for a normal installation.

Because Azure DNS and Azure Key Vault are separate plugins, download the pluggable x64 build rather than the smaller trimmed build. The Azure DNS plugin documentation confirms that the plugin is a separate download, must be unpacked beside wacs.exe, requires the pluggable release, and may need its downloaded DLLs unblocked. The Key Vault plugin documentation follows the same separate-download pattern for certificate storage.

Download assets with the same build number:

win-acme.v<build>.x64.pluggable.zip
plugin.validation.dns.azure.v<same-build>.zip
plugin.store.keyvault.v<same-build>.zip       # optional

For example, the tested build used:

win-acme.v2.2.9.1701.x64.pluggable.zip
plugin.validation.dns.azure.v2.2.9.1701.zip
plugin.store.keyvault.v2.2.9.1701.zip

Never mix plugin versions with a different wacs.exe build.

Create a permanent folder from an elevated PowerShell session:

$InstallRoot = "$env:ProgramFiles\win-acme"
New-Item -Path $InstallRoot -ItemType Directory -Force | Out-Null

Extract the main package and plugins into that same directory:

$DownloadRoot = "$env:USERPROFILE\Downloads"
$Version      = "2.2.9.1701"   # Replace with your matching build

Expand-Archive `
    -LiteralPath "$DownloadRoot\win-acme.v$Version.x64.pluggable.zip" `
    -DestinationPath $InstallRoot `
    -Force

Expand-Archive `
    -LiteralPath "$DownloadRoot\plugin.validation.dns.azure.v$Version.zip" `
    -DestinationPath $InstallRoot `
    -Force

Expand-Archive `
    -LiteralPath "$DownloadRoot\plugin.store.keyvault.v$Version.zip" `
    -DestinationPath $InstallRoot `
    -Force

Release filenames can vary. Use the exact filenames you downloaded.

Unblock downloaded DLL files

Windows may preserve an internet-zone marker on downloaded assemblies. From the installation folder, run:

Set-Location "$env:ProgramFiles\win-acme"

Get-ChildItem -Path . -Recurse -File -Filter "*.dll" |
    Unblock-File

Unblock-File .\wacs.exe

That one pipeline solves a surprisingly common reason for plugins not appearing.

Verify the installation:

.\wacs.exe --version
.\wacs.exe --verbose

The verbose startup output should report the Azure DNS plugin and, when installed, the Key Vault plugin. The validation menu should now include an option similar to:

[dns] Create verification records in Microsoft Azure DNS

Choose the certificate authority deliberately

The DNS architecture is independent of the issuing CA. The same Azure validation pattern can be used with Let’s Encrypt or with a commercial ACME service that follows CNAMEs during DNS-01 validation.

Let’s Encrypt provides publicly trusted DV certificates at no charge and is the default ACME endpoint in win-acme. Its certificates are not weaker merely because they are free. The private key and TLS configuration determine the cryptographic strength.

A commercial service may add technical support, account governance, OV or EV products, contractual terms, enterprise policy alignment and warranty programmes. Those are commercial and operational differences, not automatically stronger encryption for an equivalent DV certificate.

In one commercial implementation, SSL Dragon supplied:

ACME directory/server URL
EAB MAC ID
EAB MAC key
Account/subscription information

With win-acme, the usual mapping is:

MAC ID  -> --eab-key-identifier
MAC key -> --eab-key
Server  -> --baseuri

The win-acme --account argument is a local profile name. It is not normally the reseller’s account ID.

Register once in a controlled administrative session:

Set-Location "$env:ProgramFiles\win-acme"

$AcmeServer     = "https://your-commercial-acme-directory.example/"
$AccountProfile = "sectigo-production"
$EabKid         = "<EAB-MAC-ID>"
$EabKey         = "<BASE64URL-EAB-MAC-KEY>"
$Email          = "pki@example.com"

.\wacs.exe `
    --register `
    --baseuri $AcmeServer `
    --account $AccountProfile `
    --eab-key-identifier $EabKid `
    --eab-key $EabKey `
    --emailaddress $Email `
    --accepttos `
    --verbose

Treat the EAB key as a secret. Do not commit it to source control, place it in the long-lived certificate command, or expose it in screenshots.

When starting win-acme later, verify the connection banner. If it shows:

https://acme-v02.api.letsencrypt.org/

then you are using Let’s Encrypt, not the commercial endpoint.

Before onboarding all domains, test one wildcard order and confirm that the specific commercial ACME validator follows the CNAME. win-acme supports the substitution on the client side, but commercial CA behaviour should be verified rather than assumed.

Create the first certificate interactively

The interactive wizard is the best way to understand the options and create a correct renewal definition.

Start win-acme against the intended ACME service:

Set-Location "$env:ProgramFiles\win-acme"

.\wacs.exe `
    --baseuri "https://your-commercial-acme-directory.example/" `
    --account "sectigo-production" `
    --verbose

For Let’s Encrypt, omit the commercial --baseuri and account profile when the default account is intended.

Choose options by their descriptions because menu numbers can change between builds.

For the certificate source, choose Create certificate (full options) and then Manual input. For the first test, enter:

owlmag.com,*.owlmag.com

Choose a single certificate/order. For validation, choose:

[dns] Create verification records in Microsoft Azure DNS

Do not choose manual DNS, acme-dns, or your own script for this architecture.

Choose managed identity and provide:

DNS subscription ID:  <subscription containing the validation zone>
DNS resource group:   <resource group containing the validation zone>
Azure hosted zone:    acme-validation.stand.co.uk

Because DNS substitution is enabled, win-acme resolves:

_acme-challenge.owlmag.com

to:

owlmag-com.acme-validation.stand.co.uk

It then creates the TXT value in the Azure zone.

For private keys, RSA remains the broadest compatibility choice. EC produces smaller keys and signatures but should be tested against every consuming platform. Pick based on application support, not fashion.

Choose the certificate destinations your applications actually consume:

  • Azure Key Vault for central Azure-oriented distribution;
  • PFX for Windows and applications expecting PKCS#12;
  • PEM for Nginx, Apache, containers and Unix-style services;
  • Windows Certificate Store for local Windows workloads.

A certificate being present in Key Vault does not automatically update every application. Certificate issuance and certificate deployment are separate automation paths.

Turn the wizard into a renewal standard

Once the first interactive order works, the next job is not simply to copy a command into a notes file. It is to turn the working choices into a controlled renewal standard that can survive staff changes, certificate lifetime reductions and production incidents.

For each certificate, document the decisions that matter:

  • which ACME endpoint and account profile is being used;
  • whether the order contains the apex, the wildcard, or both;
  • which Azure DNS validation zone receives the temporary TXT record;
  • whether the VM uses managed identity or a scoped service principal;
  • where the certificate is stored after issuance;
  • which application or platform consumes the renewed certificate;
  • how renewal success is monitored.

The repeatable command should be built from those decisions, tested against one domain, and then adapted carefully for the rest of the estate. This is also the point where mistakes become expensive: the wrong ACME endpoint can issue from the wrong CA, the wrong hosted zone can make validation fail, and the wrong storage choice can leave applications still serving an old certificate.

For production estates, LDS normally turns the working wizard result into a reviewed renewal pattern with named certificates, protected secrets, consistent logging and a deployment route for each consuming service. That gives you automation without leaving a fragile one-off command as the only source of truth.

Store the certificate in Azure Key Vault

The Key Vault plugin is optional. Install it only when Key Vault is part of the consumption model.

When using Azure RBAC, assign the win-acme managed identity:

Role:  Key Vault Certificates Officer
Scope: The specific Key Vault

That role can perform certificate operations but cannot manage permissions. Applications consuming the certificate should receive separate read-only roles appropriate to their requirements.

Key Vault certificate names should use letters, numbers and hyphens. For example:

owlmag-com-wildcard

rather than:

*.owlmag.com

When the vault uses a firewall or private endpoint, the VM still needs a valid DNS and network path to the Key Vault data-plane endpoint. Correct RBAC does not override a blocked network route.

The official win-acme Key Vault plugin reference documents the unattended arguments:

--store keyvault --vaultname MyVault --certificatename MyCertificate

Plan the post-renewal deployment step

Issuing a certificate is only half the job. The consuming service still needs to receive it, load it and prove that it is serving the new expiry date.

The post-renewal step is where many certificate automation projects either become reliable or quietly become another hidden risk. Depending on the estate, this step may need to:

  • write a deployment log with the common name, thumbprint and renewal time;
  • copy PFX or PEM files into the correct application location;
  • import or update an Azure Key Vault certificate version;
  • restart a Windows service, reverse proxy, container or web server;
  • trigger a deployment pipeline;
  • notify monitoring when deployment succeeds or fails.

For a proof of concept, a small logging script is enough to confirm that win-acme can call a post-renewal hook. For production, the script should be treated like deployment code: versioned, tested under the same account used by Task Scheduler, explicit about failures, and written so that it returns a non-zero exit code when deployment fails.

This is a natural consulting checkpoint. LDS can review the renewal route, identify every certificate consumer, and build the post-renewal automation so the certificate is not just issued, but actually in use by the right service.

Prepare win-acme for 45-day SSL certificates

This is the part many current ACME tutorials miss.

win-acme creates a daily scheduled task and checks whether each renewal is due. At the time of writing, its documented default ScheduledTask.RenewalDays value is 55 days. That is reasonable for a 90-day certificate, but it cannot be treated as a permanent setting in a 45-day world.

win-acme also supports ACME Renewal Information (ARI). Keep server-directed scheduling enabled:

ScheduledTask.RenewalDisableServerSchedule = false

Before your CA changes certificate profiles, review these settings under the ScheduledTask section of settings.json:

RenewalDays
RenewalDaysRange
RenewalMinimumValidDays
RenewalDisableServerSchedule

For a 45-day certificate profile, a client without useful ARI guidance should generally renew around two-thirds of the way through the certificate lifetime, approximately day 30, rather than waiting until only a few days remain. Let’s Encrypt explicitly recommends ARI or a schedule compatible with 45-day certificates.

Do not merely change the number and assume the job is finished. Test the complete process:

  1. win-acme decides the certificate is due.
  2. The Azure TXT record is created.
  3. The CA follows the CNAME and validates it.
  4. The new certificate is stored.
  5. The application receives and loads it.
  6. The temporary TXT value is removed.
  7. Monitoring confirms the new expiry date.

Shorter lifetimes mean deployment and observability failures become more important than certificate-order failures.

How automatic renewal works after the first setup

Once the permanent CNAME exists, every renewal follows the same path:

  1. The Windows scheduled task starts win-acme.
  2. win-acme identifies certificates that are due or follows the ACME server’s renewal guidance.
  3. It requests a new order from the selected CA.
  4. The CA returns DNS-01 challenge tokens.
  5. win-acme resolves _acme-challenge.<domain> and follows the permanent CNAME.
  6. The Azure plugin authenticates with the VM managed identity.
  7. It creates one or more TXT values in acme-validation.stand.co.uk.
  8. win-acme performs public DNS pre-validation.
  9. The CA queries public DNS and validates the token.
  10. The CA issues the certificate.
  11. win-acme removes the temporary TXT values.
  12. It stores the certificate in Key Vault, PFX, PEM or another selected destination.
  13. The installation script deploys or reloads the consuming service.
  14. Logs and renewal state are updated.

There is no repeated Fasthosts change, no nameserver migration, and no self-hosted authoritative DNS service.

Monitor the automation, not just the expiry date

A scheduled task existing is not proof that renewal works.

Inspect the task:

Get-ScheduledTask |
    Where-Object TaskName -Like "*win-acme*" |
    Format-List TaskName, State, TaskPath

List renewals:

Set-Location "$env:ProgramFiles\win-acme"
.\wacs.exe --list

Test one renewal deliberately:

.\wacs.exe `
    --baseuri "https://your-commercial-acme-directory.example/" `
    --account "sectigo-production" `
    --renew `
    --friendlyname "owlmag.com wildcard" `
    --force `
    --verbose

Use forced production renewals sparingly. Prefer a staging endpoint when the CA provides one.

Monitor at least:

  • Task Scheduler exit status;
  • win-acme log errors;
  • certificate expiry from the application endpoint;
  • Key Vault certificate versions when used;
  • post-renewal deployment results;
  • whether applications have actually loaded the new certificate;
  • failed DNS validation or Azure RBAC operations.

The win-acme documentation places logs beneath a path derived from:

%ProgramData%\win-acme\<ACME base URI>\Log

Back up the account, renewal and secret state securely. Treat cached certificate material as sensitive because it may contain private keys.

Troubleshooting the failure modes that matter

The Azure DNS validation option is missing

Check these in order:

  • you installed the pluggable build;
  • the Azure plugin build exactly matches wacs.exe;
  • the ZIP was extracted beside wacs.exe, not into an extra nested directory;
  • DLL files were unblocked;
  • win-acme was restarted after extraction.

Run:

Get-ChildItem -Path "$env:ProgramFiles\win-acme" -Recurse -File -Filter "*.dll" |
    Unblock-File

& "$env:ProgramFiles\win-acme\wacs.exe" --verbose

Managed identity authentication is unavailable

Confirm that:

  • win-acme is running on the Azure VM, not on your local workstation;
  • managed identity is enabled on that VM;
  • the identity has DNS Zone Contributor on the validation zone;
  • the VM can reach Microsoft Entra and Azure Resource Manager endpoints;
  • Azure RBAC has had time to propagate.

When win-acme runs outside Azure, use a scoped service principal instead of --azureusemsi.

Azure returns 403 Forbidden

DNS validation and Key Vault use separate permissions.

For DNS, verify DNS Zone Contributor on acme-validation.stand.co.uk.

For Key Vault, verify Key Vault Certificates Officer on the intended vault and confirm the network path is open.

win-acme reports “No TXT records found”

Check the DNS chain explicitly:

Resolve-DnsName `
    -Name "_acme-challenge.owlmag.com" `
    -Type CNAME `
    -Server 1.1.1.1

Resolve-DnsName `
    -Name "owlmag-com.acme-validation.stand.co.uk" `
    -Type TXT `
    -Server 1.1.1.1

Typical causes include:

  • an old TXT record preventing the CNAME from existing;
  • a DNS control panel appending the source domain twice;
  • the wrong CNAME target;
  • propagation delay;
  • private split-DNS overriding the public answer;
  • the wrong Azure hosted zone configured in win-acme;
  • DNSSEC errors;
  • Azure permissions preventing TXT creation.

Never select “ignore and continue” while authoritative DNS still lacks the expected token.

The certificate was issued by Let’s Encrypt instead of Sectigo or DigiCert

win-acme used its default ACME endpoint because the commercial --baseuri or account profile was not selected.

Check the startup banner and inspect the issuer after issuance:

Get-PfxCertificate "C:\path\to\certificate.pfx" |
    Format-List Subject, Issuer, NotBefore, NotAfter, Thumbprint

The wildcard certificate does not cover the root domain

Request both names:

example.com,*.example.com

DNS is correct but the commercial CA rejects the order

Check:

  • the account has sufficient wildcard slots or domain entitlement;
  • the requested domain is assigned to the commercial account;
  • the CA supports CNAME following for DNS-01;
  • CAA records permit the selected CA;
  • the EAB account was registered against the same base URI used for issuance.

Interactive issuance works but Task Scheduler fails

The scheduled task normally runs as SYSTEM, not as your interactive administrator account. Look for:

  • scripts or output folders readable only by your user;
  • dependencies on mapped drives or a user profile;
  • proxy settings configured only for the interactive account;
  • certificates left under Desktop or Downloads;
  • application reload commands requiring permissions not granted to SYSTEM;
  • an Azure service principal used interactively instead of the VM managed identity.

Use absolute local paths and test the deployment under the same security context used by the scheduled task.

Security decisions worth keeping

Keep the validation zone empty except for ACME. Do not place websites, mail or production service records in acme-validation.stand.co.uk.

Keep one unique target per domain. This improves auditability and avoids confusing record collisions.

Scope Azure permissions narrowly. Grant DNS write access only to the validation zone and Key Vault certificate access only to the intended vault.

Separate issuer and consumer identities. win-acme needs permission to create or import. Applications usually need read access only.

Minimise wildcard private-key distribution. A wildcard key copied to many unrelated machines creates a broad blast radius. Prefer central TLS termination or tightly controlled Key Vault consumption where the architecture allows it.

Monitor successful deployment, not merely successful issuance. The browser does not care that the CA issued a certificate if the application is still serving the old one.

Document recovery. Record how to rebuild the VM, restore win-acme state, reassign managed identity permissions, rotate EAB credentials, revoke and replace certificates, and identify every consumer of each private key.

The value of this design

The most useful part of the solution is not the choice between Let’s Encrypt, Sectigo or DigiCert. It is the separation of responsibilities:

The original DNS provider remains authoritative for production.
Azure DNS is authoritative only for ACME validation targets.
win-acme controls only temporary TXT records.
Managed identity removes a stored Azure API credential.
The ACME certificate authority can be changed independently.
Key Vault and deployment scripts control distribution independently.

For six domains, the estate needs six certificate renewal definitions and six permanent CNAMEs, but only one validation zone, one narrowly scoped managed identity, one win-acme installation and one monitoring model.

That is a much smaller change than migrating every DNS zone. It is also much less infrastructure than operating a public acme-dns service.

Most importantly, it is designed for the future. When public TLS certificates move from one year toward 45-day SSL certificates, the organisations that avoid outages will not be the ones with the best calendar reminders. They will be the ones whose validation, issuance, deployment and monitoring already run without a human in the loop.

Related insights

Related insights

Security · 7 min read

Backup and recovery gaps SMEs often miss in Azure

Security, backup and recovery checks for Azure-backed websites and web systems before a small issue becomes an outage.

Read next

Azure · 10 min read

Docker application hosting on Azure with Nginx Proxy Manager

A practical single-VM Docker hosting pattern for multiple websites, APIs, dashboards and internal tools using Ubuntu, Docker Compose, Nginx Proxy Manager and Azure network controls.

Read next

Next step

Need help applying this?

Tell LDS about the website, app, portal or integration you are planning.