does anyone know the right order of operations for...
# azure
m
does anyone know the right order of operations for creating a
containerapp
that uses an
Azure managed certificate
for a subdomain. I create the managed environment and then get the
customDomainVerificationId
for the
txt
entries I need to make in cloudflare. I still need the
fqdn
suffix that will be used to create the
cname
records but I dont see how to get that until after the container app has been created. I need the cname record to create the DNS entry in order to create the managed cert. but I need the managed cert to create the containerapp with a
customDomain
. I tried creating the contianerapp without the custom domain and then updating it after creating the managed cert but it did not seem to work I get a generic 500 error.
Copy code
Diagnostics:
  azure-native:app/v20241002preview:ManagedCertificate (cmsCert):
    error: Status=500 Code="InternalServerError" Message="Internal server error occurred. correlation ID: d02faf16-f6a7-43d3-9c5a-6b1201182fd8"
Also the container app doesnt have any custom domains on it even though pulumi showed it as successful.
f
I did this a while back, and I seem to remember having to deploy it in stages, i.e. 1. Creating the container app with a custom domain name, but with the binding type "Disabled" 2. Create the certificate and change the binding type to "SniEnabled"
m
I figured out how to do it in a single stage through Pulumi. What is crucial in making this work are the order of creation, triggers and dependencies shown below. This is essentially what I do: • create a container app with the custom domains set as
Copy code
bindingType: 'Disabled', name: `${Subdomain}.${domain}`
• get the fqdn and domain verification from the app
Copy code
const siteFQDN = myContainerApp.configuration.apply(fqdn => fqdn?.ingress?.fqdn ?? "localhost");
const nginxCvid = myContainerApp.customDomainVerificationId.apply(cvid => cvid);
• Create the DNS entries (in my case I use cloudflare)
Copy code
const zone = cloudflare.getZone({ name: domain });

    // Create DNS records
    const CNAME = new cloudflare.Record(Subdomain, {
        zoneId: zone.then((z: cloudflare.GetZoneResult) => z.id),
        name: `${Subdomain}.${domain}`,
        type: "CNAME",
        content: siteFQDN,
        ttl: 3600,
    },{dependsOn: [managed_env, myContainerApp]});

    const TXT = new cloudflare.Record(`asuid.${Subdomain}`, {
        zoneId: zone.then((z: cloudflare.GetZoneResult) => z.id),
        name: `asuid.${cmsSubdomain}.${domain}`,
        type: "TXT",
        content: nginxCvid,
        ttl: 3600,
    },{dependsOn: [managed_env, myContainerApp ]});
• Create the certificates
Copy code
const Cert = new azure_app.ManagedCertificate("Cert", {
        resourceGroupName: resourceGroupName,
        environmentName: environment.name,
        managedCertificateName: `${Subdomain}`,
        properties: {
            domainControlValidation: "CNAME",
            subjectName: `${Subdomain}.${domain}`,
        },
    }, { dependsOn: [myContainerApp] });
• Bind the cert to the custom domain in the app.
Copy code
// Use azure-native to bind custom domains with the managed certificates
    const bindCommand = new command.local.Command("custom-domain", {
        create: pulumi.interpolate `az containerapp hostname bind \
        --hostname ${Subdomain}.${domain} \
        -g ${resourceGroupName} -n ${myContainerApp.name} \
        --environment ${environment.name} \
        --validation-method CNAME`,
        triggers: [Cert.systemData.lastModifiedAt,myContainerApp.systemData.lastModifiedAt],
    }, { dependsOn: [cmsCert, myContainerApp, environment] });
f
Isn't that going to flip you back to disabled and then (via your final command) straight back to bound each time you deploy it?
Maybe only if a pulumi refresh is involved.
m
I thought so to, so I had the custom domain setup dependent on a sort of semaphore so that I could switch if it added as disabled or snienabled. but it turns out that any change to the container caused the binding to break so I removed that since the "else" would never trigger and set the "trigger" to be on the last modified datetime to make sure that the binding was immediately restored on any change to the cert or the container.