From 2e67d178b888c653c7238e6651027d80a1120d58 Mon Sep 17 00:00:00 2001 From: Andy Lo-A-Foe Date: Wed, 9 Oct 2024 09:01:48 +0200 Subject: [PATCH] Add self_managed_certificate_nonsensitive Signed-off-by: Andy Lo-A-Foe --- docs/resources/iam_service.md | 3 + .../iam/service/resource_iam_service.go | 57 ++++++----- .../iam/service/resource_iam_service_v5.go | 97 +++++++++++++++++++ 3 files changed, 132 insertions(+), 25 deletions(-) create mode 100644 internal/services/iam/service/resource_iam_service_v5.go diff --git a/docs/resources/iam_service.md b/docs/resources/iam_service.md index d5dbf6fb..7ca54679 100644 --- a/docs/resources/iam_service.md +++ b/docs/resources/iam_service.md @@ -80,6 +80,9 @@ The following arguments are supported: * `self_managed_expires_on` - (Deprecated, Optional) Sets the certificate validity. When not specified, the certificate will have a validity of 5 years. * `self_managed_certificate` - (Optional) X509 Certificate in PEM format. When provided, overrides the generated certificate / private key combination of the IAM service. This gives you full control over the credentials. When not specified, a private key will be generated by IAM. Mutually exclusive with `self_managed_private_key` +* `self_managed_certificate_nonsensitive` - (Optional) X509 Certificate in PEM format. When provided, overrides the generated certificate / private key combination of the IAM service. + This gives you full control over the credentials. When not specified, a private key will be generated by IAM. Mutually exclusive with `self_managed_private_key` + ## Attributes Reference diff --git a/internal/services/iam/service/resource_iam_service.go b/internal/services/iam/service/resource_iam_service.go index ac553c9a..750e7251 100644 --- a/internal/services/iam/service/resource_iam_service.go +++ b/internal/services/iam/service/resource_iam_service.go @@ -27,7 +27,7 @@ func ResourceIAMService() *schema.Resource { Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, - SchemaVersion: 5, + SchemaVersion: 6, CreateContext: resourceIAMServiceCreate, ReadContext: resourceIAMServiceRead, UpdateContext: resourceIAMServiceUpdate, @@ -43,6 +43,11 @@ func ResourceIAMService() *schema.Resource { Upgrade: patchIAMServiceV4, Version: 4, }, + { + Type: ResourceIAMServiceV5().CoreConfigSchema().ImpliedType(), + Upgrade: patchIAMServiceV5, + Version: 5, + }, }, Schema: map[string]*schema.Schema{ @@ -100,6 +105,15 @@ func ResourceIAMService() *schema.Resource { Optional: true, Description: "X509 Certificate in PEM format. When provided, overrides the generated certificate / private key combination of the IAM service. This gives you full control over the credentials. When not specified, a private key will be generated by IAM.\n" + "Mutually exclusive with `self_managed_private_key`", + ConflictsWith: []string{"self_managed_certificate_nonsensitive"}, + }, + "self_managed_certificate_nonsensitive": { + Type: schema.TypeString, + Sensitive: false, + Optional: true, + Description: "X509 Certificate in PEM format. When provided, overrides the generated certificate / private key combination of the IAM service. This gives you full control over the credentials. When not specified, a private key will be generated by IAM.\n" + + "Mutually exclusive with `self_managed_certificate`", + ConflictsWith: []string{"self_managed_certificate"}, }, "private_key": { Type: schema.TypeString, @@ -162,15 +176,17 @@ func resourceIAMServiceCreate(ctx context.Context, d *schema.ResourceData, m int selfExpiresOn := d.Get("self_managed_expires_on").(string) selfPrivateKey := d.Get("self_managed_private_key").(string) selfCertificate := d.Get("self_managed_certificate").(string) + selfCertificateNS := d.Get("self_managed_certificate_nonsensitive").(string) + if selfCertificateNS != "" { + selfCertificate = selfCertificateNS + } + if selfPrivateKey == "" && selfExpiresOn != "" { return diag.FromErr(fmt.Errorf("you cannot set 'self_managed_expires_on' value without also specifying the 'self_managed_private_key'")) } if selfCertificate != "" && selfExpiresOn != "" { return diag.FromErr(fmt.Errorf("you cannot set 'self_managed_expires_on' value in combination with 'self_managed_certificate'")) } - if selfCertificate != "" && selfPrivateKey != "" { - return diag.FromErr(fmt.Errorf("you cannot set 'self_managed_private_key' value in combination with 'self_managed_certificate'")) - } var createdService *iam.Service @@ -201,7 +217,7 @@ func resourceIAMServiceCreate(ctx context.Context, d *schema.ResourceData, m int } // Set certificate if set from publicKey if selfCertificate != "" { - diags = setSelfManagedCertificate(client, *createdService, d) + diags = setSelfManagedCertificate(client, *createdService, selfCertificate) if len(diags) > 0 { _, _, _ = client.Services.DeleteService(*createdService) // Cleanup return diags @@ -316,23 +332,18 @@ func resourceIAMServiceUpdate(ctx context.Context, d *schema.ResourceData, m int _, _, _ = client.Services.AddScopes(s, []string{}, toAdd) } } - if d.HasChange("self_managed_expires_on") || d.HasChange("self_managed_private_key") || d.HasChange("self_managed_certificate") { - _, newPrivateKey := d.GetChange("self_managed_private_key") + if d.HasChange("self_managed_certificate") || + d.HasChange("self_managed_certificate_nonsensitive") { _, newCertificate := d.GetChange("self_managed_certificate") - privateKey := d.Get("private_key").(string) + _, newCertificateNS := d.GetChange("self_managed_certificate_nonsensitive") + if newCertificateNS.(string) != "" { + newCertificate = newCertificateNS.(string) + } - if newPrivateKey.(string) == "" && newCertificate.(string) == "" && privateKey == "" { + if newCertificate.(string) == "" { return resourceIAMServiceRead(ctx, d, m) // Don't update anything } - if newCertificate.(string) != "" && newPrivateKey.(string) != "" { - return diag.FromErr(fmt.Errorf("you cannot set 'self_managed_private_key' value in combination with 'self_managed_certificate'")) - } - if newPrivateKey.(string) != "" { - diags = setSelfManagedPrivateKey(client, s, d) - } - if newCertificate.(string) != "" { - diags = setSelfManagedCertificate(client, s, d) - } + diags = setSelfManagedCertificate(client, s, newCertificate.(string)) if len(diags) > 0 { return diags } @@ -402,16 +413,15 @@ func setSelfManagedPrivateKey(client *iam.Client, service iam.Service, d *schema return diags } -func setSelfManagedCertificate(client *iam.Client, service iam.Service, d *schema.ResourceData) diag.Diagnostics { +func setSelfManagedCertificate(client *iam.Client, service iam.Service, selfCertificate string) diag.Diagnostics { var diags diag.Diagnostics - selfCertificate := d.Get("self_managed_certificate").(string) fixedPEM := iam.FixPEM(selfCertificate) block, _ := pem.Decode([]byte(fixedPEM)) if block == nil { block, _ = pem.Decode([]byte(selfCertificate)) // Try unmodified decode if block == nil { - return diag.FromErr(fmt.Errorf("error decoding 'self_managed_certificate'")) + return diag.FromErr(fmt.Errorf("error decoding certificate")) } } cert, err := x509.ParseCertificate(block.Bytes) @@ -424,10 +434,7 @@ func setSelfManagedCertificate(client *iam.Client, service iam.Service, d *schem } _, _, err = client.Services.UpdateServiceCertificateDER(service, block.Bytes) if err != nil { - return diag.FromErr(fmt.Errorf("setting certificate: %w", err)) - } - if fixedPEM != "" { - _ = d.Set("private_key", nil) + return diag.FromErr(fmt.Errorf("error setting certificate: %w", err)) } return diags } diff --git a/internal/services/iam/service/resource_iam_service_v5.go b/internal/services/iam/service/resource_iam_service_v5.go new file mode 100644 index 00000000..13ef7f09 --- /dev/null +++ b/internal/services/iam/service/resource_iam_service_v5.go @@ -0,0 +1,97 @@ +package service + +import ( + "context" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + "github.com/philips-software/terraform-provider-hsdp/internal/tools" +) + +// Upgrades an IAM Service resource from v4 to v5 +func patchIAMServiceV5(_ context.Context, rawState map[string]interface{}, _ interface{}) (map[string]interface{}, error) { + if rawState == nil { + rawState = map[string]interface{}{} + } + return rawState, nil +} + +func ResourceIAMServiceV5() *schema.Resource { + return &schema.Resource{ + // This is only used for state migration, so the CRUD + // callbacks are no longer relevant + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + DiffSuppressFunc: tools.SuppressCaseDiffs, + }, + "description": { + Type: schema.TypeString, + ForceNew: true, + Required: true, + }, + "application_id": { + Type: schema.TypeString, + ForceNew: true, + Required: true, + }, + "validity": { + Type: schema.TypeInt, + Optional: true, + Default: 12, + ForceNew: true, + ValidateFunc: validation.IntBetween(1, 600), + }, + "token_validity": { + Type: schema.TypeInt, + Optional: true, + Default: 1800, + ValidateFunc: validation.IntBetween(0, 2592000), + }, + "self_managed_private_key": { + Type: schema.TypeString, + Sensitive: true, + Optional: true, + }, + "self_managed_certificate": { + Type: schema.TypeString, + Optional: true, + Deprecated: "Use 'self_managed_private_key' instead. This will be removed in a future version", + }, + "private_key": { + Type: schema.TypeString, + Sensitive: true, + Computed: true, + }, + "service_id": { + Type: schema.TypeString, + Computed: true, + }, + "organization_id": { + Type: schema.TypeString, + Computed: true, + }, + "expires_on": { + Type: schema.TypeString, + Optional: true, + Computed: true, + DiffSuppressFunc: tools.SuppressWhenGenerated, + }, + "scopes": { + Type: schema.TypeSet, + MaxItems: 100, + MinItems: 1, // openid + Required: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "default_scopes": { + Type: schema.TypeSet, + MaxItems: 100, + MinItems: 1, // openid + Required: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + }, + } +}