kv-cert
Azure Key Vault Certificate Management
Manage SSL certificates in Azure Key Vault and bind them to App Service custom domains.
RBAC Permissions
KV uses RBAC authorization. Key roles:
| Role | Purpose | Role Definition ID |
|---|---|---|
| Key Vault Certificate User | Read certs | db79e9a7-68ee-4b58-9aeb-b90e7c24fcba |
| Key Vault Certificates Officer | Create/import/delete certs | a4417e6f-fecd-4de8-b567-7b0420556985 |
| Key Vault Secrets User | Read secrets (cert private keys are stored as secrets) | 4633458b-17de-408a-b874-0445c86b69e6 |
Grant RBAC via REST API
az role assignment create may fail with MissingSubscription in some CLI versions. Use REST API instead:
# Get current user Object ID
user_oid=$(az ad signed-in-user show --query id -o tsv)
# Get KV resource ID
kv_id=$(az keyvault show --name <kv-name> --resource-group <rg> --query id -o tsv)
# Grant Key Vault Certificates Officer
az rest --method PUT \
--url "https://management.azure.com${kv_id}/providers/Microsoft.Authorization/roleAssignments/$(python -c 'import uuid; print(uuid.uuid4())')?api-version=2022-04-01" \
--body "{
\"properties\": {
\"roleDefinitionId\": \"/subscriptions/<sub-id>/providers/Microsoft.Authorization/roleDefinitions/a4417e6f-fecd-4de8-b567-7b0420556985\",
\"principalId\": \"${user_oid}\",
\"principalType\": \"User\"
}
}"
RBAC propagation takes 1-5 minutes.
Grant App Service WebApp SP access to KV
App Service reads KV certs via the Azure WebApp 1st-party Service Principal (Application ID: abfa0a7c-a6b6-4736-8310-5855508787cd, Display Name: Microsoft Azure App Service), NOT the UAMI bound to the App Service.
CRITICAL: Application ID ≠ Object ID. RBAC
principalIdmust use Object ID, not Application ID. Using Application ID silently assigns to the wrong principal.
Grant it Key Vault Certificate User + Key Vault Secrets User roles:
azure_webapp_app_id="abfa0a7c-a6b6-4736-8310-5855508787cd"
# Resolve Object ID (different per tenant — never hardcode)
azure_webapp_sp_oid=$(az ad sp show --id "${azure_webapp_app_id}" --query id -o tsv)
# Use the REST API pattern above with principalId="${azure_webapp_sp_oid}" and principalType ServicePrincipal
# Roles needed: db79e9a7-... (Certificate User) + 4633458b-... (Secrets User)
Common Queries
# List certificates in KV
az keyvault certificate list --vault-name <kv-name> \
--query "[].{name:name, thumbprint:x509ThumbprintHex}" -o table
# Show certificate details
az keyvault certificate show --vault-name <kv-name> --name <cert-name> \
--query "{
name:name, thumbprint:x509ThumbprintHex,
subject:policy.x509CertificateProperties.subject,
notBefore:attributes.notBefore, expires:attributes.expires,
issuer:policy.issuerParameters.name,
sansCount:length(policy.x509CertificateProperties.subjectAlternativeNames.dnsNames)
}" -o json
# Show SAN list
az keyvault certificate show --vault-name <kv-name> --name <cert-name> \
--query "policy.x509CertificateProperties.subjectAlternativeNames.dnsNames" -o json
# List configured issuers
az keyvault certificate issuer list --vault-name <kv-name> -o table
Create Certificate via OneCert CA
1. Prepare Policy JSON
{
"issuerParameters": { "name": "OneCertV2-PublicCA" },
"keyProperties": { "exportable": true, "keySize": 2048, "keyType": "RSA", "reuseKey": false },
"lifetimeActions": [{ "action": { "actionType": "AutoRenew" }, "trigger": { "lifetimePercentage": 80 } }],
"secretProperties": { "contentType": "application/x-pkcs12" },
"x509CertificateProperties": {
"ekus": ["1.3.6.1.5.5.7.3.1", "1.3.6.1.5.5.7.3.2"],
"keyUsage": ["digitalSignature", "keyEncipherment"],
"subject": "CN=*.example.dev.copilotlumina.com",
"subjectAlternativeNames": {
"dnsNames": [
"*.example.dev.copilotlumina.com",
"*.sub1.example.dev.copilotlumina.com"
]
},
"validityInMonths": 6
}
}
Key fields:
issuerParameters.name— must be a CA configured in the KV (az keyvault certificate issuer list)subject— Certificate CN; also add it to SAN dnsNames (modern clients only check SAN)lifetimeActions.AutoRenew— auto-renew at 80% lifetimeekus—1.3.6.1.5.5.7.3.1= Server Auth,1.3.6.1.5.5.7.3.2= Client AuthcontentType—application/x-pkcs12= PFX format with private key
2. Create
az keyvault certificate create \
--vault-name <kv-name> --name <cert-name> \
--policy @cert-policy.json
3. Copy policy from existing cert
# Export policy
az keyvault certificate show --vault-name <source-kv> --name <source-cert> \
--query "policy" -o json > cert-policy.json
# Edit: change subject/CN, dnsNames, remove read-only fields (attributes, id)
# Create with new policy
az keyvault certificate create --vault-name <target-kv> --name <new-cert> --policy @cert-policy.json
App Service: Import KV Certificate & Bind Custom Domains
Step 1: Import certificate
thumbprint=$(az webapp config ssl import \
--name <webapp-name> --resource-group <rg> \
--key-vault <kv-name> --key-vault-certificate-name <cert-name> \
--query thumbprint -o tsv)
Step 2: Add custom hostname
az webapp config hostname add \
--webapp-name <webapp-name> --resource-group <rg> \
--hostname "*.example.dev.copilotlumina.com"
Prerequisite: DNS CNAME to
<webapp>.azurewebsites.net, or TXT recordasuid.<hostname>for domain verification.
Step 3: Bind SSL (SNI)
az webapp config ssl bind \
--name <webapp-name> --resource-group <rg> \
--certificate-thumbprint <thumbprint> --ssl-type SNI
Batch operation pattern
thumbprint=$(az webapp config ssl import --name ${webapp} --resource-group ${rg} \
--key-vault ${kv} --key-vault-certificate-name ${cert} --query thumbprint -o tsv)
for hostname in "${hostnames[@]}"; do
az webapp config hostname add --webapp-name ${webapp} --resource-group ${rg} \
--hostname "${hostname}" 2>/dev/null || true
az webapp config ssl bind --name ${webapp} --resource-group ${rg} \
--certificate-thumbprint ${thumbprint} --ssl-type SNI 2>/dev/null || true
done
Verify App Service Bindings
# List custom hostnames
az webapp config hostname list --webapp-name <webapp> --resource-group <rg> -o table
# List SSL certificates
az webapp config ssl list --resource-group <rg> \
--query "[].{name:name, thumbprint:thumbprint, subject:subjectName, expires:expirationDate}" -o table
# Show hostname + SSL binding status
az webapp show --name <webapp> --resource-group <rg> \
--query "hostNameSslStates[].{name:name, sslState:sslState, thumbprint:thumbprint}" -o table
Common Issues
| Problem | Solution |
|---|---|
az role assignment create → MissingSubscription |
Use az rest --method PUT REST API instead |
ForbiddenByConnection |
KV has public network access disabled — enable temporarily or use Private Endpoint/VPN |
ForbiddenByRbac |
Missing RBAC role — grant via REST API (see above) |
| CN not in SAN → domain invalid | Always include CN in SAN dnsNames; modern clients only check SAN |
| App Service can't read KV cert | Grant Key Vault Certificate User + Key Vault Secrets User to WebApp 1st-party SP. Use Object ID (via az ad sp show --id "abfa0a7c-..." --query id), NOT Application ID |
Network Access
If KV has public network access disabled (ForbiddenByConnection):
- Temporarily enable public access (Portal → KV → Networking)
- Or use Private Endpoint / VPN
- AKS pods cannot reach KV private endpoint unless a Private DNS Zone for
privatelink.vaultcore.azure.netis linked to the AKS VNet
More from liulixiang1988/agent-skills
work-record
Record work log / save work summary / add TODO items. TRIGGER when: user says '记录工作', '保存工作', 'record work', 'save work', 'log work', '工作记录', '写工作日志', '保存工作记录', '记录一下', or similar phrases about saving/recording what was done in the current session. Also trigger when user mentions work log, work record, 工作日志, or wants to summarize completed work for future performance review. Also trigger when user says '加一个todo', 'add a todo', '添加todo', '加个待办', '记录todo', or similar phrases about adding a TODO/待办 item to the work log.
13lumina-image
Build Lumina container images. Use this skill when the user wants to build the proxy API image, build the SandboxControlPlane API image, build the sandbox agent image, or build any Lumina-specific Docker image (e.g., 'build proxy api image', 'build scp api image', 'build sandboxcontrolplane image', 'build sandbox agent').
12ado-lumina-pipeline
Trigger the Lumina-SandboxAKSProvider-Service-Dev-Deploy pipeline in Azure DevOps. Use this skill when the user wants to run/trigger/deploy the Lumina sandbox AKS pipeline, deploy sandbox orchestrator, deploy sandbox components, trigger a dev deploy, or run the dev pipeline. Also use when the user asks to re-run or reproduce a specific pipeline build.
7skill-creator
Create new skills, modify and improve existing skills, and measure skill performance. Use when users want to create a skill from scratch, update or optimize an existing skill, run evals to test a skill, benchmark skill performance with variance analysis, or optimize a skill's description for better triggering accuracy.
1