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

azurerm_key_vault_certificate resource is replaced every second time #23537

Open
1 task done
ced360 opened this issue Oct 12, 2023 · 3 comments
Open
1 task done

azurerm_key_vault_certificate resource is replaced every second time #23537

ced360 opened this issue Oct 12, 2023 · 3 comments

Comments

@ced360
Copy link

ced360 commented Oct 12, 2023

Is there an existing issue for this?

  • I have searched the existing issues

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 and review the contribution guide to help.

Terraform Version

1.0.0

AzureRM Provider Version

3.62.1

Affected Resource(s)/Data Source(s)

azurerm_key_vault_certificate

Terraform Configuration Files

resource "azurerm_key_vault" "my-vault" {
  name                        = "my-vault"
  location                    = azurerm_resource_group.aks-rg.location
  resource_group_name         = azurerm_resource_group.aks-rg.name
  enabled_for_disk_encryption = false
  tenant_id                   = data.azurerm_client_config.current.tenant_id
  soft_delete_retention_days  = 7
  purge_protection_enabled    = false

  sku_name = "standard"
}

resource "acme_certificate" "certificate" {
  for_each                  = var.certificates
  common_name               = each.key
  subject_alternative_names = each.value.subject_alternative_names
  key_type                  = each.value.key_size
  account_key_pem           = acme_registration.reg.account_key_pem
  min_days_remaining        = 1 # For tests purpose, new cert is generated when the actual is one day old

  dns_challenge {
    provider = "azure"
    config = {
      AZURE_POLLING_INTERVAL    = 1
      AZURE_PROPAGATION_TIMEOUT = 5
      AZURE_TTL                 = 1
      AZURE_CLIENT_ID           = var.CLIENT_ID
      AZURE_CLIENT_SECRET       = var.CLIENT_SECRET
      AZURE_RESOURCE_GROUP      = azurerm_resource_group.aks-rg.name
      AZURE_SUBSCRIPTION_ID     = var.SUBSCRIPTION_ID
      AZURE_TENANT_ID           = var.TENANT_ID
    }
  }
}

resource "local_file" "pfx_certificates" {
  for_each       = var.certificates
  content_base64 = acme_certificate.certificate[each.key].certificate_p12
  filename       = "${replace(replace(each.key, "*", "wildcard"), ".", "-")}.pfx" # Transforms CN to valid string
}

resource "azurerm_key_vault_certificate" "certificate" {
  for_each     = var.certificates
  name         = each.value.name
  key_vault_id = azurerm_key_vault.my-vault.id

  certificate {
    contents = acme_certificate.certificate[each.key].certificate_p12
    password = ""
  }

  certificate_policy {
    issuer_parameters {
      name = "Unknown"
    }

    key_properties {
      exportable = true
      reuse_key  = true
      key_size   = each.value.key_size
      key_type   = each.value.key_type
    }

    secret_properties {
      content_type = "application/x-pkcs12"
    }
  }
}

Debug Output/Panic Output

