From dd53600dfcb9206122f15ff126a91f0a352fa2d1 Mon Sep 17 00:00:00 2001 From: Slawomir Koszewski Date: Thu, 21 May 2026 16:48:21 +0200 Subject: [PATCH] fix: add ACME schedule environment variable for Azure Function deployment --- README.md | 130 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 126 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 6b451d0..368d069 100644 --- a/README.md +++ b/README.md @@ -80,6 +80,7 @@ All configuration is via environment variables. CLI flags override env vars when | `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_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. | ### Azure Authentication @@ -95,15 +96,136 @@ For sovereign clouds (Azure Government, Azure China), set `AZURE_AUTHORITY_HOST` ## Azure Function -The package includes an Azure Functions v4 timer trigger that runs the provisioner daily at 02:00 UTC. To deploy, point the function app at this package's entry point and configure the environment variables above as application settings. +The package includes an Azure Functions v4 timer trigger that runs the provisioner daily at 02:00 UTC. The function app requires a Managed Identity with the following RBAC assignments: | Scope | Role | |---|---| -| Key Vault | Key Vault Certificates Officer | -| Key Vault | Key Vault Secrets Officer | -| DNS Zone(s) | DNS Zone Contributor | +| Key Vault instance | Key Vault Certificates Officer | +| Key Vault instance | Key Vault Secrets Officer | +| Each DNS zone | DNS Zone Contributor | + +> **Note:** The only DNS changes made are temporary `_acme-challenge.` TXT records created during the DNS-01 challenge and deleted immediately after validation. No A, CNAME, or other records are modified. If you require tighter permissions than `DNS Zone Contributor`, create a custom role limited to `Microsoft.Network/dnszones/TXT/write` and `Microsoft.Network/dnszones/TXT/delete`. + +### Deploying with Azure Functions Core Tools + +**Prerequisites:** [Azure CLI](https://learn.microsoft.com/en-us/cli/azure/install-azure-cli), [Azure Functions Core Tools v4](https://learn.microsoft.com/en-us/azure/azure-functions/functions-run-local#install-the-azure-functions-core-tools), Node.js 24. + +**1. Log in to Azure** + +```sh +az login +``` + +**2. Create a resource group and storage account** (skip if they already exist) + +```sh +az group create --name --location + +az storage account create \ + --name \ + --resource-group \ + --location \ + --sku Standard_LRS +``` + +**3. Create the Function App** + +```sh +az functionapp create \ + --name \ + --resource-group \ + --storage-account \ + --consumption-plan-location \ + --runtime node \ + --runtime-version 24 \ + --functions-version 4 +``` + +**4. Assign a system-assigned Managed Identity** + +```sh +az functionapp identity assign \ + --name \ + --resource-group +``` + +Note the `principalId` from the output — you will need it in the next step. + +**5. Grant RBAC roles to the Managed Identity** + +```sh +# Key Vault Certificates Officer +az role assignment create \ + --assignee \ + --role "Key Vault Certificates Officer" \ + --scope /subscriptions//resourceGroups//providers/Microsoft.KeyVault/vaults/ + +# Key Vault Secrets Officer +az role assignment create \ + --assignee \ + --role "Key Vault Secrets Officer" \ + --scope /subscriptions//resourceGroups//providers/Microsoft.KeyVault/vaults/ + +# Option A — per zone (minimum permission, repeat for each managed DNS zone) +az role assignment create \ + --assignee \ + --role "DNS Zone Contributor" \ + --scope /subscriptions//resourceGroups//providers/Microsoft.Network/dnszones/ + +# Option B — per resource group (convenient when all DNS zones are in one group) +az role assignment create \ + --assignee \ + --role "DNS Zone Contributor" \ + --scope /subscriptions//resourceGroups/ +``` + +**6. Configure application settings** + +```sh +az functionapp config appsettings set \ + --name \ + --resource-group \ + --settings \ + "ACME_KEYVAULT_URL=https://.vault.azure.net" \ + "ACME_SUBSCRIPTION_ID=" \ + "ACME_RESOURCE_GROUPS=" \ + "ACME_CONTACT_EMAIL=" +``` + +**7. Build and deploy** + +```sh +npm run build +func azure functionapp publish +``` + +### Local testing + +Create a `local.settings.json` file at the project root (it is gitignored): + +```json +{ + "IsEncrypted": false, + "Values": { + "AzureWebJobsStorage": "UseDevelopmentStorage=true", + "FUNCTIONS_WORKER_RUNTIME": "node", + "ACME_KEYVAULT_URL": "https://.vault.azure.net", + "ACME_SUBSCRIPTION_ID": "", + "ACME_RESOURCE_GROUPS": "", + "ACME_CONTACT_EMAIL": "", + "ACME_SCHEDULE": "0 0 2 * * *" + } +} +``` + +Then run: + +```sh +npm run build +func start +``` ## Docker