Skip to content

Commit

Permalink
azurerm_key_vault_certificate: do not create a new certificate vers…
Browse files Browse the repository at this point in the history
…ion when only update `lifetime_action` field (hashicorp#24755)
  • Loading branch information
wuxu92 authored and rizkybiz committed Feb 29, 2024
1 parent 457f5cc commit 00d6a5e
Show file tree
Hide file tree
Showing 3 changed files with 155 additions and 40 deletions.
115 changes: 76 additions & 39 deletions internal/services/keyvault/key_vault_certificate_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"time"

"github.com/Azure/go-autorest/autorest"
"github.com/google/go-cmp/cmp"
"github.com/hashicorp/go-azure-helpers/lang/pointer"
"github.com/hashicorp/go-azure-helpers/lang/response"
"github.com/hashicorp/go-azure-helpers/resourcemanager/commonids"
Expand Down Expand Up @@ -620,7 +621,28 @@ func resourceKeyVaultCertificateUpdate(d *schema.ResourceData, meta interface{})
d.SetId(certificateId.ID())
}
}
if d.HasChange("certificate_policy") {

// update lifetime_action only should not recreate a certificate
var lifeTimeOld, lifeTimeNew interface{}
var policyOld, policyNew map[string]interface{}

policyOldRaw, policyNewRaw := d.GetChange("certificate_policy")
policyOldList, policyNewList := policyOldRaw.([]interface{}), policyNewRaw.([]interface{})

if len(policyOldList) > 0 {
policyOld = policyOldList[0].(map[string]interface{})
lifeTimeOld = policyOld["lifetime_action"]
delete(policyOld, "lifetime_action")
}
if len(policyNewList) > 0 {
policyNew = policyNewList[0].(map[string]interface{})
lifeTimeNew = policyNew["lifetime_action"]
delete(policyNew, "lifetime_action")
}

// do not recreate cerfiticate when only lifetime_action changes
if !cmp.Equal(policyNewList, policyOldList) {
policyNew["lifetime_action"] = lifeTimeNew
newCert, err := createCertificate(d, meta)
if err != nil {
return err
Expand All @@ -632,10 +654,18 @@ func resourceKeyVaultCertificateUpdate(d *schema.ResourceData, meta interface{})
d.SetId(certificateId.ID())
}

if d.HasChange("tags") {
if updateLifetime := !cmp.Equal(lifeTimeOld, lifeTimeNew); d.HasChange("tags") || updateLifetime {
patch := keyvault.CertificateUpdateParameters{}
if t, ok := d.GetOk("tags"); ok {
patch.Tags = tags.Expand(t.(map[string]interface{}))
if d.HasChange("tags") {
if t, ok := d.GetOk("tags"); ok {
patch.Tags = tags.Expand(t.(map[string]interface{}))
}
}

if updateLifetime {
patch.CertificatePolicy = &keyvault.CertificatePolicy{
LifetimeActions: expandKeyVaultCertificatePolicyLifetimeAction(lifeTimeNew),
}
}

if _, err = client.UpdateCertificate(ctx, id.KeyVaultBaseUrl, id.Name, "", patch); err != nil {
Expand Down Expand Up @@ -908,41 +938,7 @@ func expandKeyVaultCertificatePolicy(d *pluginsdk.ResourceData) (*keyvault.Certi
ReuseKey: utils.Bool(props["reuse_key"].(bool)),
}

lifetimeActions := make([]keyvault.LifetimeAction, 0)
actions := policyRaw["lifetime_action"].([]interface{})
for _, v := range actions {
action := v.(map[string]interface{})
lifetimeAction := keyvault.LifetimeAction{}

if v, ok := action["action"]; ok {
as := v.([]interface{})
a := as[0].(map[string]interface{})
lifetimeAction.Action = &keyvault.Action{
ActionType: keyvault.CertificatePolicyAction(a["action_type"].(string)),
}
}

if v, ok := action["trigger"]; ok {
triggers := v.([]interface{})
if triggers[0] != nil {
trigger := triggers[0].(map[string]interface{})
lifetimeAction.Trigger = &keyvault.Trigger{}

d := trigger["days_before_expiry"].(int)
if d > 0 {
lifetimeAction.Trigger.DaysBeforeExpiry = utils.Int32(int32(d))
}

p := trigger["lifetime_percentage"].(int)
if p > 0 {
lifetimeAction.Trigger.LifetimePercentage = utils.Int32(int32(p))
}
}
}

lifetimeActions = append(lifetimeActions, lifetimeAction)
}
policy.LifetimeActions = &lifetimeActions
policy.LifetimeActions = expandKeyVaultCertificatePolicyLifetimeAction(policyRaw["lifetime_action"])

secrets := policyRaw["secret_properties"].([]interface{})
secret := secrets[0].(map[string]interface{})
Expand Down Expand Up @@ -999,6 +995,47 @@ func expandKeyVaultCertificatePolicy(d *pluginsdk.ResourceData) (*keyvault.Certi
return &policy, nil
}

func expandKeyVaultCertificatePolicyLifetimeAction(actions interface{}) *[]keyvault.LifetimeAction {
lifetimeActions := make([]keyvault.LifetimeAction, 0)
if actions == nil {
return &lifetimeActions
}

for _, v := range actions.([]interface{}) {
action := v.(map[string]interface{})
lifetimeAction := keyvault.LifetimeAction{}

if v, ok := action["action"]; ok {
as := v.([]interface{})
a := as[0].(map[string]interface{})
lifetimeAction.Action = &keyvault.Action{
ActionType: keyvault.CertificatePolicyAction(a["action_type"].(string)),
}
}

if v, ok := action["trigger"]; ok {
triggers := v.([]interface{})
if triggers[0] != nil {
trigger := triggers[0].(map[string]interface{})
lifetimeAction.Trigger = &keyvault.Trigger{}

d := trigger["days_before_expiry"].(int)
if d > 0 {
lifetimeAction.Trigger.DaysBeforeExpiry = utils.Int32(int32(d))
}

p := trigger["lifetime_percentage"].(int)
if p > 0 {
lifetimeAction.Trigger.LifetimePercentage = utils.Int32(int32(p))
}
}
}

lifetimeActions = append(lifetimeActions, lifetimeAction)
}
return &lifetimeActions
}

func flattenKeyVaultCertificatePolicy(input *keyvault.CertificatePolicy, certData *[]byte) []interface{} {
if input == nil {
return []interface{}{}
Expand Down
78 changes: 78 additions & 0 deletions internal/services/keyvault/key_vault_certificate_resource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,28 @@ func TestAccKeyVaultCertificate_basicGenerate(t *testing.T) {
})
}

func TestAccKeyVaultCertificate_updateLifeTime(t *testing.T) {
data := acceptance.BuildTestData(t, "azurerm_key_vault_certificate", "test")
r := KeyVaultCertificateResource{}

data.ResourceTest(t, r, []acceptance.TestStep{
{
Config: r.basicGenerate(data),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
),
},
data.ImportStep(),
{
Config: r.basicGenerateUpdateLifetimeAction(data),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
),
},
data.ImportStep(),
})
}

func TestAccKeyVaultCertificate_basicGenerateUnknownIssuer(t *testing.T) {
data := acceptance.BuildTestData(t, "azurerm_key_vault_certificate", "test")
r := KeyVaultCertificateResource{}
Expand Down Expand Up @@ -713,6 +735,62 @@ resource "azurerm_key_vault_certificate" "test" {
`, r.template(data), data.RandomString)
}

func (r KeyVaultCertificateResource) basicGenerateUpdateLifetimeAction(data acceptance.TestData) string {
return fmt.Sprintf(`
provider "azurerm" {
features {}
}
%s
resource "azurerm_key_vault_certificate" "test" {
name = "acctestcert%s"
key_vault_id = azurerm_key_vault.test.id
certificate_policy {
issuer_parameters {
name = "Self"
}
key_properties {
exportable = true
key_size = 2048
key_type = "RSA"
reuse_key = true
}
lifetime_action {
action {
action_type = "EmailContacts"
}
trigger {
days_before_expiry = 30
}
}
secret_properties {
content_type = "application/x-pkcs12"
}
x509_certificate_properties {
key_usage = [
"cRLSign",
"dataEncipherment",
"digitalSignature",
"keyAgreement",
"keyEncipherment",
"keyCertSign",
]
subject = "CN=hello-world"
validity_in_months = 12
}
}
}
`, r.template(data), data.RandomString)
}

func (r KeyVaultCertificateResource) updateCertificate(data acceptance.TestData) string {
return fmt.Sprintf(`
provider "azurerm" {
Expand Down
2 changes: 1 addition & 1 deletion website/docs/r/key_vault_certificate.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ The following arguments are supported:

* `certificate` - (Optional) A `certificate` block as defined below, used to Import an existing certificate. Changing this will create a new version of the Key Vault Certificate.

* `certificate_policy` - (Optional) A `certificate_policy` block as defined below. Changing this will create a new version of the Key Vault Certificate.
* `certificate_policy` - (Optional) A `certificate_policy` block as defined below. Changing this (except the `lifetime_action` field) will create a new version of the Key Vault Certificate.

~> **NOTE:** When creating a Key Vault Certificate, at least one of `certificate` or `certificate_policy` is required. Provide `certificate` to import an existing certificate, `certificate_policy` to generate a new certificate.

Expand Down

0 comments on commit 00d6a5e

Please sign in to comment.