# azurerm_key_vault_certificate.certificate["*.sub.domain"] must be replaced
-/+ resource "azurerm_key_vault_certificate" "certificate" {
...
      ~ certificate_policy {
          ~ key_properties {
              + curve      = (known after apply)
              ~ reuse_key  = false -> true # forces replacement
...

Expected Behaviour

azurerm_key_vault_certificate.certificate["*.sub.domain"] will be updated in-place

~ resource "azurerm_key_vault_certificate" "certificate" {
id = "https://my-vault.vault.azure.net/certificates/certificate-autorenew-test/******************************"
name = "certificate-autorenew-test"
tags = {}
# (11 unchanged attributes hidden)
~ certificate {
~ contents = (sensitive value)
# (1 unchanged attribute hidden)
}
# (1 unchanged block hidden)
}

Actual Behaviour

I wanted to use that project : https://github.com/rdvansloten/letsencrypt-terraform-azure/tree/main to create a Let's Encrypt certificate in my key vault, and renew the certificate if it need to. To test my implementation, I fixed the min_days_remaining property of the acme_certificate resource to 1. The idea behind this is that I don't have to wait a certain number of days to test automatic renewal, I just have to wait for tomorrow. The first time I launch my pipeline, it will create the certificate and push it to my key vault.

  • The second time : the behaviour is OK, like in the "Expected Behaviour" block. A new certificate is generated and push to my key vault as the current version. The old one becomes the old version.
  • The third time : it is the time when the bug appear, it seems that is considered that I want to change the reuse_key from false to true (Already fixed to true in the code) and it forces the replacement of the resource. After, if I check my certificate, there is nothing in the old versions section, and there is a brand new certificate in the current version section.
  • The 4th time : the behaviour is OK...
  • The 5th time : the behaviour is KO...
  • and so on

Steps to Reproduce

No response

Important Factoids

No response

References

No response

@wuxu92
Copy link
Contributor

wuxu92 commented Oct 13, 2023

Hi @ced360 Could you please have a try with the latest AzureRM Provider? As #20627 has removed the ForceNew of the reuse_key field.

@ced360
Copy link
Author

ced360 commented Oct 16, 2023

Hi, I replaced my version of azurerm with 3.70.0 and, indeed, plans are more consistent ! He doesn't want to destroy the existent certificate anymore. I made some modifications in the code because I wanted to replace another existent key vault certificate with the one I generate now, and when I apply, I obtain this error :

╷
│ Error: keyvault.BaseClient#ImportCertificate: Failure responding to request: StatusCode=400 -- Original Error: autorest/azure: Service returned an error. Status=400 Code="BadParameter" Message="No certificate with private key found in the specified X.509 certificate content. Please specify X.509 certificate content with only one certificate containing private key."

│ 
│   with azurerm_key_vault_certificate.existent-cert,
│   on vault.tf line 71, in resource "azurerm_key_vault_certificate" "existent-cert":
│   71: resource "azurerm_key_vault_certificate" "existent-cert" {
│ 
╵

I tried with that :

# Generate a private key
resource "tls_private_key" "cert_private_key" {
  algorithm = var.certificates["*.sub.domain"].key_type
  rsa_bits  = var.certificates["*.sub.domain].key_size
}

# Generate LetsEncrypt certificates using Azure DNS
resource "acme_certificate" "certificate" {
  common_name               = "*.sub.domain"
  subject_alternative_names = var.certificates["*.sub.domain"].subject_alternative_names
  key_type                  = var.certificates["*.sub.domain"].key_size
  account_key_pem           = acme_registration.reg.account_key_pem
  min_days_remaining        = 30 # New certificate will be generate after 60 days

  dns_challenge {
    provider = "azure"
    config = {
      AZURE_POLLING_INTERVAL    = 1
      AZURE_PROPAGATION_TIMEOUT = 5
      AZURE_TTL                 = 1
      AZURE_CLIENT_ID           = var.CLIENT_ID
      AZURE_CLIENT_SECRET       = var.CLIENT_SECRET
      AZURE_RESOURCE_GROUP      = azurerm_resource_group.aks-rg.name
      AZURE_SUBSCRIPTION_ID     = var.SUBSCRIPTION_ID
      AZURE_TENANT_ID           = var.TENANT_ID
    }
  }
}

# Import certificates to Azure Key Vault. 
# IMPORTANT: Does not update certificates if Purge Protection is ON! See workaround below.
resource "azurerm_key_vault_certificate" "existent-cert" {
  name           = var.certificates["*.sub.domain"].name
  key_vault_id = azurerm_key_vault.my-vault.id

  certificate {
    contents = acme_certificate.certificate.certificate_pem
    password = ""
  }

  certificate_policy {
    issuer_parameters {
      name = "Unknown"
    }

    key_properties {
      exportable = true
      reuse_key  = true
      key_size   = var.certificates["*.sub.domain"].key_size
      key_type   = var.certificates["*.sub.domain"].key_type
    }

    lifetime_action {
      action {
        action_type = "EmailContacts"
      }
      trigger {
        days_before_expiry  = 0
        lifetime_percentage = 80
      }
    }

    secret_properties {
      content_type = "application/x-pem-file"
    }
  }
}

But now, I am unable to generate certificate because of this error. I tried to modify p12 in pem, 2048 in 4096 and other things, but I always obtain this new behaviour.

@wuxu92
Copy link
Contributor

wuxu92 commented Nov 28, 2023

@ced360 The Contents field should be base64 encoded as key_vault_certificate#contents. I'm not sure if the acme_certificate.certificate.certificate_pem is a valid base64 string. Could you have a try with base64encode(acme_certificate.certificate.certificate_pem)?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants