From e9c4ee66ca0fbd10f28343a5ee8bdea0532c06c6 Mon Sep 17 00:00:00 2001 From: Marcel Jacek Date: Thu, 19 Mar 2026 13:06:06 +0100 Subject: [PATCH] feat(secretsmanager): add support for kms keys relates to STACKITTPR-539 --- README.md | 12 +- docs/data-sources/secretsmanager_instance.md | 11 ++ docs/resources/secretsmanager_instance.md | 11 ++ .../secretsmanager/instance/datasource.go | 39 +++++- .../secretsmanager/instance/resource.go | 117 +++++++++++++++--- .../secretsmanager/instance/resource_test.go | 103 +++++++++++++++ .../secretsmanager/secretsmanager_acc_test.go | 93 +++++++++++++- .../secretsmanager/testdata/resource-max.tf | 40 ++++++ 8 files changed, 394 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index 36de53f4f..b0969c998 100644 --- a/README.md +++ b/README.md @@ -222,12 +222,12 @@ Terraform acceptance tests are run using the command `make test-acceptance-tf`. Additionally: -| Env var | Value | Example value | needed for Acc tests of the following services | -|---------------------------------------------|---------------------------------------------------------------------------------------------------------|----------------------------------------|------------------------------------------------| -| `TF_ACC_ORGANIZATION_ID` | ID of the STACKIT test organization | `5353ccfa-a984-4b96-a71d-b863dd2b7087` | `authorization`, `iaas` | -| `TF_ACC_TEST_PROJECT_SERVICE_ACCOUNT_EMAIL` | Email of the STACKIT service account | `abc-serviceaccount@sa.stackit.cloud` | `authorization`, `resourcemanager` | | -| `TF_ACC_TEST_PROJECT_PARENT_CONTAINER_ID` | Container ID of the project parent container (folder within an organization or the organization itself) | `organization-d2b7087` | `resourcemanager` | -| `TF_ACC_TEST_PROJECT_PARENT_UUID` | UUID ID of the project parent container (folder within an organization or the organization itself) | `5353ccfa-a984-4b96-a71d-b863dd2b7087` | `resourcemanager` | +| Env var | Value | Example value | needed for Acc tests of the following services | +|---------------------------------------------|---------------------------------------------------------------------------------------------------------|----------------------------------------|--------------------------------------------------------------| +| `TF_ACC_ORGANIZATION_ID` | ID of the STACKIT test organization | `5353ccfa-a984-4b96-a71d-b863dd2b7087` | `authorization`, `iaas` | +| `TF_ACC_TEST_PROJECT_SERVICE_ACCOUNT_EMAIL` | Email of the STACKIT service account | `abc-serviceaccount@sa.stackit.cloud` | `authorization`, `iaas`, `resourcemanager`, `secretsmanager` | +| `TF_ACC_TEST_PROJECT_PARENT_CONTAINER_ID` | Container ID of the project parent container (folder within an organization or the organization itself) | `organization-d2b7087` | `resourcemanager` | +| `TF_ACC_TEST_PROJECT_PARENT_UUID` | UUID ID of the project parent container (folder within an organization or the organization itself) | `5353ccfa-a984-4b96-a71d-b863dd2b7087` | `resourcemanager` | ### Run Acceptance Tests of a single service diff --git a/docs/data-sources/secretsmanager_instance.md b/docs/data-sources/secretsmanager_instance.md index 7f8e903e7..1dfe066b9 100644 --- a/docs/data-sources/secretsmanager_instance.md +++ b/docs/data-sources/secretsmanager_instance.md @@ -31,4 +31,15 @@ data "stackit_secretsmanager_instance" "example" { - `acls` (Set of String) The access control list for this instance. Each entry is an IP or IP range that is permitted to access, in CIDR notation - `id` (String) Terraform's internal resource ID. It is structured as "`project_id`,`instance_id`". +- `kms_key` (Attributes) The STACKIT-KMS key for secret encryption and decryption. (see [below for nested schema](#nestedatt--kms_key)) - `name` (String) Instance name. + + +### Nested Schema for `kms_key` + +Read-Only: + +- `key_id` (String) UUID of the key within the STACKIT-KMS to use for the encryption. +- `key_ring_id` (String) UUID of the keyring where the key is located within the STACKTI-KMS. +- `key_version` (Number) Version of the key within the STACKIT-KMS to use for the encryption. +- `service_account_email` (String) Service-Account linked to the Key within the STACKIT-KMS. diff --git a/docs/resources/secretsmanager_instance.md b/docs/resources/secretsmanager_instance.md index 8848b37de..0c5b67db0 100644 --- a/docs/resources/secretsmanager_instance.md +++ b/docs/resources/secretsmanager_instance.md @@ -37,8 +37,19 @@ import { ### Optional - `acls` (Set of String) The access control list for this instance. Each entry is an IP or IP range that is permitted to access, in CIDR notation +- `kms_key` (Attributes) The STACKIT-KMS key for secret encryption and decryption. (see [below for nested schema](#nestedatt--kms_key)) ### Read-Only - `id` (String) Terraform's internal resource ID. It is structured as "`project_id`,`instance_id`". - `instance_id` (String) ID of the Secrets Manager instance. + + +### Nested Schema for `kms_key` + +Required: + +- `key_id` (String) UUID of the key within the STACKIT-KMS to use for the encryption. +- `key_ring_id` (String) UUID of the keyring where the key is located within the STACKTI-KMS. +- `key_version` (Number) Version of the key within the STACKIT-KMS to use for the encryption. +- `service_account_email` (String) Service-Account linked to the Key within the STACKIT-KMS. diff --git a/stackit/internal/services/secretsmanager/instance/datasource.go b/stackit/internal/services/secretsmanager/instance/datasource.go index d17f40016..931c2c1fc 100644 --- a/stackit/internal/services/secretsmanager/instance/datasource.go +++ b/stackit/internal/services/secretsmanager/instance/datasource.go @@ -58,12 +58,17 @@ func (r *instanceDataSource) Configure(ctx context.Context, req datasource.Confi // Schema defines the schema for the data source. func (r *instanceDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) { descriptions := map[string]string{ - "main": "Secrets Manager instance data source schema. Must have a `region` specified in the provider configuration.", - "id": "Terraform's internal resource ID. It is structured as \"`project_id`,`instance_id`\".", - "instance_id": "ID of the Secrets Manager instance.", - "project_id": "STACKIT project ID to which the instance is associated.", - "name": "Instance name.", - "acls": "The access control list for this instance. Each entry is an IP or IP range that is permitted to access, in CIDR notation", + "main": "Secrets Manager instance data source schema. Must have a `region` specified in the provider configuration.", + "id": "Terraform's internal resource ID. It is structured as \"`project_id`,`instance_id`\".", + "instance_id": "ID of the Secrets Manager instance.", + "project_id": "STACKIT project ID to which the instance is associated.", + "name": "Instance name.", + "acls": "The access control list for this instance. Each entry is an IP or IP range that is permitted to access, in CIDR notation", + "kms_key": "The STACKIT-KMS key for secret encryption and decryption.", + "kms_key.key_id": "UUID of the key within the STACKIT-KMS to use for the encryption.", + "kms_key.key_ring_id": "UUID of the keyring where the key is located within the STACKTI-KMS.", + "kms_key.key_version": "Version of the key within the STACKIT-KMS to use for the encryption.", + "kms_key.service_account_email": "Service-Account linked to the Key within the STACKIT-KMS.", } resp.Schema = schema.Schema{ @@ -98,6 +103,28 @@ func (r *instanceDataSource) Schema(_ context.Context, _ datasource.SchemaReques ElementType: types.StringType, Computed: true, }, + "kms_key": schema.SingleNestedAttribute{ + Description: descriptions["kms_key"], + Computed: true, + Attributes: map[string]schema.Attribute{ + "key_id": schema.StringAttribute{ + Description: descriptions["kms_key.key_id"], + Computed: true, + }, + "key_ring_id": schema.StringAttribute{ + Description: descriptions["kms_key.key_ring_id"], + Computed: true, + }, + "key_version": schema.Int64Attribute{ + Description: descriptions["kms_key.key_version"], + Computed: true, + }, + "service_account_email": schema.StringAttribute{ + Description: descriptions["kms_key.service_account_email"], + Computed: true, + }, + }, + }, }, } } diff --git a/stackit/internal/services/secretsmanager/instance/resource.go b/stackit/internal/services/secretsmanager/instance/resource.go index cb56df6a2..d61021ca3 100644 --- a/stackit/internal/services/secretsmanager/instance/resource.go +++ b/stackit/internal/services/secretsmanager/instance/resource.go @@ -42,6 +42,14 @@ type Model struct { ProjectId types.String `tfsdk:"project_id"` Name types.String `tfsdk:"name"` ACLs types.Set `tfsdk:"acls"` + KmsKey *KmsKeyModel `tfsdk:"kms_key"` +} + +type KmsKeyModel struct { + KeyId types.String `tfsdk:"key_id"` + KeyRingId types.String `tfsdk:"key_ring_id"` + KeyVersion types.Int64 `tfsdk:"key_version"` + ServiceAccountEmail types.String `tfsdk:"service_account_email"` } // NewInstanceResource is a helper function to simplify the provider implementation. @@ -77,12 +85,17 @@ func (r *instanceResource) Configure(ctx context.Context, req resource.Configure // Schema defines the schema for the resource. func (r *instanceResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { descriptions := map[string]string{ - "main": "Secrets Manager instance resource schema. Must have a `region` specified in the provider configuration.", - "id": "Terraform's internal resource ID. It is structured as \"`project_id`,`instance_id`\".", - "instance_id": "ID of the Secrets Manager instance.", - "project_id": "STACKIT project ID to which the instance is associated.", - "name": "Instance name.", - "acls": "The access control list for this instance. Each entry is an IP or IP range that is permitted to access, in CIDR notation", + "main": "Secrets Manager instance resource schema. Must have a `region` specified in the provider configuration.", + "id": "Terraform's internal resource ID. It is structured as \"`project_id`,`instance_id`\".", + "instance_id": "ID of the Secrets Manager instance.", + "project_id": "STACKIT project ID to which the instance is associated.", + "name": "Instance name.", + "acls": "The access control list for this instance. Each entry is an IP or IP range that is permitted to access, in CIDR notation", + "kms_key": "The STACKIT-KMS key for secret encryption and decryption.", + "kms_key.key_id": "UUID of the key within the STACKIT-KMS to use for the encryption.", + "kms_key.key_ring_id": "UUID of the keyring where the key is located within the STACKTI-KMS.", + "kms_key.key_version": "Version of the key within the STACKIT-KMS to use for the encryption.", + "kms_key.service_account_email": "Service-Account linked to the Key within the STACKIT-KMS.", } resp.Schema = schema.Schema{ @@ -118,11 +131,9 @@ func (r *instanceResource) Schema(_ context.Context, _ resource.SchemaRequest, r }, }, "name": schema.StringAttribute{ - Description: descriptions["name"], - Required: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.RequiresReplace(), - }, + Description: descriptions["name"], + Required: true, + PlanModifiers: []planmodifier.String{}, Validators: []validator.String{ stringvalidator.LengthAtLeast(1), }, @@ -137,6 +148,28 @@ func (r *instanceResource) Schema(_ context.Context, _ resource.SchemaRequest, r ), }, }, + "kms_key": schema.SingleNestedAttribute{ + Description: descriptions["kms_key"], + Optional: true, + Attributes: map[string]schema.Attribute{ + "key_id": schema.StringAttribute{ + Description: descriptions["kms_key.key_id"], + Required: true, + }, + "key_ring_id": schema.StringAttribute{ + Description: descriptions["kms_key.key_ring_id"], + Required: true, + }, + "key_version": schema.Int64Attribute{ + Description: descriptions["kms_key.key_version"], + Required: true, + }, + "service_account_email": schema.StringAttribute{ + Description: descriptions["kms_key.service_account_email"], + Required: true, + }, + }, + }, }, } } @@ -284,6 +317,21 @@ func (r *instanceResource) Update(ctx context.Context, req resource.UpdateReques ctx = tflog.SetField(ctx, "project_id", projectId) ctx = tflog.SetField(ctx, "instance_id", instanceId) + // Generate API request body from model + payload, err := toUpdatePayload(&model) + if err != nil { + core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating instance", fmt.Sprintf("Creating API payload: %v", err)) + return + } + // Update instance + err = r.client.UpdateInstance(ctx, projectId, instanceId).UpdateInstancePayload(*payload).Execute() + if err != nil { + core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating instance", fmt.Sprintf("Calling API: %v", err)) + return + } + + ctx = core.LogResponse(ctx) + var acls []string if !(model.ACLs.IsNull() || model.ACLs.IsUnknown()) { diags = model.ACLs.ElementsAs(ctx, &acls, false) @@ -294,7 +342,7 @@ func (r *instanceResource) Update(ctx context.Context, req resource.UpdateReques } // Update ACLs - err := updateACLs(ctx, projectId, instanceId, acls, r.client) + err = updateACLs(ctx, projectId, instanceId, acls, r.client) if err != nil { core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating instance", fmt.Sprintf("Updating ACLs: %v", err)) return @@ -398,6 +446,15 @@ func mapFields(instance *secretsmanager.Instance, aclList *secretsmanager.ListAC model.InstanceId = types.StringValue(instanceId) model.Name = types.StringPointerValue(instance.Name) + if instance.KmsKey != nil { + model.KmsKey = &KmsKeyModel{ + KeyId: types.StringPointerValue(instance.KmsKey.KeyId), + KeyRingId: types.StringPointerValue(instance.KmsKey.KeyRingId), + KeyVersion: types.Int64PointerValue(instance.KmsKey.KeyVersion), + ServiceAccountEmail: types.StringPointerValue(instance.KmsKey.ServiceAccountEmail), + } + } + err := mapACLs(aclList, model) if err != nil { return err @@ -431,9 +488,41 @@ func toCreatePayload(model *Model) (*secretsmanager.CreateInstancePayload, error if model == nil { return nil, fmt.Errorf("nil model") } - return &secretsmanager.CreateInstancePayload{ + payload := &secretsmanager.CreateInstancePayload{ + Name: conversion.StringValueToPointer(model.Name), + } + + if model.KmsKey != nil { + payload.KmsKey = &secretsmanager.KmsKeyPayload{ + KeyId: conversion.StringValueToPointer(model.KmsKey.KeyId), + KeyRingId: conversion.StringValueToPointer(model.KmsKey.KeyRingId), + KeyVersion: conversion.Int64ValueToPointer(model.KmsKey.KeyVersion), + ServiceAccountEmail: conversion.StringValueToPointer(model.KmsKey.ServiceAccountEmail), + } + } + + return payload, nil +} + +func toUpdatePayload(model *Model) (*secretsmanager.UpdateInstancePayload, error) { + if model == nil { + return nil, fmt.Errorf("nil model") + } + + payload := &secretsmanager.UpdateInstancePayload{ Name: conversion.StringValueToPointer(model.Name), - }, nil + } + + if model.KmsKey != nil { + payload.KmsKey = &secretsmanager.KmsKeyPayload{ + KeyId: conversion.StringValueToPointer(model.KmsKey.KeyId), + KeyRingId: conversion.StringValueToPointer(model.KmsKey.KeyRingId), + KeyVersion: conversion.Int64ValueToPointer(model.KmsKey.KeyVersion), + ServiceAccountEmail: conversion.StringValueToPointer(model.KmsKey.ServiceAccountEmail), + } + } + + return payload, nil } // updateACLs creates and deletes ACLs so that the instance's ACLs are the ones in the model diff --git a/stackit/internal/services/secretsmanager/instance/resource_test.go b/stackit/internal/services/secretsmanager/instance/resource_test.go index 39d2df834..6c821a025 100644 --- a/stackit/internal/services/secretsmanager/instance/resource_test.go +++ b/stackit/internal/services/secretsmanager/instance/resource_test.go @@ -141,6 +141,28 @@ func TestToCreatePayload(t *testing.T) { }, true, }, + { + "with_kms_key", + &Model{ + Name: types.StringValue("name"), + KmsKey: &KmsKeyModel{ + KeyId: types.StringValue("kid"), + KeyRingId: types.StringValue("key-ring-id"), + KeyVersion: types.Int64Value(1), + ServiceAccountEmail: types.StringValue("service-account-email"), + }, + }, + &secretsmanager.CreateInstancePayload{ + Name: utils.Ptr("name"), + KmsKey: &secretsmanager.KmsKeyPayload{ + KeyId: utils.Ptr("kid"), + KeyRingId: utils.Ptr("key-ring-id"), + KeyVersion: utils.Ptr(int64(1)), + ServiceAccountEmail: utils.Ptr("service-account-email"), + }, + }, + true, + }, { "null_fields_and_int_conversions", &Model{ @@ -485,3 +507,84 @@ func TestUpdateACLs(t *testing.T) { }) } } + +func TestToUpdatePayload(t *testing.T) { + tests := []struct { + description string + input *Model + expected *secretsmanager.UpdateInstancePayload + isValid bool + }{ + { + "default_values", + &Model{}, + &secretsmanager.UpdateInstancePayload{}, + true, + }, + { + "simple_values", + &Model{ + Name: types.StringValue("name"), + }, + &secretsmanager.UpdateInstancePayload{ + Name: utils.Ptr("name"), + }, + true, + }, + { + "with_kms_key", + &Model{ + Name: types.StringValue("name"), + KmsKey: &KmsKeyModel{ + KeyId: types.StringValue("kid"), + KeyRingId: types.StringValue("key-ring-id"), + KeyVersion: types.Int64Value(1), + ServiceAccountEmail: types.StringValue("service-account-email"), + }, + }, + &secretsmanager.UpdateInstancePayload{ + Name: utils.Ptr("name"), + KmsKey: &secretsmanager.KmsKeyPayload{ + KeyId: utils.Ptr("kid"), + KeyRingId: utils.Ptr("key-ring-id"), + KeyVersion: utils.Ptr(int64(1)), + ServiceAccountEmail: utils.Ptr("service-account-email"), + }, + }, + true, + }, + { + "null_fields_and_int_conversions", + &Model{ + Name: types.StringValue(""), + }, + &secretsmanager.UpdateInstancePayload{ + Name: utils.Ptr(""), + }, + true, + }, + { + "nil_model", + nil, + nil, + false, + }, + } + for _, tt := range tests { + t.Run(tt.description, func(t *testing.T) { + output, err := toUpdatePayload(tt.input) + if !tt.isValid && err == nil { + t.Fatalf("Should have failed") + } + if tt.isValid && err != nil { + t.Fatalf("Should not have failed: %v", err) + } + if tt.isValid { + diff := cmp.Diff(output, tt.expected) + if diff != "" { + t.Fatalf("Data does not match: %s", diff) + } + } + }) + } +} diff --git a/stackit/internal/services/secretsmanager/secretsmanager_acc_test.go b/stackit/internal/services/secretsmanager/secretsmanager_acc_test.go index 16e6c1101..27c7c0e96 100644 --- a/stackit/internal/services/secretsmanager/secretsmanager_acc_test.go +++ b/stackit/internal/services/secretsmanager/secretsmanager_acc_test.go @@ -12,6 +12,7 @@ import ( "github.com/hashicorp/terraform-plugin-testing/config" "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/plancheck" "github.com/hashicorp/terraform-plugin-testing/terraform" core_config "github.com/stackitcloud/stackit-sdk-go/core/config" "github.com/stackitcloud/stackit-sdk-go/core/utils" @@ -36,12 +37,14 @@ var testConfigVarsMin = config.Variables{ } var testConfigVarsMax = config.Variables{ - "project_id": config.StringVariable(testutil.ProjectId), - "instance_name": config.StringVariable("tf-acc-" + acctest.RandStringFromCharSet(8, acctest.CharSetAlpha)), - "user_description": config.StringVariable("tf-acc-" + acctest.RandStringFromCharSet(8, acctest.CharSetAlpha)), - "acl1": config.StringVariable("10.100.0.0/24"), - "acl2": config.StringVariable("10.100.1.0/24"), - "write_enabled": config.BoolVariable(true), + "project_id": config.StringVariable(testutil.ProjectId), + "instance_name": config.StringVariable("tf-acc-" + acctest.RandStringFromCharSet(8, acctest.CharSetAlpha)), + "user_description": config.StringVariable("tf-acc-" + acctest.RandStringFromCharSet(8, acctest.CharSetAlpha)), + "acl1": config.StringVariable("10.100.0.0/24"), + "acl2": config.StringVariable("10.100.1.0/24"), + "write_enabled": config.BoolVariable(true), + "service_account_mail": config.StringVariable(testutil.TestProjectServiceAccountEmail), + "use_kms_key": config.BoolVariable(true), } func configVarsInvalid(vars config.Variables) config.Variables { @@ -52,6 +55,7 @@ func configVarsInvalid(vars config.Variables) config.Variables { func configVarsMinUpdated() config.Variables { tempConfig := maps.Clone(testConfigVarsMin) + tempConfig["instance_name"] = config.StringVariable(testutil.ConvertConfigVariable(tempConfig["instance_name"]) + "-updated") tempConfig["write_enabled"] = config.BoolVariable(false) return tempConfig } @@ -59,6 +63,7 @@ func configVarsMinUpdated() config.Variables { func configVarsMaxUpdated() config.Variables { tempConfig := maps.Clone(testConfigVarsMax) tempConfig["write_enabled"] = config.BoolVariable(false) + tempConfig["use_kms_key"] = config.BoolVariable(false) tempConfig["acl2"] = config.StringVariable("10.100.2.0/24") return tempConfig } @@ -183,6 +188,12 @@ func TestAccSecretsManagerMin(t *testing.T) { { Config: resourceMinConfig, ConfigVariables: configVarsMinUpdated(), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction("stackit_secretsmanager_user.user", plancheck.ResourceActionUpdate), + plancheck.ExpectResourceAction("stackit_secretsmanager_instance.instance", plancheck.ResourceActionUpdate), + }, + }, Check: resource.ComposeAggregateTestCheckFunc( // Instance resource.TestCheckResourceAttr("stackit_secretsmanager_instance.instance", "project_id", testutil.ConvertConfigVariable(configVarsMinUpdated()["project_id"])), @@ -250,6 +261,24 @@ func TestAccSecretsManagerMax(t *testing.T) { resource.TestCheckResourceAttr("stackit_secretsmanager_user.user", "write_enabled", testutil.ConvertConfigVariable(testConfigVarsMax["write_enabled"])), resource.TestCheckResourceAttrSet("stackit_secretsmanager_user.user", "username"), resource.TestCheckResourceAttrSet("stackit_secretsmanager_user.user", "password"), + + // Instance with kms key + resource.TestCheckResourceAttr("stackit_secretsmanager_instance.instance_with_key", "project_id", testutil.ConvertConfigVariable(testConfigVarsMax["project_id"])), + resource.TestCheckResourceAttrSet("stackit_secretsmanager_instance.instance_with_key", "instance_id"), + resource.TestCheckResourceAttr("stackit_secretsmanager_instance.instance_with_key", "name", testutil.ConvertConfigVariable(testConfigVarsMax["instance_name"])), + resource.TestCheckResourceAttr("stackit_secretsmanager_instance.instance_with_key", "acls.#", "2"), + resource.TestCheckResourceAttr("stackit_secretsmanager_instance.instance_with_key", "acls.0", testutil.ConvertConfigVariable(testConfigVarsMax["acl1"])), + resource.TestCheckResourceAttr("stackit_secretsmanager_instance.instance_with_key", "acls.1", testutil.ConvertConfigVariable(testConfigVarsMax["acl2"])), + resource.TestCheckResourceAttrPair( + "stackit_secretsmanager_instance.instance_with_key", "kms_key.key_id", + "stackit_kms_key.key", "key_id", + ), + resource.TestCheckResourceAttrPair( + "stackit_secretsmanager_instance.instance_with_key", "kms_key.key_ring_id", + "stackit_kms_keyring.keyring", "keyring_id", + ), + resource.TestCheckResourceAttr("stackit_secretsmanager_instance.instance_with_key", "kms_key.key_version", "1"), + resource.TestCheckResourceAttr("stackit_secretsmanager_instance.instance_with_key", "kms_key.service_account_email", testutil.ConvertConfigVariable(testConfigVarsMax["service_account_mail"])), ), }, // Data source @@ -287,6 +316,24 @@ func TestAccSecretsManagerMax(t *testing.T) { "stackit_secretsmanager_user.user", "username", "data.stackit_secretsmanager_user.user", "username", ), + + // Instance with kms key + resource.TestCheckResourceAttr("data.stackit_secretsmanager_instance.instance_with_key", "project_id", testutil.ConvertConfigVariable(testConfigVarsMax["project_id"])), + resource.TestCheckResourceAttrSet("data.stackit_secretsmanager_instance.instance_with_key", "instance_id"), + resource.TestCheckResourceAttr("data.stackit_secretsmanager_instance.instance_with_key", "name", testutil.ConvertConfigVariable(testConfigVarsMax["instance_name"])), + resource.TestCheckResourceAttr("data.stackit_secretsmanager_instance.instance_with_key", "acls.#", "2"), + resource.TestCheckResourceAttr("data.stackit_secretsmanager_instance.instance_with_key", "acls.0", testutil.ConvertConfigVariable(testConfigVarsMax["acl1"])), + resource.TestCheckResourceAttr("data.stackit_secretsmanager_instance.instance_with_key", "acls.1", testutil.ConvertConfigVariable(testConfigVarsMax["acl2"])), + resource.TestCheckResourceAttrPair( + "data.stackit_secretsmanager_instance.instance_with_key", "kms_key.key_id", + "stackit_kms_key.key", "key_id", + ), + resource.TestCheckResourceAttrPair( + "data.stackit_secretsmanager_instance.instance_with_key", "kms_key.key_ring_id", + "stackit_kms_keyring.keyring", "keyring_id", + ), + resource.TestCheckResourceAttr("data.stackit_secretsmanager_instance.instance_with_key", "kms_key.key_version", "1"), + resource.TestCheckResourceAttr("data.stackit_secretsmanager_instance.instance_with_key", "kms_key.service_account_email", testutil.ConvertConfigVariable(testConfigVarsMax["service_account_mail"])), ), }, // Import @@ -307,6 +354,24 @@ func TestAccSecretsManagerMax(t *testing.T) { ImportState: true, ImportStateVerify: true, }, + // Import + { + ConfigVariables: testConfigVarsMax, + ResourceName: "stackit_secretsmanager_instance.instance_with_key", + ImportStateIdFunc: func(s *terraform.State) (string, error) { + r, ok := s.RootModule().Resources["stackit_secretsmanager_instance.instance_with_key"] + if !ok { + return "", fmt.Errorf("couldn't find resource stackit_secretsmanager_instance.instance_with_key") + } + instanceId, ok := r.Primary.Attributes["instance_id"] + if !ok { + return "", fmt.Errorf("couldn't find attribute instance_id") + } + return fmt.Sprintf("%s,%s", testutil.ProjectId, instanceId), nil + }, + ImportState: true, + ImportStateVerify: true, + }, { Config: resourceMaxConfig, ConfigVariables: testConfigVarsMax, @@ -336,6 +401,13 @@ func TestAccSecretsManagerMax(t *testing.T) { { Config: resourceMaxConfig, ConfigVariables: configVarsMaxUpdated(), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction("stackit_secretsmanager_user.user", plancheck.ResourceActionUpdate), + plancheck.ExpectResourceAction("stackit_secretsmanager_instance.instance", plancheck.ResourceActionUpdate), + plancheck.ExpectResourceAction("stackit_secretsmanager_instance.instance_with_key", plancheck.ResourceActionUpdate), + }, + }, Check: resource.ComposeAggregateTestCheckFunc( // Instance resource.TestCheckResourceAttr("stackit_secretsmanager_instance.instance", "project_id", testutil.ConvertConfigVariable(configVarsMaxUpdated()["project_id"])), @@ -359,6 +431,15 @@ func TestAccSecretsManagerMax(t *testing.T) { resource.TestCheckResourceAttr("stackit_secretsmanager_user.user", "write_enabled", testutil.ConvertConfigVariable(configVarsMaxUpdated()["write_enabled"])), resource.TestCheckResourceAttrSet("stackit_secretsmanager_user.user", "username"), resource.TestCheckResourceAttrSet("stackit_secretsmanager_user.user", "password"), + + // Instance with kms key + resource.TestCheckResourceAttr("stackit_secretsmanager_instance.instance_with_key", "project_id", testutil.ConvertConfigVariable(configVarsMaxUpdated()["project_id"])), + resource.TestCheckResourceAttrSet("stackit_secretsmanager_instance.instance_with_key", "instance_id"), + resource.TestCheckResourceAttr("stackit_secretsmanager_instance.instance_with_key", "name", testutil.ConvertConfigVariable(configVarsMaxUpdated()["instance_name"])), + resource.TestCheckResourceAttr("stackit_secretsmanager_instance.instance_with_key", "acls.#", "2"), + resource.TestCheckResourceAttr("stackit_secretsmanager_instance.instance_with_key", "acls.0", testutil.ConvertConfigVariable(configVarsMaxUpdated()["acl1"])), + resource.TestCheckResourceAttr("stackit_secretsmanager_instance.instance_with_key", "acls.1", testutil.ConvertConfigVariable(configVarsMaxUpdated()["acl2"])), + resource.TestCheckNoResourceAttr("stackit_secretsmanager_instance.instance_with_key", "kms_key"), ), }, // Deletion is done by the framework implicitly diff --git a/stackit/internal/services/secretsmanager/testdata/resource-max.tf b/stackit/internal/services/secretsmanager/testdata/resource-max.tf index deea23ba4..cb882a16a 100644 --- a/stackit/internal/services/secretsmanager/testdata/resource-max.tf +++ b/stackit/internal/services/secretsmanager/testdata/resource-max.tf @@ -4,6 +4,8 @@ variable "user_description" {} variable "write_enabled" {} variable "acl1" {} variable "acl2" {} +variable "service_account_mail" {} +variable "use_kms_key" {} resource "stackit_secretsmanager_instance" "instance" { project_id = var.project_id @@ -32,3 +34,41 @@ data "stackit_secretsmanager_user" "user" { instance_id = stackit_secretsmanager_instance.instance.instance_id user_id = stackit_secretsmanager_user.user.user_id } + +# just needed for the test setup for secretsmanager with own keys +resource "stackit_kms_keyring" "keyring" { + project_id = var.project_id + display_name = var.instance_name +} + +# just needed for the test setup for secretsmanager with own keys +resource "stackit_kms_key" "key" { + project_id = var.project_id + keyring_id = stackit_kms_keyring.keyring.keyring_id + display_name = var.instance_name + protection = "software" + algorithm = "aes_256_gcm" + purpose = "symmetric_encrypt_decrypt" +} + +resource "stackit_secretsmanager_instance" "instance_with_key" { + project_id = var.project_id + name = var.instance_name + acls = [ + var.acl1, + var.acl2, + ] + + kms_key = var.use_kms_key ? { + key_id = stackit_kms_key.key.key_id + key_ring_id = stackit_kms_keyring.keyring.keyring_id + key_version = 1 + service_account_email = var.service_account_mail + } : null +} + + +data "stackit_secretsmanager_instance" "instance_with_key" { + project_id = var.project_id + instance_id = stackit_secretsmanager_instance.instance_with_key.instance_id +}