There are a couple of threads in this channel deal...
# azure
l
There are a couple of threads in this channel dealing with Azure's circular dependency between WebAppHostNameBinding and Certificate: https://pulumi-community.slack.com/archives/CRVK66N5U/p1695666407859359 https://pulumi-community.slack.com/archives/CRVK66N5U/p1685980035375119 Here's my C# two pulumi up workaround, in case it's helpful to anyone. It really is far from ideal that we have to go this extent - doesn't play well with CI/CD scenarios at all.
Copy code
// EXPLANATION OF WHAT'S GOING ON HERE...
//
// For background, see: <https://github.com/pulumi/pulumi-azure-native/issues/578>
//
// TL;DR - Azure has a circular dependency between WebAppHostNameBinding and Certificate
// a) A binding for the FQDN must exist to create a certificate for that FQDN
// b) For the binding to be TLS-enabled, it needs the thumbprint of the certificate
//
// The solution here requires *TWO* "pulumi up" executions:
// 1) Determines that the binding does not exist, so creates it without a thumbprint
// 2) Once the binding exist, creates the certificate and updates the binding with the thumbprint
// (Technically, per Pulumi, it replaces the binding)

var customDnsFqdn = config.Require("CustomDnsFqdn");
// apiAppService and rgName are available in this scope

try
{
	var inputArgs = Output.Tuple<string, string>(apiAppService.Name, rgName);

	// We don't have access to the app service or resource group names until
	// "Apply" can resolve them.
	//
	// And we can't use the non-async version of "Invoke" because the exception
	// thrown when the resource isn't found in that version aborts the program
	// immediately - "catch" doesn't work.
	inputArgs.Apply(args =>
	{
		var bindingLookupArgs = new GetWebAppHostNameBindingArgs
		{
			HostName = customDnsFqdn,
			Name = args.Item1,
			ResourceGroupName = args.Item2,
		};

		var bindingLookupResult = GetWebAppHostNameBinding.InvokeAsync(bindingLookupArgs).Result;

		return "found";
	});

	// If we get here, the initial binding exists; so we can create the certificate
	// and update the binding
	var managedCert = new Certificate("mycertname", new()
	{
		ServerFarmId = apiAppService.ServerFarmId!,
		CanonicalName = customDnsFqdn,
		HostNames = new[]
		{
			customDnsFqdn,
		},
		Location = "eastus",
		ResourceGroupName = rgName
	});

	var customDomain = new WebAppHostNameBinding("mybindingname", new()
	{
		HostName = customDnsFqdn,
		HostNameType = HostNameType.Verified,
		ResourceGroupName = rgName,
		Name = apiAppService.Name,
		SiteName = apiAppService.Name,
		SslState = SslState.SniEnabled,
		Thumbprint = managedCert.Thumbprint
	});
}
catch
{
	<http://Log.Info|Log.Info>($"***** WebAppHostNameBinding DOES NOT EXIST *****");
	// Do nothing - we've verified that the resource doesn't exist

	// If we get here, we create an initial binding for the FQDN so
	// that the subsequent "pulumi up" can create the certificate
	var prodCustomDomain = new WebAppHostNameBinding("mybindingname", new()
	{
		HostName = customDnsFqdn,
		HostNameType = HostNameType.Verified,
		ResourceGroupName = rgName,
		Name = apiAppService.Name,
		SiteName = apiAppService.Name
	});
}