update: version 0.6.0 and a refactor of parameters.

This commit is contained in:
2026-05-22 14:04:56 +02:00
parent e0f8b1b402
commit 2c481baf39
6 changed files with 310 additions and 121 deletions
+202 -40
View File
@@ -38,59 +38,219 @@ The provisioner discovers domains by scanning Azure DNS zones. Tag a **zone** or
## CLI Usage
```
Commands:
run Scan DNS zones and issue or renew certificates (default)
scan List all domains tagged for ACME management
status Show certificate expiry status for all managed domains
renew Force-renew a certificate for a specific domain
download Download the PEM bundle for a domain from Key Vault
assign-role Assign Key Vault roles to a principal for a domain certificate
All commands share a common set of options. Required options vary per command and are listed in each section below.
Common options:
--keyvault-url <url> Azure KeyVault URL
--subscription-id <id> Azure subscription ID
--resource-group <rg> Resource group to scan (repeatable)
--dns-zone <zone> Restrict to specific DNS zone (repeatable)
--email <email> ACME contact email
--renewal-threshold <days> Days before expiry to renew (default: 30)
--dry-run Show what would be done without making changes
--log-level <level> debug | info | warn | error (default: info)
--output <format> table | json (scan and status commands)
--http <port> Use HTTP-01 challenge on the given port (run and renew only)
### Common options
| Option | Description |
|---|---|
| `--keyvault-name <name>` | Azure Key Vault name (constructs `https://<name>.vault.azure.net`) |
| `--keyvault-url <url>` | Azure Key Vault URL — use for sovereign clouds or non-standard URLs |
| `--subscription-id <id>` | Azure subscription ID |
| `--resource-group <rg>` | Resource group to scan |
| `--email <email>` | ACME contact email |
| `--renewal-threshold <days>` | Days before expiry to trigger renewal (default: 30) |
| `--log-level <level>` | `debug` \| `info` \| `warn` \| `error` (default: `info`) |
---
### Scanning scope
The `run`, `scan`, `status`, and `renew` commands accept optional positional arguments to narrow the scope:
| Arguments | Scope |
|---|---|
| *(none)* | All tagged records in all DNS zones in the subscription |
| `--resource-group <rg>` | All tagged records in all DNS zones in the resource group |
| `--resource-group <rg> <zone>` | All tagged records in the specified DNS zone |
| `--resource-group <rg> <zone> <name> [name...]` | Specific record names in the zone — **bypasses tag filtering** |
Record names are the DNS label within the zone (e.g. `api`, `www`, `@` for apex, `*` for wildcard). FQDNs are constructed by appending the zone name.
---
### `run [zone] [names...]` — issue or renew certificates
Issues or renews certificates for all domains in scope.
**Required:** `--keyvault-name` or `--keyvault-url`, `--subscription-id`, `--email`
```sh
# All tagged records in the subscription
azure-acme-provisioner run \
--keyvault-name myvault \
--subscription-id <subscription-id> \
--email admin@example.com
# All tagged records in a resource group
azure-acme-provisioner run \
--keyvault-name myvault \
--subscription-id <subscription-id> \
--resource-group my-dns-rg \
--email admin@example.com
# All tagged records in a specific zone
azure-acme-provisioner run \
--keyvault-name myvault \
--subscription-id <subscription-id> \
--resource-group my-dns-rg \
--email admin@example.com \
example.com
# Specific records (bypasses tags)
azure-acme-provisioner run \
--keyvault-name myvault \
--subscription-id <subscription-id> \
--resource-group my-dns-rg \
--email admin@example.com \
example.com api www
```
| Option | Description |
|---|---|
| `--http <port>` | Use HTTP-01 challenge on the given port instead of DNS-01 |
| `--pem` | Store certificates as PEM bundle instead of PFX (PKCS#12) |
| `--dry-run` | Show what would be issued or renewed without making changes |
### `scan [zone] [names...]` — list FQDNs in scope
Lists all FQDNs for which certificates will be issued, along with their zone and resource group.
**Required:** `--subscription-id`
```sh
azure-acme-provisioner scan \
--subscription-id <subscription-id> \
--resource-group my-dns-rg
```
| Option | Description |
|---|---|
| `--output table\|json` | Output format (default: `table`) |
### `status [zone] [names...]` — show certificate expiry
Shows the expiry date and days remaining for each domain's certificate.
**Required:** `--keyvault-name` or `--keyvault-url`, `--subscription-id`
```sh
azure-acme-provisioner status \
--keyvault-name myvault \
--subscription-id <subscription-id> \
--resource-group my-dns-rg
```
| Option | Description |
|---|---|
| `--output table\|json` | Output format (default: `table`) |
### `renew [zone] [names...]` — force-renew certificates
Force-renews certificates in scope, bypassing the renewal threshold. Accepts the same scope arguments as `run`.
**Required:** `--keyvault-name` or `--keyvault-url`, `--subscription-id`, `--email`
```sh
# Renew all managed certificates
azure-acme-provisioner renew \
--keyvault-name myvault \
--subscription-id <subscription-id> \
--email admin@example.com
# Renew specific records in a zone (bypasses tags)
azure-acme-provisioner renew \
--keyvault-name myvault \
--subscription-id <subscription-id> \
--resource-group my-dns-rg \
--email admin@example.com \
example.com api www
```
| Option | Description |
|---|---|
| `--http <port>` | Use HTTP-01 challenge on the given port instead of DNS-01 |
| `--pem` | Store certificates as PEM bundle instead of PFX (PKCS#12) |
### `download <fqdn>` — download a certificate
Fetches the certificate bundle from Key Vault and writes it to stdout or a file.
**Required:** `--keyvault-name` or `--keyvault-url`
```sh
# Print to stdout
azure-acme-provisioner download api.example.com --keyvault-name myvault
# Write to a file
azure-acme-provisioner download api.example.com --keyvault-name myvault --output api.example.com.pfx
```
| Option | Description |
|---|---|
| `--output <file>` | Write to file instead of stdout |
### `convert <fqdn>` — convert certificate format
Converts a stored certificate between PFX (PKCS#12) and PEM format in-place.
**Required:** `--keyvault-name` or `--keyvault-url`
```sh
# Convert to PFX (default)
azure-acme-provisioner convert api.example.com --keyvault-name myvault
# Convert to PEM bundle
azure-acme-provisioner convert api.example.com --keyvault-name myvault --pem
```
| Option | Description |
|---|---|
| `--pem` | Convert to PEM bundle instead of PFX (PKCS#12) |
### `assign-role <fqdn>` — assign Key Vault RBAC roles
Assigns **Key Vault Certificate User** and **Key Vault Secrets User** roles to a principal for the certificate and secret objects corresponding to the given FQDN.
**Required:** `--principal-id`, `--principal-type`, `--keyvault-resource-group`, `--keyvault-name` or `--keyvault-url`, `--subscription-id`
```sh
azure-acme-provisioner assign-role api.example.com \
--principal-id <object-id> \
--principal-type ServicePrincipal \
--keyvault-resource-group my-kv-rg \
--keyvault-name myvault \
--subscription-id <subscription-id>
```
| Option | Description |
|---|---|
| `--principal-id <id>` | Object ID of the Azure principal to assign roles to |
| `--principal-type <type>` | `User` \| `Group` \| `ServicePrincipal` (use `ServicePrincipal` for managed identities) |
| `--keyvault-resource-group <rg>` | Resource group that contains the Key Vault |
| `--dry-run` | Show what would be assigned without making changes |
---
### Challenge methods
By default `run` and `renew` use **DNS-01** via Azure DNS (requires DNS Zone Contributor role).
Pass `--http <port>` to use **HTTP-01** instead. The provisioner starts a temporary Express HTTP server on the given port and shuts it down after each certificate is issued. The server must be reachable from the internet on that port for the ACME CA to validate ownership.
Pass `--http <port>` to use **HTTP-01** instead. The provisioner starts a temporary Express HTTP server on the given port and shuts it down after each certificate is issued. The ACME CA always validates against **port 80**, so either pass `--http 80` directly, or run the listener on a non-privileged port and forward port 80 to it externally (reverse proxy, NAT rule, or Docker port mapping).
```sh
# DNS-01 (default)
azure-acme-provisioner run
azure-acme-provisioner run --keyvault-name myvault --subscription-id <id> --email admin@example.com
# HTTP-01 on port 80
azure-acme-provisioner run --http 80
azure-acme-provisioner run --keyvault-name myvault --subscription-id <id> --email admin@example.com --http 80
# HTTP-01 on a non-privileged port (useful behind a reverse proxy or NAT rule)
azure-acme-provisioner run --http 8080
azure-acme-provisioner run --keyvault-name myvault --subscription-id <id> --email admin@example.com --http 8080
```
> **Note:** Binding port 80 requires root privileges or `CAP_NET_BIND_SERVICE`. When running in Docker, map the host port to the container: `-p 80:8080` and pass `--http 8080`.
### Downloading certificates
The `download` command fetches the PEM bundle (private key + certificate + chain) from Key Vault and writes it to stdout or a file:
```sh
# Print to stdout
azure-acme-provisioner download api.example.com
# Write to a file
azure-acme-provisioner download api.example.com --output api.example.com.pem
```
## Configuration
All configuration is via environment variables. CLI flags override env vars when both are provided.
@@ -101,19 +261,21 @@ All configuration is via environment variables. CLI flags override env vars when
|---|---|
| `ACME_KEYVAULT_URL` | Azure Key Vault URL, e.g. `https://myvault.vault.azure.net` |
| `ACME_SUBSCRIPTION_ID` | Azure subscription ID |
| `ACME_RESOURCE_GROUPS` | Comma-separated list of resource groups to scan |
| `ACME_CONTACT_EMAIL` | Contact email registered with the ACME CA |
### Optional
| Variable | Default | Description |
|---|---|---|
| `ACME_DNS_ZONES` | all zones in resource groups | Comma-separated list of DNS zone names to restrict scanning |
| `ACME_RESOURCE_GROUP` | all resource groups in subscription | Resource group to scan |
| `ACME_DNS_ZONE` | all zones in resource group | DNS zone name to restrict scanning |
| `ACME_CERT_NAMES` | tag-based discovery | Comma-separated record names within the zone — bypasses tag filtering |
| `ACME_DIRECTORY_URL` | Let's Encrypt production | ACME directory URL |
| `ACME_RENEWAL_THRESHOLD_DAYS` | `30` | Renew certificates this many days before expiry |
| `ACME_DNS_PROPAGATION_WAIT` | `60` | Maximum seconds to wait for DNS TXT record propagation |
| `ACME_DNS_CHALLENGE_TTL` | `60` | TTL (seconds) for DNS-01 challenge TXT records |
| `ACME_HTTP_PORT` | unset | If set to a positive integer, use HTTP-01 challenge on that port instead of DNS-01 |
| `ACME_CERT_FORMAT` | `pfx` | Certificate storage format: `pfx` (PKCS#12) or `pem` |
| `ACME_LOG_LEVEL` | `info` | Log level: `debug`, `info`, `warn`, `error` |
| `ACME_SCHEDULE` | `0 0 2 * * *` | Azure Function timer schedule (cron expression, 6-field format). Only used when deployed as an Azure Function. |
@@ -225,7 +387,7 @@ az functionapp config appsettings set \
--settings \
"ACME_KEYVAULT_URL=https://<keyvault-name>.vault.azure.net" \
"ACME_SUBSCRIPTION_ID=<subscription-id>" \
"ACME_RESOURCE_GROUPS=<dns-resource-group>" \
"ACME_RESOURCE_GROUP=<dns-resource-group>" \
"ACME_CONTACT_EMAIL=<email>" \
"ACME_SCHEDULE=0 0 2 * * *"
```
@@ -251,7 +413,7 @@ Create `local.settings.json` at the project root (gitignored) and fill in your v
"FUNCTIONS_WORKER_RUNTIME": "node",
"ACME_KEYVAULT_URL": "https://<keyvault-name>.vault.azure.net",
"ACME_SUBSCRIPTION_ID": "<subscription-id>",
"ACME_RESOURCE_GROUPS": "<dns-resource-group>",
"ACME_RESOURCE_GROUP": "<dns-resource-group>",
"ACME_CONTACT_EMAIL": "<email>",
"ACME_SCHEDULE": "0 0 2 * * *"
}
@@ -291,7 +453,7 @@ console.log(result);
## Certificate Storage
Certificates are stored as native Azure Key Vault Certificates (PEM format, `application/x-pem-file`), making them available to Azure App Service, API Management, and other Azure services that integrate with Key Vault.
Certificates are stored as native Azure Key Vault Certificates in **PFX (PKCS#12) format** (`application/x-pkcs12`) by default, making them directly consumable by Azure App Service, API Management, and other Azure services that integrate with Key Vault. Pass `--pem` (or set `ACME_CERT_FORMAT=pem`) to store as a PEM bundle (`application/x-pem-file`) instead. Use the `convert` command to change the format of an already-stored certificate.
ACME account credentials (private key and account URL) are stored as Key Vault Secrets and reused across runs.