Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Support for App Service Managed Certificates #4824

Closed
ablyler opened this issue Nov 7, 2019 · 19 comments
Closed

Add Support for App Service Managed Certificates #4824

ablyler opened this issue Nov 7, 2019 · 19 comments

Comments

@ablyler
Copy link

ablyler commented Nov 7, 2019

Community Note

  • Please vote on this issue by adding a 👍 reaction to the original issue to help the community and maintainers prioritize this request
  • Please do not leave "+1" or "me too" comments, they generate extra noise for issue followers and do not help prioritize the request
  • If you are interested in working on this issue or have submitted a pull request, please leave a comment

Description

Add support for the just announced Managed Certificate capability of Azure App Services.

New or Affected Resource(s)

  • azurerm_app_service_certificate

Potential Terraform Configuration

Unknown

References

@ablyler ablyler changed the title Add Support for App Service Managed Certificate Add Support for App Service Managed Certificates Nov 7, 2019
@tombuildsstuff tombuildsstuff transferred this issue from hashicorp/terraform-provider-azuread Nov 7, 2019
@drdamour
Copy link
Contributor

drdamour commented Jan 3, 2020

brought this up in the PR, but it's feature specific

does the hostname for the managed cert have to already be setup as a custom domain binding for the app service? It does in the GUI it seems since it's a select box not a combo/free text box
image

If so, then the https://www.terraform.io/docs/providers/azurerm/r/app_service_custom_hostname_binding.html would need to be split to having the cert for a binding be it's own resource as the chain would need to be

setup custom domain binding -> generate managed certificate -> bind managed certificate to custom domain binding

@krzyszt0fd
Copy link

krzyszt0fd commented Mar 23, 2020

Perhaps it could be achieved with azurerm_app_service_certificate_order?
I tried using it but despite the fact that the order was successfully created I can't use it as the certificates attribute is an empty list. Azure Portal hint says a vault must be created to store the cert. The azurerm_app_service_certificate_order resource doesn't have an argument to select a key vault though.

https://github.com/Azure/azure-quickstart-templates/tree/master/101-app-service-certificate-standard

@drdamour
Copy link
Contributor

azurerm_app_service_certificate_order Is something else.

afaik u must use the gui to put it in the vault

@krzyszt0fd
Copy link

I have checked with ARM Microsoft.Web/certificates (2019-08-01) and the result was:
Properties.CanonicalName is invalid. Certificate creation requires hostname xxx added to an app in the serverFarmId /subscriptions/xxx/resourceGroups/xxx/providers/Microsoft.Web/serverfarms/xxx

@drdamour
Copy link
Contributor

note to whoever implements this, they'll need to account for the fact that an App Service Managed Certificate can change thumbprints at anytime outside any normal tf applies. You can kinda hack around this with the existing azurerm_app_service_certificate datasource which will return app service managed certs, but it creates a chicken egg problem since you can't conditionally use a datasource per hashicorp/terraform#16380

@imod
Copy link

imod commented Sep 16, 2020

here is a workaround we use https://gist.github.com/imod/52050eec87f87a60b5944c29ebf0f7a1
...but as pointed out by @drdamour the certificate can change at any time and then the binding should be marked as tainted to be recreated.

@martinjt
Copy link

Is this going anywhere? or blocked for some reason?

@martinjt
Copy link

for anyone interested... it's blocked by:

Azure/azure-sdk-for-go#6498

which in turn is blocked by:
Azure/azure-rest-api-specs#5029

If anyone knows anyone at MS that can push someone to fix it, that would be swell...

@thomasbeauvais
Copy link

This would be great!

Right now, without this we have to manually add the certification, then take the thumbprint and paste it in tf. The thumbprint can change at any time.

@AdamCoulterOz
Copy link
Contributor

@thomasbeauvais - I've got a few custom terraform modules to bridge the gap while we are waiting... ill link you to them here shortly

@AdamCoulterOz
Copy link
Contributor

AdamCoulterOz commented Oct 13, 2020

@tombuildsstuff -- In order to achieve this support for Managed App Service Certificates there are 3 other issues which need to be fixed:

Below I've managed to make it work end-to-end, fully automated within terraform, but I've filled the gaps with custom script resources, thanks to @scottwinkler (fully stateful with Read, Update, Create, Delete support) in some published modules... I'll remove these modules in my implementation as the gaps are filled.

data "azurerm_dns_zone" "main" {
  provider            = azurerm.corp
  name                = var.dns_zone
  resource_group_name = var.dns_zone_resource_group
}

# This custom module is the same as a data.azurerm_app_service, except 
# this exposes the required CustomDomainVerificationId field which is being fixed in #7537
module "webapp_data" {
  source     = "AdamCoulterOz/webappdata/azurerm"
  web_app_id = azurerm_app_service.mywebsite.id
}

resource "azurerm_dns_cname_record" "main" {
  provider            = azurerm.corp
  name                = var.id
  zone_name           = data.azurerm_dns_zone.main.name
  resource_group_name = var.dns_zone_resource_group
  ttl                 = 300
  record              = azurerm_app_service.mywebsite.default_site_hostname
}

resource "azurerm_dns_txt_record" "main" {
  provider            = azurerm.corp
  name                = "asuid.${var.id}"
  zone_name           = data.azurerm_dns_zone.main.name
  resource_group_name = var.dns_zone_resource_group
  ttl                 = 300
  record {
    value = module.webapp_data.custom_domain_verification_id
  }
}

# Ignore the certificate binding fields in this resource, as it would cause a circular
# dependency, this is critical as the managed certificate needs to be in-between
# these 2 parts.... a fix for this is tracked in #8069
resource "azurerm_app_service_custom_hostname_binding" "main" {
  hostname            = trim(azurerm_dns_cname_record.main.fqdn,".")
  app_service_name    = azurerm_app_service.mywebsite.name
  resource_group_name = azurerm_resource_group.mywebsite.name
  depends_on          = [azurerm_dns_txt_record.main]
  lifecycle {
    ignore_changes = [ssl_state,thumbprint]
  }
  # TODO: Add retry / wait until the txt/cname records have propagated
  # @tombuildsstuff is there general pattern for this?
}

# Creates the managed web app certificate, same as azurerm_app_service_certificate,
# except it allows the password to be empty string, tracked in azure-sdk-for-go#6498
# but im not sure they've tried setting the password to empty string yet, instead of just not setting it
module "webappcert" {
  source              = "AdamCoulterOz/webappcert/azurerm"
  resource_group      = azurerm_resource_group.mywebsite.name
  location            = azurerm_resource_group.mywebsite.location
  app_service_plan_id = data.azurerm_app_service_plan.hosting_group.id
  name                = trim(azurerm_dns_cname_record.main.fqdn,".")
  depends_on          = [azurerm_app_service_custom_hostname_binding.main]
}

# This is the second half of the required split of azurerm_app_service_custom_hostname_binding
# to separate the certificate binding into a separate resource, tracked in #8069
module "webappcertbind" {
  source         = "AdamCoulterOz/webappcertbind/azurerm"
  resource_group = azurerm_resource_group.mywebsite.name
  web_app_name   = azurerm_app_service.mywebsite.name
  thumbprint     = module.webappcert.thumbprint
}

Note that the following provider block is also required (in addition to pwsh 7+, and a few pwsh modules):

Install-Module @('Az.Accounts', 'Az.Websites', 'AzureHelpers') 
provider "shell" {
  environment = {
    AzureClientId     = var.client_id
    AzureTenantId     = var.tenant_id
    AzureSubscription = var.subscription_id
  }

  sensitive_environment = {
    AzureClientSecret = var.client_secret
  }

  interpreter = ["pwsh", "-Command"]
}

fyi @thomasbeauvais / @ablyler / @drdamour / @krzyszt0fd / @imod

@drdamour
Copy link
Contributor

that "" password vs nil is a pretty funny story i'll tell my grand kids

@AdamCoulterOz
Copy link
Contributor

AdamCoulterOz commented Nov 27, 2020

So we are almost there... as of v2.38.0 custom_domain_verification_id is now available as an attribute of azurerm_app_service and a new resource for azurerm_app_service_managed_certificate has been released... thanks @jackofallops !!

Now the only last remaining piece in this puzzle is ... #8069 (azurerm_app_service_custom_hostname_certificate_binding), which will allow us to bind the managed certificate to the hostname_binding.

@jackofallops
Copy link
Member

@AdamCoulterOz - I'll take a look into this Monday, I think it's going to be more involved than first glance.

@tombuildsstuff
Copy link
Contributor

Closing since this has been fixed via #9415

@ghost
Copy link

ghost commented Dec 10, 2020

This has been released in version 2.40.0 of the provider. Please see the Terraform documentation on provider versioning or reach out if you need any assistance upgrading. As an example:

provider "azurerm" {
    version = "~> 2.40.0"
}
# ... other configuration ...

@AdamCoulterOz
Copy link
Contributor

Boooh yeah.... works great!!!

https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/app_service_certificate_binding#example-usage

Thanks @jackofallops for getting all the different parts of this one over the line!

@drdamour
Copy link
Contributor

fwiw i verified u can also use the id from https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/app_service_certificate in the app_service_certificate_binding i've been using a pattern like

certificate_id = app_service_certificate_binding_example_exists ? app_service_certificate_binding.example.id : azurerm_app_service_managed_certificate.example.id

to default to managed cert if an explicit cert isn't present

@ghost
Copy link

ghost commented Jan 9, 2021

I'm going to lock this issue because it has been closed for 30 days ⏳. This helps our maintainers find and focus on the active issues.

If you feel this issue should be reopened, we encourage creating a new issue linking back to this one for added context. If you feel I made an error 🤖 🙉 , please reach out to my human friends 👉 [email protected]. Thanks!

@ghost ghost locked as resolved and limited conversation to collaborators Jan 9, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.