diff --git a/mongodbatlas/framework/validator/aws_kms_config_validator.go b/mongodbatlas/framework/validator/aws_kms_config_validator.go new file mode 100644 index 0000000000..0a9dde91ae --- /dev/null +++ b/mongodbatlas/framework/validator/aws_kms_config_validator.go @@ -0,0 +1,54 @@ +package validator + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework-validators/helpers/validatordiag" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" +) + +type awsKmsConfigValidator struct{} + +func (v awsKmsConfigValidator) Description(_ context.Context) string { + return "for credentials: `access_key_id` and `secret_access_key` are allowed but not `role_id`." + + " For roles: `access_key_id` and `secret_access_key` are not allowed but `role_id` is allowed" +} + +func (v awsKmsConfigValidator) MarkdownDescription(ctx context.Context) string { + return v.Description(ctx) +} + +func (v awsKmsConfigValidator) ValidateObject(ctx context.Context, req validator.ObjectRequest, response *validator.ObjectResponse) { + // If the value is unknown or null, there is nothing to validate. + if req.ConfigValue.IsUnknown() || req.ConfigValue.IsNull() { + return + } + + obj, diag := req.ConfigValue.ToObjectValue(ctx) + if diag.HasError() { + response.Diagnostics.Append(diag.Errors()...) + return + } + + attrMap := obj.Attributes() + ak, akOk := attrMap["access_key_id"] + sa, saOk := attrMap["secret_access_key"] + r, rOk := attrMap["role_id"] + accessKeyDefined := akOk && !ak.IsNull() + secretAccessKeyDefined := saOk && !sa.IsNull() + roleIDDefined := rOk && !r.IsNull() + + if (accessKeyDefined && secretAccessKeyDefined && roleIDDefined) || + (accessKeyDefined && roleIDDefined) || + (secretAccessKeyDefined && roleIDDefined) { + response.Diagnostics.Append(validatordiag.InvalidAttributeValueDiagnostic( + req.Path, + v.Description(ctx), + req.ConfigValue.String(), + )) + } +} + +func AwsKmsConfig() validator.Object { + return awsKmsConfigValidator{} +} diff --git a/mongodbatlas/framework/validator/aws_kms_config_validator_test.go b/mongodbatlas/framework/validator/aws_kms_config_validator_test.go new file mode 100644 index 0000000000..4b36be62f7 --- /dev/null +++ b/mongodbatlas/framework/validator/aws_kms_config_validator_test.go @@ -0,0 +1,105 @@ +package validator + +import ( + "context" + "testing" + + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +func TestValidAwsKmsConfig(t *testing.T) { + enabled := true + validType1 := map[string]attr.Type{ + "enabled": types.BoolType, + "customer_master_key_id": types.StringType, + "region": types.StringType, + "role_id": types.StringType, + } + validValue1 := map[string]attr.Value{ + "enabled": types.BoolValue(enabled), + "customer_master_key_id": types.StringValue("testCustomerMasterKeyID"), + "region": types.StringValue("testRegion"), + "role_id": types.StringValue("testRoleID"), + } + validType2 := map[string]attr.Type{ + "enabled": types.BoolType, + "access_key_id": types.StringType, + "secret_access_key": types.StringType, + "customer_master_key_id": types.StringType, + "region": types.StringType, + } + validValue2 := map[string]attr.Value{ + "enabled": types.BoolValue(enabled), + "access_key_id": types.StringValue("testAccessKey"), + "secret_access_key": types.StringValue("testSecretAccessKey"), + "customer_master_key_id": types.StringValue("testCustomerMasterKeyID"), + "region": types.StringValue("testRegion"), + } + inValidType := map[string]attr.Type{ + "enabled": types.BoolType, + "access_key_id": types.StringType, + "secret_access_key": types.StringType, + "customer_master_key_id": types.StringType, + "region": types.StringType, + "role_id": types.StringType, + } + inValidValue := map[string]attr.Value{ + "enabled": types.BoolValue(enabled), + "access_key_id": types.StringValue("testAccessKey"), + "secret_access_key": types.StringValue("testSecretAccessKey"), + "customer_master_key_id": types.StringValue("testCustomerMasterKeyID"), + "region": types.StringValue("testRegion"), + "role_id": types.StringValue("testRoleID"), + } + + tests := []struct { + awsKmsConfigValue map[string]attr.Value + awsKmsConfigType map[string]attr.Type + name string + wantErr bool + }{ + { + name: "Valid Value 1", + awsKmsConfigValue: validValue1, + awsKmsConfigType: validType1, + wantErr: false, + }, + { + name: "Valid Value 2", + awsKmsConfigValue: validValue2, + awsKmsConfigType: validType2, + wantErr: false, + }, + { + name: "Invalid Value", + awsKmsConfigValue: inValidValue, + awsKmsConfigType: inValidType, + wantErr: true, + }, + } + + for _, tt := range tests { + wantErr := tt.wantErr + + awsKmsConfigValidator := awsKmsConfigValidator{} + validatorRequest := validator.ObjectRequest{ + ConfigValue: types.ObjectValueMust(tt.awsKmsConfigType, tt.awsKmsConfigValue), + } + + validatorResponse := validator.ObjectResponse{ + Diagnostics: diag.Diagnostics{}, + } + + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + awsKmsConfigValidator.ValidateObject(context.Background(), validatorRequest, &validatorResponse) + + if validatorResponse.Diagnostics.HasError() && !wantErr { + t.Errorf("error = %v, wantErr %v", validatorResponse.Diagnostics.Errors(), wantErr) + } + }) + } +} diff --git a/mongodbatlas/fw_provider.go b/mongodbatlas/fw_provider.go index f60bea3696..b2e5a102ba 100644 --- a/mongodbatlas/fw_provider.go +++ b/mongodbatlas/fw_provider.go @@ -22,6 +22,7 @@ import ( "github.com/hashicorp/terraform-plugin-mux/tf5to6server" "github.com/hashicorp/terraform-plugin-mux/tf6muxserver" sdkv2schema "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + cstmvalidator "github.com/mongodb/terraform-provider-mongodbatlas/mongodbatlas/framework/validator" "github.com/mongodb/terraform-provider-mongodbatlas/version" ) @@ -378,6 +379,7 @@ func (p *MongodbtlasProvider) DataSources(context.Context) []func() datasource.D func (p *MongodbtlasProvider) Resources(context.Context) []func() resource.Resource { return []func() resource.Resource{ NewProjectRS, + NewEncryptionAtRestRS, NewDatabaseUserRS, NewAlertConfigurationRS, } diff --git a/mongodbatlas/fw_resource_mongodbatlas_encryption_at_rest.go b/mongodbatlas/fw_resource_mongodbatlas_encryption_at_rest.go new file mode 100644 index 0000000000..c35279b88f --- /dev/null +++ b/mongodbatlas/fw_resource_mongodbatlas_encryption_at_rest.go @@ -0,0 +1,565 @@ +package mongodbatlas + +import ( + "context" + "fmt" + "log" + "net/http" + "reflect" + "strings" + "time" + + matlas "go.mongodb.org/atlas/mongodbatlas" + + "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/boolplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" + + "github.com/mongodb/terraform-provider-mongodbatlas/mongodbatlas/framework/conversion" + validators "github.com/mongodb/terraform-provider-mongodbatlas/mongodbatlas/framework/validator" +) + +const ( + encryptionAtRestResourceName = "encryption_at_rest" + errorCreateEncryptionAtRest = "error creating Encryption At Rest: %s" + errorReadEncryptionAtRest = "error getting Encryption At Rest: %s" + errorDeleteEncryptionAtRest = "error deleting Encryption At Rest: (%s): %s" + errorUpdateEncryptionAtRest = "error updating Encryption At Rest: %s" +) + +var _ resource.Resource = &EncryptionAtRestRS{} +var _ resource.ResourceWithImportState = &EncryptionAtRestRS{} + +func NewEncryptionAtRestRS() resource.Resource { + return &EncryptionAtRestRS{} +} + +type EncryptionAtRestRS struct { + client *MongoDBClient +} + +type tfEncryptionAtRestRSModel struct { + ID types.String `tfsdk:"id"` + ProjectID types.String `tfsdk:"project_id"` + AwsKmsConfig []tfAwsKmsConfigModel `tfsdk:"aws_kms_config"` + AzureKeyVaultConfig []tfAzureKeyVaultConfigModel `tfsdk:"azure_key_vault_config"` + GoogleCloudKmsConfig []tfGcpKmsConfigModel `tfsdk:"google_cloud_kms_config"` +} + +type tfAwsKmsConfigModel struct { + AccessKeyID types.String `tfsdk:"access_key_id"` + SecretAccessKey types.String `tfsdk:"secret_access_key"` + CustomerMasterKeyID types.String `tfsdk:"customer_master_key_id"` + Region types.String `tfsdk:"region"` + RoleID types.String `tfsdk:"role_id"` + Enabled types.Bool `tfsdk:"enabled"` +} +type tfAzureKeyVaultConfigModel struct { + ClientID types.String `tfsdk:"client_id"` + AzureEnvironment types.String `tfsdk:"azure_environment"` + SubscriptionID types.String `tfsdk:"subscription_id"` + ResourceGroupName types.String `tfsdk:"resource_group_name"` + KeyVaultName types.String `tfsdk:"key_vault_name"` + KeyIdentifier types.String `tfsdk:"key_identifier"` + Secret types.String `tfsdk:"secret"` + TenantID types.String `tfsdk:"tenant_id"` + Enabled types.Bool `tfsdk:"enabled"` +} +type tfGcpKmsConfigModel struct { + ServiceAccountKey types.String `tfsdk:"service_account_key"` + KeyVersionResourceID types.String `tfsdk:"key_version_resource_id"` + Enabled types.Bool `tfsdk:"enabled"` +} + +func (r *EncryptionAtRestRS) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = fmt.Sprintf("%s_%s", req.ProviderTypeName, encryptionAtRestResourceName) +} + +func (r *EncryptionAtRestRS) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + client, err := ConfigureClient(req.ProviderData) + if err != nil { + resp.Diagnostics.AddError(errorConfigureSummary, err.Error()) + return + } + r.client = client +} + +func (r *EncryptionAtRestRS) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + "project_id": schema.StringAttribute{ + Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + }, + }, + Blocks: map[string]schema.Block{ + "aws_kms_config": schema.ListNestedBlock{ + Validators: []validator.List{listvalidator.SizeAtMost(1)}, + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "enabled": schema.BoolAttribute{ + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.Bool{ + boolplanmodifier.UseStateForUnknown(), + }, + }, + "access_key_id": schema.StringAttribute{ + Optional: true, + Sensitive: true, + }, + "secret_access_key": schema.StringAttribute{ + Optional: true, + Sensitive: true, + }, + "customer_master_key_id": schema.StringAttribute{ + Optional: true, + Sensitive: true, + }, + "region": schema.StringAttribute{ + Optional: true, + }, + "role_id": schema.StringAttribute{ + Optional: true, + }, + }, + Validators: []validator.Object{validators.AwsKmsConfig()}, + }, + }, + "azure_key_vault_config": schema.ListNestedBlock{ + Validators: []validator.List{listvalidator.SizeAtMost(1)}, + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "enabled": schema.BoolAttribute{ + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.Bool{ + boolplanmodifier.UseStateForUnknown(), + }, + }, + "client_id": schema.StringAttribute{ + Optional: true, + Sensitive: true, + }, + "azure_environment": schema.StringAttribute{ + Optional: true, + }, + "subscription_id": schema.StringAttribute{ + Optional: true, + Sensitive: true, + }, + "resource_group_name": schema.StringAttribute{ + Optional: true, + }, + "key_vault_name": schema.StringAttribute{ + Optional: true, + }, + "key_identifier": schema.StringAttribute{ + Optional: true, + Sensitive: true, + }, + "secret": schema.StringAttribute{ + Optional: true, + Sensitive: true, + }, + "tenant_id": schema.StringAttribute{ + Optional: true, + Sensitive: true, + }, + }, + }, + }, + "google_cloud_kms_config": schema.ListNestedBlock{ + Validators: []validator.List{listvalidator.SizeAtMost(1)}, + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "enabled": schema.BoolAttribute{ + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.Bool{ + boolplanmodifier.UseStateForUnknown(), + }, + }, + "service_account_key": schema.StringAttribute{ + Optional: true, + Sensitive: true, + }, + "key_version_resource_id": schema.StringAttribute{ + Optional: true, + Sensitive: true, + }, + }, + }, + }, + }, + } +} + +func (r *EncryptionAtRestRS) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var encryptionAtRestPlan *tfEncryptionAtRestRSModel + var encryptionAtRestConfig *tfEncryptionAtRestRSModel + conn := r.client.Atlas + + resp.Diagnostics.Append(req.Plan.Get(ctx, &encryptionAtRestPlan)...) + resp.Diagnostics.Append(req.Config.Get(ctx, &encryptionAtRestConfig)...) + if resp.Diagnostics.HasError() { + return + } + + projectID := encryptionAtRestPlan.ProjectID.ValueString() + encryptionAtRestReq := &matlas.EncryptionAtRest{ + GroupID: projectID, + } + if encryptionAtRestPlan.AwsKmsConfig != nil { + encryptionAtRestReq.AwsKms = *newAtlasAwsKms(encryptionAtRestPlan.AwsKmsConfig) + } + if encryptionAtRestPlan.AzureKeyVaultConfig != nil { + encryptionAtRestReq.AzureKeyVault = *newAtlasAzureKeyVault(encryptionAtRestPlan.AzureKeyVaultConfig) + } + if encryptionAtRestPlan.GoogleCloudKmsConfig != nil { + encryptionAtRestReq.GoogleCloudKms = *newAtlasGcpKms(encryptionAtRestPlan.GoogleCloudKmsConfig) + } + + var encryptionResp *matlas.EncryptionAtRest + var err error + for i := 0; i < 5; i++ { + encryptionResp, _, err = conn.EncryptionsAtRest.Create(ctx, encryptionAtRestReq) + if err != nil { + if strings.Contains(err.Error(), "CANNOT_ASSUME_ROLE") || strings.Contains(err.Error(), "INVALID_AWS_CREDENTIALS") || + strings.Contains(err.Error(), "CLOUD_PROVIDER_ACCESS_ROLE_NOT_AUTHORIZED") { + log.Printf("warning issue performing authorize EncryptionsAtRest not done try again: %s \n", err.Error()) + log.Println("retrying ") + time.Sleep(10 * time.Second) + encryptionAtRestReq.GroupID = projectID + continue + } + } + if err != nil { + resp.Diagnostics.AddError(fmt.Sprintf(errorCreateEncryptionAtRest, projectID), err.Error()) + return + } + break + } + + encryptionAtRestPlanNew := newTFEncryptionAtRestRSModel(ctx, projectID, encryptionResp, encryptionAtRestPlan) + resetDefaultsFromConfigOrState(ctx, encryptionAtRestPlan, encryptionAtRestPlanNew, encryptionAtRestConfig) + + // set state to fully populated data + diags := resp.State.Set(ctx, encryptionAtRestPlanNew) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } +} + +func (r *EncryptionAtRestRS) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var encryptionAtRestState tfEncryptionAtRestRSModel + var isImport bool + + // get current state + resp.Diagnostics.Append(req.State.Get(ctx, &encryptionAtRestState)...) + if resp.Diagnostics.HasError() { + return + } + projectID := encryptionAtRestState.ProjectID.ValueString() + + // Use the ID only with the IMPORT operation + if encryptionAtRestState.ID.ValueString() != "" && (projectID == "") { + projectID = encryptionAtRestState.ID.ValueString() + isImport = true + } + + conn := r.client.Atlas + + encryptionResp, _, err := conn.EncryptionsAtRest.Get(context.Background(), projectID) + if err != nil { + resp.Diagnostics.AddError("error when getting encryption at rest resource during read", fmt.Sprintf(errorReadEncryptionAtRest, err.Error())) + return + } + + encryptionAtRestStateNew := newTFEncryptionAtRestRSModel(ctx, projectID, encryptionResp, &encryptionAtRestState) + if !isImport { + resetDefaultsFromConfigOrState(ctx, &encryptionAtRestState, encryptionAtRestStateNew, nil) + } + + // save read data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &encryptionAtRestStateNew)...) + if resp.Diagnostics.HasError() { + return + } +} + +func (r *EncryptionAtRestRS) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var encryptionAtRestState *tfEncryptionAtRestRSModel + var encryptionAtRestConfig *tfEncryptionAtRestRSModel + var encryptionAtRestPlan *tfEncryptionAtRestRSModel + conn := r.client.Atlas + + // get current config + resp.Diagnostics.Append(req.Config.Get(ctx, &encryptionAtRestConfig)...) + if resp.Diagnostics.HasError() { + return + } + // get current state + resp.Diagnostics.Append(req.State.Get(ctx, &encryptionAtRestState)...) + if resp.Diagnostics.HasError() { + return + } + // get current plan + resp.Diagnostics.Append(req.Plan.Get(ctx, &encryptionAtRestPlan)...) + if resp.Diagnostics.HasError() { + return + } + projectID := encryptionAtRestState.ProjectID.ValueString() + atlasEncryptionAtRest, atlasResp, err := conn.EncryptionsAtRest.Get(context.Background(), projectID) + if err != nil { + if resp != nil && atlasResp.StatusCode == http.StatusNotFound { + resp.State.RemoveResource(ctx) + return + } + resp.Diagnostics.AddError("error when getting encryption at rest resource during update", fmt.Sprintf(errorProjectRead, projectID, err.Error())) + return + } + + if hasAwsKmsConfigChanged(encryptionAtRestPlan.AwsKmsConfig, encryptionAtRestState.AwsKmsConfig) { + atlasEncryptionAtRest.AwsKms = *newAtlasAwsKms(encryptionAtRestPlan.AwsKmsConfig) + } + if hasAzureKeyVaultConfigChanged(encryptionAtRestPlan.AzureKeyVaultConfig, encryptionAtRestState.AzureKeyVaultConfig) { + atlasEncryptionAtRest.AzureKeyVault = *newAtlasAzureKeyVault(encryptionAtRestPlan.AzureKeyVaultConfig) + } + if hasGcpKmsConfigChanged(encryptionAtRestPlan.GoogleCloudKmsConfig, encryptionAtRestState.GoogleCloudKmsConfig) { + atlasEncryptionAtRest.GoogleCloudKms = *newAtlasGcpKms(encryptionAtRestPlan.GoogleCloudKmsConfig) + } + + atlasEncryptionAtRest.GroupID = projectID + + encryptionResp, _, err := conn.EncryptionsAtRest.Create(ctx, atlasEncryptionAtRest) + if err != nil { + resp.Diagnostics.AddError("error updating encryption at rest", fmt.Sprintf(errorUpdateEncryptionAtRest, err.Error())) + return + } + + encryptionAtRestStateNew := newTFEncryptionAtRestRSModel(ctx, projectID, encryptionResp, encryptionAtRestPlan) + resetDefaultsFromConfigOrState(ctx, encryptionAtRestState, encryptionAtRestStateNew, encryptionAtRestConfig) + + // save updated data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &encryptionAtRestStateNew)...) +} + +func (r *EncryptionAtRestRS) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var encryptionAtRestState *tfEncryptionAtRestRSModel + + // read prior state data into the model + resp.Diagnostics.Append(req.State.Get(ctx, &encryptionAtRestState)...) + if resp.Diagnostics.HasError() { + return + } + + conn := r.client.Atlas + projectID := encryptionAtRestState.ProjectID.ValueString() + _, err := conn.EncryptionsAtRest.Delete(ctx, projectID) + + if err != nil { + resp.Diagnostics.AddError("error when destroying resource", fmt.Sprintf(errorDeleteEncryptionAtRest, projectID, err.Error())) + return + } +} + +func (r *EncryptionAtRestRS) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func hasGcpKmsConfigChanged(gcpKmsConfigsPlan, gcpKmsConfigsState []tfGcpKmsConfigModel) bool { + return !reflect.DeepEqual(gcpKmsConfigsPlan, gcpKmsConfigsState) +} + +func hasAzureKeyVaultConfigChanged(azureKeyVaultConfigPlan, azureKeyVaultConfigState []tfAzureKeyVaultConfigModel) bool { + return !reflect.DeepEqual(azureKeyVaultConfigPlan, azureKeyVaultConfigState) +} + +func hasAwsKmsConfigChanged(awsKmsConfigPlan, awsKmsConfigState []tfAwsKmsConfigModel) bool { + return !reflect.DeepEqual(awsKmsConfigPlan, awsKmsConfigState) +} + +// resetDefaultsFromConfigOrState resets certain values that are not returned by the Atlas APIs from the Config +// However, during Read() and ImportState() since there is no access to the Config object, we use the State/Plan +// to achieve the same and encryptionAtRestRSConfig in that case is passed as nil in the calling method. +// +// encryptionAtRestRSCurrent - current State/Plan for this resource +// encryptionAtRestRSNew - final object that will be written in the State once the CRUD operation succeeds +// encryptionAtRestRSConfig - Config object for this resource +func resetDefaultsFromConfigOrState(ctx context.Context, encryptionAtRestRSCurrent, encryptionAtRestRSNew, encryptionAtRestRSConfig *tfEncryptionAtRestRSModel) { + handleAwsKmsConfigDefaults(ctx, encryptionAtRestRSCurrent, encryptionAtRestRSNew, encryptionAtRestRSConfig) + handleAzureKeyVaultConfigDefaults(ctx, encryptionAtRestRSCurrent, encryptionAtRestRSNew, encryptionAtRestRSConfig) + handleGcpKmsConfig(ctx, encryptionAtRestRSCurrent, encryptionAtRestRSNew, encryptionAtRestRSConfig) +} + +func handleGcpKmsConfig(ctx context.Context, earRSCurrent, earRSNew, earRSConfig *tfEncryptionAtRestRSModel) { + // this is required to avoid unnecessary change detection during plan after migration to Plugin Framework if user didn't set this block + if earRSCurrent.GoogleCloudKmsConfig == nil { + earRSNew.GoogleCloudKmsConfig = []tfGcpKmsConfigModel{} + return + } + + // handling sensitive values that are not returned in the API response, so we sync them from the config + // that user provided. encryptionAtRestRSConfig is nil during Read(), so we use the current plan + if earRSConfig != nil && len(earRSConfig.GoogleCloudKmsConfig) > 0 { + earRSNew.GoogleCloudKmsConfig[0].ServiceAccountKey = earRSConfig.GoogleCloudKmsConfig[0].ServiceAccountKey + } else { + earRSNew.GoogleCloudKmsConfig[0].ServiceAccountKey = earRSCurrent.GoogleCloudKmsConfig[0].ServiceAccountKey + } +} + +func handleAwsKmsConfigDefaults(ctx context.Context, earRSCurrent, earRSNew, earRSConfig *tfEncryptionAtRestRSModel) { + // this is required to avoid unnecessary change detection during plan after migration to Plugin Framework if user didn't set this block + if earRSCurrent.AwsKmsConfig == nil { + earRSNew.AwsKmsConfig = []tfAwsKmsConfigModel{} + return + } + + // handling sensitive values that are not returned in the API response, so we sync them from the config + // that user provided. encryptionAtRestRSConfig is nil during Read(), so we use the current plan + if earRSConfig != nil && len(earRSConfig.AwsKmsConfig) > 0 { + earRSNew.AwsKmsConfig[0].Region = earRSConfig.AwsKmsConfig[0].Region + } else { + earRSNew.AwsKmsConfig[0].Region = earRSCurrent.AwsKmsConfig[0].Region + } +} + +func handleAzureKeyVaultConfigDefaults(ctx context.Context, earRSCurrent, earRSNew, earRSConfig *tfEncryptionAtRestRSModel) { + // this is required to avoid unnecessary change detection during plan after migration to Plugin Framework if user didn't set this block + if earRSCurrent.AzureKeyVaultConfig == nil { + earRSNew.AzureKeyVaultConfig = []tfAzureKeyVaultConfigModel{} + return + } + + // handling sensitive values that are not returned in the API response, so we sync them from the config + // that user provided. encryptionAtRestRSConfig is nil during Read(), so we use the current plan + if earRSConfig != nil && len(earRSConfig.AzureKeyVaultConfig) > 0 { + earRSNew.AzureKeyVaultConfig[0].Secret = earRSConfig.AzureKeyVaultConfig[0].Secret + } else { + earRSNew.AzureKeyVaultConfig[0].Secret = earRSCurrent.AzureKeyVaultConfig[0].Secret + } +} + +func newTFEncryptionAtRestRSModel(ctx context.Context, projectID string, encryptionResp *matlas.EncryptionAtRest, plan *tfEncryptionAtRestRSModel) *tfEncryptionAtRestRSModel { + return &tfEncryptionAtRestRSModel{ + ID: types.StringValue(projectID), + ProjectID: types.StringValue(projectID), + AwsKmsConfig: newTFAwsKmsConfig(ctx, &encryptionResp.AwsKms, plan.AwsKmsConfig), + AzureKeyVaultConfig: newTFAzureKeyVaultConfig(ctx, &encryptionResp.AzureKeyVault, plan.AzureKeyVaultConfig), + GoogleCloudKmsConfig: newTFGcpKmsConfig(ctx, &encryptionResp.GoogleCloudKms, plan.GoogleCloudKmsConfig), + } +} + +func newTFAwsKmsConfig(ctx context.Context, awsKms *matlas.AwsKms, currStateSlice []tfAwsKmsConfigModel) []tfAwsKmsConfigModel { + if awsKms == nil { + return []tfAwsKmsConfigModel{} + } + newState := tfAwsKmsConfigModel{} + + newState.Enabled = types.BoolPointerValue(awsKms.Enabled) + newState.CustomerMasterKeyID = types.StringValue(awsKms.CustomerMasterKeyID) + newState.Region = types.StringValue(awsKms.Region) + newState.AccessKeyID = conversion.StringNullIfEmpty(awsKms.AccessKeyID) + newState.SecretAccessKey = conversion.StringNullIfEmpty(awsKms.SecretAccessKey) + newState.RoleID = conversion.StringNullIfEmpty(awsKms.RoleID) + + return []tfAwsKmsConfigModel{newState} +} + +func newTFAzureKeyVaultConfig(ctx context.Context, az *matlas.AzureKeyVault, currStateSlice []tfAzureKeyVaultConfigModel) []tfAzureKeyVaultConfigModel { + if az == nil { + return []tfAzureKeyVaultConfigModel{} + } + newState := tfAzureKeyVaultConfigModel{} + + newState.Enabled = types.BoolPointerValue(az.Enabled) + newState.ClientID = types.StringValue(az.ClientID) + newState.AzureEnvironment = types.StringValue(az.AzureEnvironment) + newState.SubscriptionID = types.StringValue(az.SubscriptionID) + newState.ResourceGroupName = types.StringValue(az.ResourceGroupName) + newState.KeyVaultName = types.StringValue(az.KeyVaultName) + newState.KeyIdentifier = types.StringValue(az.KeyIdentifier) + newState.TenantID = types.StringValue(az.TenantID) + newState.Secret = conversion.StringNullIfEmpty(az.Secret) + + return []tfAzureKeyVaultConfigModel{newState} +} + +func newTFGcpKmsConfig(ctx context.Context, gcpKms *matlas.GoogleCloudKms, currStateSlice []tfGcpKmsConfigModel) []tfGcpKmsConfigModel { + if gcpKms == nil { + return []tfGcpKmsConfigModel{} + } + newState := tfGcpKmsConfigModel{} + + newState.Enabled = types.BoolPointerValue(gcpKms.Enabled) + newState.KeyVersionResourceID = types.StringValue(gcpKms.KeyVersionResourceID) + newState.ServiceAccountKey = conversion.StringNullIfEmpty(gcpKms.ServiceAccountKey) + + return []tfGcpKmsConfigModel{newState} +} + +func newAtlasAwsKms(tfAwsKmsConfigSlice []tfAwsKmsConfigModel) *matlas.AwsKms { + if tfAwsKmsConfigSlice == nil || len(tfAwsKmsConfigSlice) < 1 { + return &matlas.AwsKms{} + } + v := tfAwsKmsConfigSlice[0] + + awsRegion, _ := valRegion(v.Region.ValueString()) + + return &matlas.AwsKms{ + Enabled: v.Enabled.ValueBoolPointer(), + AccessKeyID: v.AccessKeyID.ValueString(), + SecretAccessKey: v.SecretAccessKey.ValueString(), + CustomerMasterKeyID: v.CustomerMasterKeyID.ValueString(), + Region: awsRegion, + RoleID: v.RoleID.ValueString(), + } +} + +func newAtlasGcpKms(tfGcpKmsConfigSlice []tfGcpKmsConfigModel) *matlas.GoogleCloudKms { + if tfGcpKmsConfigSlice == nil || len(tfGcpKmsConfigSlice) < 1 { + return &matlas.GoogleCloudKms{} + } + v := tfGcpKmsConfigSlice[0] + + return &matlas.GoogleCloudKms{ + Enabled: v.Enabled.ValueBoolPointer(), + ServiceAccountKey: v.ServiceAccountKey.ValueString(), + KeyVersionResourceID: v.KeyVersionResourceID.ValueString(), + } +} + +func newAtlasAzureKeyVault(tfAzKeyVaultConfigSlice []tfAzureKeyVaultConfigModel) *matlas.AzureKeyVault { + if tfAzKeyVaultConfigSlice == nil || len(tfAzKeyVaultConfigSlice) < 1 { + return &matlas.AzureKeyVault{} + } + v := tfAzKeyVaultConfigSlice[0] + + return &matlas.AzureKeyVault{ + Enabled: v.Enabled.ValueBoolPointer(), + ClientID: v.ClientID.ValueString(), + AzureEnvironment: v.AzureEnvironment.ValueString(), + SubscriptionID: v.SubscriptionID.ValueString(), + ResourceGroupName: v.ResourceGroupName.ValueString(), + KeyVaultName: v.KeyVaultName.ValueString(), + KeyIdentifier: v.KeyIdentifier.ValueString(), + Secret: v.Secret.ValueString(), + TenantID: v.TenantID.ValueString(), + } +} diff --git a/mongodbatlas/fw_resource_mongodbatlas_encryption_at_rest_migration_test.go b/mongodbatlas/fw_resource_mongodbatlas_encryption_at_rest_migration_test.go new file mode 100644 index 0000000000..5a14f96260 --- /dev/null +++ b/mongodbatlas/fw_resource_mongodbatlas_encryption_at_rest_migration_test.go @@ -0,0 +1,222 @@ +package mongodbatlas + +import ( + "os" + "testing" + + matlas "go.mongodb.org/atlas/mongodbatlas" + + "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/mwielbut/pointy" +) + +func TestAccAdvRS_Migration_EncryptionAtRest_basicAWS(t *testing.T) { + SkipTestExtCred(t) + var ( + resourceName = "mongodbatlas_encryption_at_rest.test" + projectID = os.Getenv("MONGODB_ATLAS_PROJECT_ID") + + awsKms = matlas.AwsKms{ + Enabled: pointy.Bool(true), + CustomerMasterKeyID: os.Getenv("AWS_CUSTOMER_MASTER_KEY_ID"), + Region: os.Getenv("AWS_REGION"), + RoleID: os.Getenv("AWS_ROLE_ID"), + } + ) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testCheckAwsEnv(t) }, + CheckDestroy: testAccCheckMongoDBAtlasEncryptionAtRestDestroy, + Steps: []resource.TestStep{ + { + ExternalProviders: map[string]resource.ExternalProvider{ + "mongodbatlas": { + VersionConstraint: "1.11.0", + Source: "mongodb/mongodbatlas", + }, + }, + Config: testAccMongoDBAtlasEncryptionAtRestConfigAwsKms(projectID, &awsKms), + Check: resource.ComposeTestCheckFunc( + testAccCheckMongoDBAtlasEncryptionAtRestExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "project_id", projectID), + resource.TestCheckResourceAttr(resourceName, "aws_kms_config.0.enabled", "true"), + resource.TestCheckResourceAttr(resourceName, "aws_kms_config.0.region", awsKms.Region), + resource.TestCheckResourceAttr(resourceName, "aws_kms_config.0.role_id", awsKms.RoleID), + ), + }, + { + ProtoV6ProviderFactories: testAccProviderV6Factories, + Config: testAccMongoDBAtlasEncryptionAtRestConfigAwsKms(projectID, &awsKms), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PostApplyPreRefresh: []plancheck.PlanCheck{ + DebugPlan(), + }, + }, + PlanOnly: true, + }, + }, + }) +} + +func TestAccAdvRS_Migration_EncryptionAtRest_WithRole_basicAWS(t *testing.T) { + SkipTest(t) + SkipTestExtCred(t) + var ( + resourceName = "mongodbatlas_encryption_at_rest.test" + projectID = os.Getenv("MONGODB_ATLAS_PROJECT_ID") + accessKeyID = os.Getenv("AWS_ACCESS_KEY_ID") + secretKey = os.Getenv("AWS_SECRET_ACCESS_KEY") + policyName = acctest.RandomWithPrefix("test-aws-policy") + roleName = acctest.RandomWithPrefix("test-aws-role") + + awsKms = matlas.AwsKms{ + Enabled: pointy.Bool(true), + CustomerMasterKeyID: os.Getenv("AWS_CUSTOMER_MASTER_KEY_ID"), + Region: os.Getenv("AWS_REGION"), + } + ) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testCheckAwsEnv(t) }, + CheckDestroy: testAccCheckMongoDBAtlasEncryptionAtRestDestroy, + Steps: []resource.TestStep{ + { + ExternalProviders: map[string]resource.ExternalProvider{ + "mongodbatlas": { + VersionConstraint: "1.11.0", + Source: "mongodb/mongodbatlas", + }, + "aws": { + VersionConstraint: "5.1.0", + Source: "hashicorp/aws", + }, + }, + Config: testAccMongoDBAtlasEncryptionAtRestConfigAwsKmsWithRole(awsKms.Region, accessKeyID, secretKey, projectID, policyName, roleName, false, &awsKms), + }, + { + ExternalProviders: map[string]resource.ExternalProvider{ + "aws": { + VersionConstraint: "5.1.0", + Source: "hashicorp/aws", + }, + }, + ProtoV6ProviderFactories: testAccProviderV6Factories, + Config: testAccMongoDBAtlasEncryptionAtRestConfigAwsKmsWithRole(awsKms.Region, accessKeyID, secretKey, projectID, policyName, roleName, false, &awsKms), + Check: resource.ComposeTestCheckFunc( + testAccCheckMongoDBAtlasEncryptionAtRestExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "project_id", projectID), + resource.TestCheckResourceAttr(resourceName, "aws_kms_config.0.enabled", "true"), + resource.TestCheckResourceAttr(resourceName, "aws_kms_config.0.region", awsKms.Region), + resource.TestCheckResourceAttr(resourceName, "aws_kms_config.0.role_id", awsKms.RoleID), + ), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PostApplyPreRefresh: []plancheck.PlanCheck{ + DebugPlan(), + }, + }, + PlanOnly: true, + }, + }, + }) +} + +func TestAccAdvRS_Migration_EncryptionAtRest_basicAzure(t *testing.T) { + SkipTestExtCred(t) + var ( + resourceName = "mongodbatlas_encryption_at_rest.test" + projectID = os.Getenv("MONGODB_ATLAS_PROJECT_ID") + + azureKeyVault = matlas.AzureKeyVault{ + Enabled: pointy.Bool(true), + ClientID: os.Getenv("AZURE_CLIENT_ID"), + AzureEnvironment: "AZURE", + SubscriptionID: os.Getenv("AZURE_SUBSCRIPTION_ID"), + ResourceGroupName: os.Getenv("AZURE_RESOURCE_GROUP_NAME"), + KeyVaultName: os.Getenv("AZURE_KEY_VAULT_NAME"), + KeyIdentifier: os.Getenv("AZURE_KEY_IDENTIFIER"), + Secret: os.Getenv("AZURE_SECRET"), + TenantID: os.Getenv("AZURE_TENANT_ID"), + } + ) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testCheckEncryptionAtRestEnvAzure(t) }, + CheckDestroy: testAccCheckMongoDBAtlasEncryptionAtRestDestroy, + Steps: []resource.TestStep{ + { + ExternalProviders: map[string]resource.ExternalProvider{ + "mongodbatlas": { + VersionConstraint: "1.11.0", + Source: "mongodb/mongodbatlas", + }, + }, + Config: testAccMongoDBAtlasEncryptionAtRestConfigAzureKeyVault(projectID, &azureKeyVault), + Check: resource.ComposeTestCheckFunc( + testAccCheckMongoDBAtlasEncryptionAtRestExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "project_id", projectID), + resource.TestCheckResourceAttr(resourceName, "azure_key_vault_config.0.enabled", "true"), + resource.TestCheckResourceAttr(resourceName, "azure_key_vault_config.0.azure_environment", azureKeyVault.AzureEnvironment), + resource.TestCheckResourceAttr(resourceName, "azure_key_vault_config.0.resource_group_name", azureKeyVault.ResourceGroupName), + resource.TestCheckResourceAttr(resourceName, "azure_key_vault_config.0.key_vault_name", azureKeyVault.KeyVaultName), + ), + }, + { + ProtoV6ProviderFactories: testAccProviderV6Factories, + Config: testAccMongoDBAtlasEncryptionAtRestConfigAzureKeyVault(projectID, &azureKeyVault), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PostApplyPreRefresh: []plancheck.PlanCheck{ + DebugPlan(), + }, + }, + PlanOnly: true, + }, + }, + }) +} + +func TestAccAdvRS_Migration_EncryptionAtRest_basicGCP(t *testing.T) { + SkipTestExtCred(t) + var ( + resourceName = "mongodbatlas_encryption_at_rest.test" + projectID = os.Getenv("MONGODB_ATLAS_PROJECT_ID") + + googleCloudKms = matlas.GoogleCloudKms{ + Enabled: pointy.Bool(true), + ServiceAccountKey: os.Getenv("GCP_SERVICE_ACCOUNT_KEY"), + KeyVersionResourceID: os.Getenv("GCP_KEY_VERSION_RESOURCE_ID"), + } + ) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPreCheckGPCEnv(t) }, + CheckDestroy: testAccCheckMongoDBAtlasEncryptionAtRestDestroy, + Steps: []resource.TestStep{ + { + ExternalProviders: map[string]resource.ExternalProvider{ + "mongodbatlas": { + VersionConstraint: "1.11.0", + Source: "mongodb/mongodbatlas", + }, + }, + Config: testAccMongoDBAtlasEncryptionAtRestConfigGoogleCloudKms(projectID, &googleCloudKms), + Check: resource.ComposeTestCheckFunc( + testAccCheckMongoDBAtlasEncryptionAtRestExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "project_id", projectID), + resource.TestCheckResourceAttr(resourceName, "google_cloud_kms_config.0.enabled", "true"), + ), + }, + { + ProtoV6ProviderFactories: testAccProviderV6Factories, + Config: testAccMongoDBAtlasEncryptionAtRestConfigGoogleCloudKms(projectID, &googleCloudKms), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PostApplyPreRefresh: []plancheck.PlanCheck{ + DebugPlan(), + }, + }, + PlanOnly: true, + }, + }, + }) +} diff --git a/mongodbatlas/resource_mongodbatlas_encryption_at_rest_test.go b/mongodbatlas/fw_resource_mongodbatlas_encryption_at_rest_test.go similarity index 73% rename from mongodbatlas/resource_mongodbatlas_encryption_at_rest_test.go rename to mongodbatlas/fw_resource_mongodbatlas_encryption_at_rest_test.go index 89a5c4ed23..147e41eb62 100644 --- a/mongodbatlas/resource_mongodbatlas_encryption_at_rest_test.go +++ b/mongodbatlas/fw_resource_mongodbatlas_encryption_at_rest_test.go @@ -6,12 +6,12 @@ import ( "os" "testing" - "github.com/hashicorp/terraform-plugin-testing/helper/acctest" + matlas "go.mongodb.org/atlas/mongodbatlas" + "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/terraform" "github.com/mwielbut/pointy" - matlas "go.mongodb.org/atlas/mongodbatlas" ) const ( @@ -81,7 +81,7 @@ EOF resource "mongodbatlas_encryption_at_rest" "test" { project_id = "%s" - aws_kms { + aws_kms_config { enabled = %t customer_master_key_id = "%s" region = "%s" @@ -108,6 +108,7 @@ func TestAccAdvRSEncryptionAtRest_basicAWS(t *testing.T) { SecretAccessKey: os.Getenv("AWS_SECRET_ACCESS_KEY"), CustomerMasterKeyID: os.Getenv("AWS_CUSTOMER_MASTER_KEY_ID"), Region: os.Getenv("AWS_REGION"), + RoleID: os.Getenv("AWS_ROLE_ID"), } awsKmsUpdated = matlas.AwsKms{ @@ -116,10 +117,11 @@ func TestAccAdvRSEncryptionAtRest_basicAWS(t *testing.T) { SecretAccessKey: os.Getenv("AWS_SECRET_ACCESS_KEY_UPDATED"), CustomerMasterKeyID: os.Getenv("AWS_CUSTOMER_MASTER_KEY_ID_UPDATED"), Region: os.Getenv("AWS_REGION_UPDATED"), + RoleID: os.Getenv("AWS_ROLE_ID"), } ) - resource.ParallelTest(t, resource.TestCase{ + resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testCheckAwsEnv(t) }, ProtoV6ProviderFactories: testAccProviderV6Factories, CheckDestroy: testAccCheckMongoDBAtlasEncryptionAtRestDestroy, @@ -129,6 +131,9 @@ func TestAccAdvRSEncryptionAtRest_basicAWS(t *testing.T) { Check: resource.ComposeTestCheckFunc( testAccCheckMongoDBAtlasEncryptionAtRestExists(resourceName), resource.TestCheckResourceAttr(resourceName, "project_id", projectID), + resource.TestCheckResourceAttr(resourceName, "aws_kms_config.0.enabled", "true"), + resource.TestCheckResourceAttr(resourceName, "aws_kms_config.0.region", awsKms.Region), + resource.TestCheckResourceAttr(resourceName, "aws_kms_config.0.role_id", awsKms.RoleID), ), }, { @@ -136,8 +141,18 @@ func TestAccAdvRSEncryptionAtRest_basicAWS(t *testing.T) { Check: resource.ComposeTestCheckFunc( testAccCheckMongoDBAtlasEncryptionAtRestExists(resourceName), resource.TestCheckResourceAttr(resourceName, "project_id", projectID), + resource.TestCheckResourceAttr(resourceName, "aws_kms_config.0.enabled", "true"), + resource.TestCheckResourceAttr(resourceName, "aws_kms_config.0.region", awsKmsUpdated.Region), + resource.TestCheckResourceAttr(resourceName, "aws_kms_config.0.role_id", awsKmsUpdated.RoleID), ), }, + { + ResourceName: resourceName, + ImportStateIdFunc: testAccCheckMongoDBAtlasEncryptionAtRestImportStateIDFunc(resourceName), + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"google_cloud_kms_config", "azure_key_vault_config"}, + }, }, }) } @@ -173,7 +188,7 @@ func TestAccAdvRSEncryptionAtRest_basicAzure(t *testing.T) { } ) - resource.ParallelTest(t, resource.TestCase{ + resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testCheckEncryptionAtRestEnvAzure(t) }, ProtoV6ProviderFactories: testAccProviderV6Factories, CheckDestroy: testAccCheckMongoDBAtlasEncryptionAtRestDestroy, @@ -183,6 +198,10 @@ func TestAccAdvRSEncryptionAtRest_basicAzure(t *testing.T) { Check: resource.ComposeTestCheckFunc( testAccCheckMongoDBAtlasEncryptionAtRestExists(resourceName), resource.TestCheckResourceAttr(resourceName, "project_id", projectID), + resource.TestCheckResourceAttr(resourceName, "azure_key_vault_config.0.enabled", "true"), + resource.TestCheckResourceAttr(resourceName, "azure_key_vault_config.0.azure_environment", azureKeyVault.AzureEnvironment), + resource.TestCheckResourceAttr(resourceName, "azure_key_vault_config.0.resource_group_name", azureKeyVault.ResourceGroupName), + resource.TestCheckResourceAttr(resourceName, "azure_key_vault_config.0.key_vault_name", azureKeyVault.KeyVaultName), ), }, { @@ -190,8 +209,20 @@ func TestAccAdvRSEncryptionAtRest_basicAzure(t *testing.T) { Check: resource.ComposeTestCheckFunc( testAccCheckMongoDBAtlasEncryptionAtRestExists(resourceName), resource.TestCheckResourceAttr(resourceName, "project_id", projectID), + resource.TestCheckResourceAttr(resourceName, "azure_key_vault_config.0.enabled", "true"), + resource.TestCheckResourceAttr(resourceName, "azure_key_vault_config.0.azure_environment", azureKeyVaultUpdated.AzureEnvironment), + resource.TestCheckResourceAttr(resourceName, "azure_key_vault_config.0.resource_group_name", azureKeyVaultUpdated.ResourceGroupName), + resource.TestCheckResourceAttr(resourceName, "azure_key_vault_config.0.key_vault_name", azureKeyVaultUpdated.KeyVaultName), ), }, + { + ResourceName: resourceName, + ImportStateIdFunc: testAccCheckMongoDBAtlasEncryptionAtRestImportStateIDFunc(resourceName), + ImportState: true, + ImportStateVerify: true, + // "azure_key_vault_config.0.secret" is a sensitive value not returned by the API + ImportStateVerifyIgnore: []string{"google_cloud_kms_config", "aws_kms_config", "azure_key_vault_config.0.secret"}, + }, }, }) } @@ -215,7 +246,7 @@ func TestAccAdvRSEncryptionAtRest_basicGCP(t *testing.T) { } ) - resource.ParallelTest(t, resource.TestCase{ + resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccPreCheckGPCEnv(t) }, ProtoV6ProviderFactories: testAccProviderV6Factories, CheckDestroy: testAccCheckMongoDBAtlasEncryptionAtRestDestroy, @@ -225,6 +256,7 @@ func TestAccAdvRSEncryptionAtRest_basicGCP(t *testing.T) { Check: resource.ComposeTestCheckFunc( testAccCheckMongoDBAtlasEncryptionAtRestExists(resourceName), resource.TestCheckResourceAttr(resourceName, "project_id", projectID), + resource.TestCheckResourceAttr(resourceName, "google_cloud_kms_config.0.enabled", "true"), ), }, { @@ -232,8 +264,17 @@ func TestAccAdvRSEncryptionAtRest_basicGCP(t *testing.T) { Check: resource.ComposeTestCheckFunc( testAccCheckMongoDBAtlasEncryptionAtRestExists(resourceName), resource.TestCheckResourceAttr(resourceName, "project_id", projectID), + resource.TestCheckResourceAttr(resourceName, "google_cloud_kms_config.0.enabled", "true"), ), }, + { + ResourceName: resourceName, + ImportStateIdFunc: testAccCheckMongoDBAtlasEncryptionAtRestImportStateIDFunc(resourceName), + ImportState: true, + ImportStateVerify: true, + // "google_cloud_kms_config.0.service_account_key" is a sensitive value not returned by the API + ImportStateVerifyIgnore: []string{"aws_kms_config", "azure_key_vault_config", "google_cloud_kms_config.0.service_account_key"}, + }, }, }) } @@ -257,7 +298,13 @@ func TestAccAdvRSEncryptionAtRestWithRole_basicAWS(t *testing.T) { ) resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t); testCheckAwsEnv(t) }, + PreCheck: func() { testAccPreCheck(t); testCheckAwsEnv(t) }, + ExternalProviders: map[string]resource.ExternalProvider{ + "aws": { + VersionConstraint: "5.1.0", + Source: "hashicorp/aws", + }, + }, ProtoV6ProviderFactories: testAccProviderV6Factories, CheckDestroy: testAccCheckMongoDBAtlasEncryptionAtRestDestroy, Steps: []resource.TestStep{ @@ -271,13 +318,20 @@ func TestAccAdvRSEncryptionAtRestWithRole_basicAWS(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "project_id", projectID), ), }, + { + ResourceName: resourceName, + ImportStateIdFunc: testAccCheckMongoDBAtlasEncryptionAtRestImportStateIDFunc(resourceName), + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"google_cloud_kms_config", "azure_key_vault_config"}, + }, }, }) } func testAccCheckMongoDBAtlasEncryptionAtRestExists(resourceName string) resource.TestCheckFunc { return func(s *terraform.State) error { - conn := testAccProviderSdkV2.Meta().(*MongoDBClient).Atlas + conn := testMongoDBClient.(*MongoDBClient).Atlas rs, ok := s.RootModule().Resources[resourceName] if !ok { @@ -297,7 +351,7 @@ func testAccCheckMongoDBAtlasEncryptionAtRestExists(resourceName string) resourc } func testAccCheckMongoDBAtlasEncryptionAtRestDestroy(s *terraform.State) error { - conn := testAccProviderSdkV2.Meta().(*MongoDBClient).Atlas + conn := testMongoDBClient.(*MongoDBClient).Atlas for _, rs := range s.RootModule().Resources { if rs.Type != "mongodbatlas_encryption_at_rest" { @@ -324,13 +378,12 @@ func testAccMongoDBAtlasEncryptionAtRestConfigAwsKms(projectID string, aws *matl aws_kms_config { enabled = %t - access_key_id = "%s" - secret_access_key = "%s" customer_master_key_id = "%s" region = "%s" + role_id = "%s" } } - `, projectID, *aws.Enabled, aws.AccessKeyID, aws.SecretAccessKey, aws.CustomerMasterKeyID, aws.Region) + `, projectID, *aws.Enabled, aws.CustomerMasterKeyID, aws.Region, aws.RoleID) } func testAccMongoDBAtlasEncryptionAtRestConfigAzureKeyVault(projectID string, azure *matlas.AzureKeyVault) string { @@ -378,3 +431,14 @@ func testAccMongoDBAtlasEncryptionAtRestConfigAwsKmsWithRole(region, awsAccesKey } return config } + +func testAccCheckMongoDBAtlasEncryptionAtRestImportStateIDFunc(resourceName string) resource.ImportStateIdFunc { + return func(s *terraform.State) (string, error) { + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return "", fmt.Errorf("not found: %s", resourceName) + } + + return rs.Primary.ID, nil + } +} diff --git a/mongodbatlas/provider.go b/mongodbatlas/provider.go index 7acf1eabf4..cd7fc79d7f 100644 --- a/mongodbatlas/provider.go +++ b/mongodbatlas/provider.go @@ -222,7 +222,6 @@ func getResourcesMap() map[string]*schema.Resource { "mongodbatlas_cluster": resourceMongoDBAtlasCluster(), "mongodbatlas_network_container": resourceMongoDBAtlasNetworkContainer(), "mongodbatlas_network_peering": resourceMongoDBAtlasNetworkPeering(), - "mongodbatlas_encryption_at_rest": resourceMongoDBAtlasEncryptionAtRest(), "mongodbatlas_maintenance_window": resourceMongoDBAtlasMaintenanceWindow(), "mongodbatlas_auditing": resourceMongoDBAtlasAuditing(), "mongodbatlas_team": resourceMongoDBAtlasTeam(), diff --git a/mongodbatlas/resource_mongodbatlas_data_lake.go b/mongodbatlas/resource_mongodbatlas_data_lake.go index be9c85236b..ce6ced87b4 100644 --- a/mongodbatlas/resource_mongodbatlas_data_lake.go +++ b/mongodbatlas/resource_mongodbatlas_data_lake.go @@ -545,3 +545,17 @@ func expandDataLakeDataProcessRegion(d *schema.ResourceData) *matlas.DataProcess } return nil } + +func counterEmptyValues(values map[string]interface{}) bool { + count := 0 + for i := range values { + if val, ok := values[i]; ok { + strval, okT := val.(string) + if okT && strval == "" || strval == "false" { + count++ + } + } + } + + return len(values) == count +} diff --git a/mongodbatlas/resource_mongodbatlas_encryption_at_rest.go b/mongodbatlas/resource_mongodbatlas_encryption_at_rest.go deleted file mode 100644 index ac2263e697..0000000000 --- a/mongodbatlas/resource_mongodbatlas_encryption_at_rest.go +++ /dev/null @@ -1,677 +0,0 @@ -package mongodbatlas - -import ( - "context" - "fmt" - "log" - "net/http" - "strings" - "time" - - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/mwielbut/pointy" - "github.com/spf13/cast" - matlas "go.mongodb.org/atlas/mongodbatlas" -) - -const ( - errorCreateEncryptionAtRest = "error creating Encryption At Rest: %s" - errorReadEncryptionAtRest = "error getting Encryption At Rest: %s" - errorDeleteEncryptionAtRest = "error deleting Encryption At Rest: (%s): %s" - errorUpdateEncryptionAtRest = "error updating Encryption At Rest: %s" - errorAlertEncryptionAtRestSetting = "error setting `%s` for Encryption At Rest (%s): %s" -) - -func resourceMongoDBAtlasEncryptionAtRest() *schema.Resource { - return &schema.Resource{ - CreateContext: resourceMongoDBAtlasEncryptionAtRestCreate, - ReadContext: resourceMongoDBAtlasEncryptionAtRestRead, - DeleteContext: resourceMongoDBAtlasEncryptionAtRestDelete, - UpdateContext: resourceMongoDBAtlasEncryptionAtRestUpdate, - Importer: &schema.ResourceImporter{}, - Schema: map[string]*schema.Schema{ - "project_id": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, - "aws_kms": { - Type: schema.TypeMap, - Optional: true, - Sensitive: true, - Deprecated: fmt.Sprintf(DeprecationMessageParameterToResource, "v1.12.0", "aws_kms_config"), - ConflictsWith: []string{"aws_kms_config"}, - Elem: &schema.Schema{ - Type: schema.TypeString, - }, - ValidateFunc: func(val interface{}, key string) (warns []string, errs []error) { - v := val.(map[string]interface{}) - - _, akOk := v["access_key_id"] - _, saOk := v["secret_access_key"] - _, rOk := v["role_id"] - - if (akOk && saOk && rOk) || (akOk && rOk) || (saOk && rOk) { - errs = append(errs, fmt.Errorf("%q For credentials: `access_key_id` and `secret_access_key` are allowed but not `role_id`."+ - " For roles: `access_key_id` and `secret_access_key` are not allowed but `role_id` is allowed", key)) - } - - return - }, - }, - "azure_key_vault": { - Type: schema.TypeMap, - Optional: true, - Sensitive: true, - Deprecated: fmt.Sprintf(DeprecationMessageParameterToResource, "v1.12.0", "azure_key_vault_config"), - ConflictsWith: []string{"azure_key_vault_config"}, - Elem: &schema.Schema{ - Type: schema.TypeString, - }, - }, - "google_cloud_kms": { - Type: schema.TypeMap, - Optional: true, - Sensitive: true, - Deprecated: fmt.Sprintf(DeprecationMessageParameterToResource, "v1.12.0", "google_cloud_kms_config"), - ConflictsWith: []string{"google_cloud_kms_config"}, - Elem: &schema.Schema{ - Type: schema.TypeString, - }, - }, - "aws_kms_config": { - Type: schema.TypeList, - MaxItems: 1, - Optional: true, - Sensitive: true, - ConflictsWith: []string{"aws_kms"}, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "enabled": { - Type: schema.TypeBool, - Optional: true, - }, - "access_key_id": { - Type: schema.TypeString, - Optional: true, - Sensitive: true, - }, - "secret_access_key": { - Type: schema.TypeString, - Optional: true, - Sensitive: true, - }, - "customer_master_key_id": { - Type: schema.TypeString, - Optional: true, - Sensitive: true, - }, - "region": { - Type: schema.TypeString, - Optional: true, - }, - "role_id": { - Type: schema.TypeString, - Optional: true, - }, - }, - }, - }, - "azure_key_vault_config": { - Type: schema.TypeList, - MaxItems: 1, - Optional: true, - Sensitive: true, - ConflictsWith: []string{"azure_key_vault"}, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "enabled": { - Type: schema.TypeBool, - Required: true, - }, - "client_id": { - Type: schema.TypeString, - Optional: true, - Sensitive: true, - }, - "azure_environment": { - Type: schema.TypeString, - Optional: true, - }, - "subscription_id": { - Type: schema.TypeString, - Optional: true, - Sensitive: true, - }, - "resource_group_name": { - Type: schema.TypeString, - Optional: true, - }, - "key_vault_name": { - Type: schema.TypeString, - Optional: true, - }, - "key_identifier": { - Type: schema.TypeString, - Optional: true, - Sensitive: true, - }, - "secret": { - Type: schema.TypeString, - Optional: true, - Sensitive: true, - }, - "tenant_id": { - Type: schema.TypeString, - Optional: true, - Sensitive: true, - }, - }, - }, - }, - "google_cloud_kms_config": { - Type: schema.TypeList, - MaxItems: 1, - Optional: true, - Sensitive: true, - ConflictsWith: []string{"google_cloud_kms"}, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "enabled": { - Type: schema.TypeBool, - Optional: true, - }, - "service_account_key": { - Type: schema.TypeString, - Optional: true, - Sensitive: true, - }, - "key_version_resource_id": { - Type: schema.TypeString, - Optional: true, - Sensitive: true, - }, - }, - }, - }, - }, - } -} - -func resourceMongoDBAtlasEncryptionAtRestCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - conn := meta.(*MongoDBClient).Atlas - - encryptionAtRestReq := &matlas.EncryptionAtRest{ - GroupID: d.Get("project_id").(string), - } - - // Deprecated workflows - aws, awsOk := d.GetOk("aws_kms") - if awsOk { - encryptionAtRestReq.AwsKms = expandAwsKms(aws.(map[string]interface{})) - } - azure, azureOk := d.GetOk("azure_key_vault") - if azureOk { - encryptionAtRestReq.AzureKeyVault = expandAzureKeyVault(azure.(map[string]interface{})) - } - gcp, gcpOk := d.GetOk("google_cloud_kms") - if gcpOk { - encryptionAtRestReq.GoogleCloudKms = expandGCPKms(gcp.(map[string]interface{})) - } - - // End Depprecated workflows - - awsC, awsCOk := d.GetOk("aws_kms_config") - if awsCOk { - err := validateAwsKms(awsC.([]interface{})) - if err != nil { - return diag.FromErr(err) - } - encryptionAtRestReq.AwsKms = expandAwsKmsConfig(awsC.([]interface{})) - } - azureC, azureCOk := d.GetOk("azure_key_vault_config") - if azureCOk { - encryptionAtRestReq.AzureKeyVault = expandAzureKeyVaultConfig(azureC.([]interface{})) - } - gcpC, gcpCOk := d.GetOk("google_cloud_kms_config") - if gcpCOk { - encryptionAtRestReq.GoogleCloudKms = expandGCPKmsConfig(gcpC.([]interface{})) - } - - for i := 0; i < 5; i++ { - _, _, err := conn.EncryptionsAtRest.Create(ctx, encryptionAtRestReq) - if err != nil { - if strings.Contains(err.Error(), "CANNOT_ASSUME_ROLE") || strings.Contains(err.Error(), "INVALID_AWS_CREDENTIALS") || - strings.Contains(err.Error(), "CLOUD_PROVIDER_ACCESS_ROLE_NOT_AUTHORIZED") { - log.Printf("warning issue performing authorize EncryptionsAtRest not done try again: %s \n", err.Error()) - log.Println("retrying ") - time.Sleep(10 * time.Second) - encryptionAtRestReq.GroupID = d.Get("project_id").(string) - continue - } - } - if err != nil { - return diag.FromErr(fmt.Errorf(errorCreateEncryptionAtRest, err)) - } - break - } - - d.SetId(d.Get("project_id").(string)) - - return resourceMongoDBAtlasEncryptionAtRestRead(ctx, d, meta) -} - -func resourceMongoDBAtlasEncryptionAtRestRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - conn := meta.(*MongoDBClient).Atlas - - resp, response, err := conn.EncryptionsAtRest.Get(context.Background(), d.Id()) - if err != nil { - if response != nil && response.StatusCode == http.StatusNotFound { - d.SetId("") - return nil - } - - return diag.FromErr(fmt.Errorf(errorReadEncryptionAtRest, err)) - } - - // Deprecated workflows - values := flattenAWSKMS(&resp.AwsKms) - if !counterEmptyValues(values) { - aws, awsOk := d.GetOk("aws_kms") - if awsOk { - aws2 := aws.(map[string]interface{}) - values["secret_access_key"] = cast.ToString(aws2["secret_access_key"]) - if v, sa := values["role_id"]; sa { - if v.(string) == "" { - delete(values, "role_id") - } - } - if v, sa := values["access_key_id"]; sa { - if v.(string) == "" { - delete(values, "access_key_id") - delete(values, "secret_access_key") - } - } - if err = d.Set("aws_kms", values); err != nil { - return diag.FromErr(fmt.Errorf(errorAlertEncryptionAtRestSetting, "aws_kms", d.Id(), err)) - } - } - } - - values = flattenAzureVault(&resp.AzureKeyVault) - if !counterEmptyValues(values) { - azure, azureOk := d.GetOk("azure_key_vault") - if azureOk { - azure2 := azure.(map[string]interface{}) - values["secret"] = cast.ToString(azure2["secret"]) - if v, sa := values["secret"]; sa { - if v.(string) == "" { - delete(values, "secret") - } - } - if err = d.Set("azure_key_vault", values); err != nil { - return diag.FromErr(fmt.Errorf(errorAlertEncryptionAtRestSetting, "azure_key_vault", d.Id(), err)) - } - } - } - - values = flattenGCPKms(&resp.GoogleCloudKms) - if !counterEmptyValues(values) { - gcp, gcpOk := d.GetOk("google_cloud_kms") - if gcpOk { - gcp2 := gcp.(map[string]interface{}) - values["service_account_key"] = cast.ToString(gcp2["service_account_key"]) - if v, sa := values["service_account_key"]; sa { - if v.(string) == "" { - delete(values, "service_account_key") - } - } - if err = d.Set("google_cloud_kms", values); err != nil { - return diag.FromErr(fmt.Errorf(errorAlertEncryptionAtRestSetting, "google_cloud_kms", d.Id(), err)) - } - } - } - // End Deprecated workflows - - values2 := flattenAWSKMSConfig(&resp.AwsKms) - if len(values2) != 0 { - value := values2[0].(map[string]interface{}) - if !counterEmptyValues(value) { - aws, awsOk := d.GetOk("aws_kms_config") - if awsOk { - awsObj := aws.([]interface{}) - if len(awsObj) > 0 { - aws2 := awsObj[0].(map[string]interface{}) - value["secret_access_key"] = cast.ToString(aws2["secret_access_key"]) - if v, sa := value["role_id"]; sa { - if v.(string) == "" { - delete(value, "role_id") - } - } - if v, sa := value["access_key_id"]; sa { - if v.(string) == "" { - delete(value, "access_key_id") - delete(value, "secret_access_key") - } - } - } - } - values2[0] = value - - if err = d.Set("aws_kms_config", values2); err != nil { - return diag.FromErr(fmt.Errorf(errorAlertEncryptionAtRestSetting, "aws_kms_config", d.Id(), err)) - } - } - } - - values2 = flattenAzureVaultConfig(&resp.AzureKeyVault) - if len(values2) != 0 { - value := values2[0].(map[string]interface{}) - - if !counterEmptyValues(value) { - azure, azureOk := d.GetOk("azure_key_vault_config") - if azureOk { - azureObj := azure.([]interface{}) - if len(azureObj) > 0 { - azure2 := azureObj[0].(map[string]interface{}) - value["secret"] = cast.ToString(azure2["secret"]) - if v, sa := value["secret"]; sa { - if v.(string) == "" { - delete(value, "secret") - } - } - } - } - values2[0] = value - - if err = d.Set("azure_key_vault_config", values2); err != nil { - return diag.FromErr(fmt.Errorf(errorAlertEncryptionAtRestSetting, "azure_key_vault_config", d.Id(), err)) - } - } - } - - values2 = flattenGCPKmsConfig(&resp.GoogleCloudKms) - if len(values2) != 0 { - value := values2[0].(map[string]interface{}) - if !counterEmptyValues(value) { - gcp, gcpOk := d.GetOk("google_cloud_kms_config") - if gcpOk { - gcpObj := gcp.([]interface{}) - if len(gcpObj) > 0 { - gcp2 := gcpObj[0].(map[string]interface{}) - value["service_account_key"] = cast.ToString(gcp2["service_account_key"]) - if v, sa := value["service_account_key"]; sa { - if v.(string) == "" { - delete(value, "service_account_key") - } - } - } - } - values2[0] = value - - if err = d.Set("google_cloud_kms_config", values2); err != nil { - return diag.FromErr(fmt.Errorf(errorAlertEncryptionAtRestSetting, "google_cloud_kms_config", d.Id(), err)) - } - } - } - - if err = d.Set("project_id", d.Id()); err != nil { - return diag.FromErr(fmt.Errorf(errorAlertEncryptionAtRestSetting, "project_id", d.Id(), err)) - } - - return nil -} - -func resourceMongoDBAtlasEncryptionAtRestUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - conn := meta.(*MongoDBClient).Atlas - projectID := d.Id() - - encrypt, _, err := conn.EncryptionsAtRest.Get(ctx, projectID) - if err != nil { - return diag.FromErr(fmt.Errorf(errorUpdateEncryptionAtRest, err)) - } - - encrypt.GroupID = projectID - - if d.HasChange("aws_kms") { - encrypt.AwsKms = expandAwsKms(d.Get("aws_kms").(map[string]interface{})) - } - - if d.HasChange("azure_key_vault") { - encrypt.AzureKeyVault = expandAzureKeyVault(d.Get("azure_key_vault").(map[string]interface{})) - } - - if d.HasChange("google_cloud_kms") { - encrypt.GoogleCloudKms = expandGCPKms(d.Get("google_cloud_kms").(map[string]interface{})) - } - - if d.HasChange("aws_kms_config") { - encrypt.AwsKms = expandAwsKmsConfig(d.Get("aws_kms_config").([]interface{})) - } - - if d.HasChange("azure_key_vault_config") { - encrypt.AzureKeyVault = expandAzureKeyVaultConfig(d.Get("azure_key_vault_config").([]interface{})) - } - - if d.HasChange("google_cloud_kms_config") { - encrypt.GoogleCloudKms = expandGCPKmsConfig(d.Get("google_cloud_kms_config").([]interface{})) - } - - _, _, err = conn.EncryptionsAtRest.Create(ctx, encrypt) - if err != nil { - return diag.FromErr(fmt.Errorf("error updating encryption at rest (%s): %s", projectID, err)) - } - - return resourceMongoDBAtlasEncryptionAtRestRead(ctx, d, meta) -} - -func resourceMongoDBAtlasEncryptionAtRestDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - conn := meta.(*MongoDBClient).Atlas - - _, err := conn.EncryptionsAtRest.Delete(ctx, d.Id()) - if err != nil { - return diag.FromErr(fmt.Errorf(errorDeleteEncryptionAtRest, d.Id(), err)) - } - - return nil -} - -func expandAwsKms(awsKms map[string]interface{}) matlas.AwsKms { - awsRegion, _ := valRegion(awsKms["region"]) - - return matlas.AwsKms{ - Enabled: pointy.Bool(cast.ToBool(awsKms["enabled"])), - AccessKeyID: cast.ToString(awsKms["access_key_id"]), - SecretAccessKey: cast.ToString(awsKms["secret_access_key"]), - CustomerMasterKeyID: cast.ToString(awsKms["customer_master_key_id"]), - Region: awsRegion, - RoleID: cast.ToString(awsKms["role_id"]), - } -} - -func expandAzureKeyVault(azure map[string]interface{}) matlas.AzureKeyVault { - return matlas.AzureKeyVault{ - Enabled: pointy.Bool(cast.ToBool(azure["enabled"])), - ClientID: cast.ToString(azure["client_id"]), - AzureEnvironment: cast.ToString(azure["azure_environment"]), - SubscriptionID: cast.ToString(azure["subscription_id"]), - ResourceGroupName: cast.ToString(azure["resource_group_name"]), - KeyVaultName: cast.ToString(azure["key_vault_name"]), - KeyIdentifier: cast.ToString(azure["key_identifier"]), - Secret: cast.ToString(azure["secret"]), - TenantID: cast.ToString(azure["tenant_id"]), - } -} - -func expandGCPKms(gcpKms map[string]interface{}) matlas.GoogleCloudKms { - return matlas.GoogleCloudKms{ - Enabled: pointy.Bool(cast.ToBool(gcpKms["enabled"])), - ServiceAccountKey: cast.ToString(gcpKms["service_account_key"]), - KeyVersionResourceID: cast.ToString(gcpKms["key_version_resource_id"]), - } -} - -func flattenAWSKMS(m *matlas.AwsKms) map[string]interface{} { - return map[string]interface{}{ - "enabled": cast.ToString(m.Enabled), - "access_key_id": m.AccessKeyID, - "customer_master_key_id": m.CustomerMasterKeyID, - "region": m.Region, - "role_id": m.RoleID, - } -} - -func flattenAzureVault(m *matlas.AzureKeyVault) map[string]interface{} { - return map[string]interface{}{ - "enabled": cast.ToString(m.Enabled), - "client_id": m.ClientID, - "azure_environment": m.AzureEnvironment, - "subscription_id": m.SubscriptionID, - "resource_group_name": m.ResourceGroupName, - "key_vault_name": m.KeyVaultName, - "key_identifier": m.KeyIdentifier, - "secret": m.Secret, - "tenant_id": m.TenantID, - } -} - -func flattenGCPKms(m *matlas.GoogleCloudKms) map[string]interface{} { - return map[string]interface{}{ - "enabled": cast.ToString(m.Enabled), - "service_account_key": m.ServiceAccountKey, - "key_version_resource_id": m.KeyVersionResourceID, - } -} - -func expandAwsKmsConfig(awsKms []interface{}) matlas.AwsKms { - if len(awsKms) == 0 { - return matlas.AwsKms{} - } - - awsKmsObj := awsKms[0].(map[string]interface{}) - - awsRegion, _ := valRegion(awsKmsObj["region"]) - - return matlas.AwsKms{ - Enabled: pointy.Bool(cast.ToBool(awsKmsObj["enabled"])), - AccessKeyID: cast.ToString(awsKmsObj["access_key_id"]), - SecretAccessKey: cast.ToString(awsKmsObj["secret_access_key"]), - CustomerMasterKeyID: cast.ToString(awsKmsObj["customer_master_key_id"]), - Region: awsRegion, - RoleID: cast.ToString(awsKmsObj["role_id"]), - } -} - -func validateAwsKms(awsKms []interface{}) error { - if len(awsKms) == 0 { - return fmt.Errorf("empty aws_kms_config") - } - - v := awsKms[0].(map[string]interface{}) - - ak, akOk := v["access_key_id"] - sa, saOk := v["secret_access_key"] - r, rOk := v["role_id"] - - if ((akOk && ak != "") && (saOk && sa != "") && (rOk && r != "")) || ((akOk && ak != "") && (rOk && r != "")) || ((saOk && sa != "") && (rOk && r != "")) { - return fmt.Errorf("for credentials: `access_key_id` and `secret_access_key` are allowed but not `role_id`." + - " For roles: `access_key_id` and `secret_access_key` are not allowed but `role_id` is allowed") - } - - return nil -} - -func expandAzureKeyVaultConfig(azure []interface{}) matlas.AzureKeyVault { - if len(azure) == 0 { - return matlas.AzureKeyVault{} - } - - azureObj := azure[0].(map[string]interface{}) - - return matlas.AzureKeyVault{ - Enabled: pointy.Bool(cast.ToBool(azureObj["enabled"])), - ClientID: cast.ToString(azureObj["client_id"]), - AzureEnvironment: cast.ToString(azureObj["azure_environment"]), - SubscriptionID: cast.ToString(azureObj["subscription_id"]), - ResourceGroupName: cast.ToString(azureObj["resource_group_name"]), - KeyVaultName: cast.ToString(azureObj["key_vault_name"]), - KeyIdentifier: cast.ToString(azureObj["key_identifier"]), - Secret: cast.ToString(azureObj["secret"]), - TenantID: cast.ToString(azureObj["tenant_id"]), - } -} - -func expandGCPKmsConfig(gcpKms []interface{}) matlas.GoogleCloudKms { - if len(gcpKms) == 0 { - return matlas.GoogleCloudKms{} - } - - gcpKmsObj := gcpKms[0].(map[string]interface{}) - - return matlas.GoogleCloudKms{ - Enabled: pointy.Bool(cast.ToBool(gcpKmsObj["enabled"])), - ServiceAccountKey: cast.ToString(gcpKmsObj["service_account_key"]), - KeyVersionResourceID: cast.ToString(gcpKmsObj["key_version_resource_id"]), - } -} - -func flattenAWSKMSConfig(m *matlas.AwsKms) []interface{} { - objMap := make(map[string]interface{}, 1) - - if cast.ToBool(m.Enabled) { - objMap["enabled"] = m.Enabled - } - - objMap["access_key_id"] = m.AccessKeyID - objMap["customer_master_key_id"] = m.CustomerMasterKeyID - objMap["region"] = m.Region - objMap["role_id"] = m.RoleID - - return []interface{}{objMap} -} - -func flattenAzureVaultConfig(m *matlas.AzureKeyVault) []interface{} { - objMap := make(map[string]interface{}, 1) - - if cast.ToBool(m.Enabled) { - objMap["enabled"] = m.Enabled - } - - objMap["client_id"] = m.ClientID - objMap["azure_environment"] = m.AzureEnvironment - objMap["subscription_id"] = m.SubscriptionID - objMap["resource_group_name"] = m.ResourceGroupName - objMap["key_vault_name"] = m.KeyVaultName - objMap["key_identifier"] = m.KeyIdentifier - objMap["secret"] = m.Secret - objMap["tenant_id"] = m.TenantID - - return []interface{}{objMap} -} - -func flattenGCPKmsConfig(m *matlas.GoogleCloudKms) []interface{} { - objMap := make(map[string]interface{}, 1) - - if cast.ToBool(m.Enabled) { - objMap["enabled"] = m.Enabled - } - objMap["service_account_key"] = m.ServiceAccountKey - objMap["key_version_resource_id"] = m.KeyVersionResourceID - - return []interface{}{objMap} -} - -func counterEmptyValues(values map[string]interface{}) bool { - count := 0 - for i := range values { - if val, ok := values[i]; ok { - strval, okT := val.(string) - if okT && strval == "" || strval == "false" { - count++ - } - } - } - - return len(values) == count -} diff --git a/website/docs/r/encryption_at_rest.html.markdown b/website/docs/r/encryption_at_rest.html.markdown index d83ea2937d..3169025981 100644 --- a/website/docs/r/encryption_at_rest.html.markdown +++ b/website/docs/r/encryption_at_rest.html.markdown @@ -116,9 +116,6 @@ resource "mongodbatlas_cluster" "example_cluster" { ## Argument Reference * `project_id` - (Required) The unique identifier for the project. -* `aws_kms` - (Required) Specifies AWS KMS configuration details and whether Encryption at Rest is enabled for an Atlas project. -* `azure_key_vault` - (Required) Specifies Azure Key Vault configuration details and whether Encryption at Rest is enabled for an Atlas project. -* `google_cloud_kms` - (Required) Specifies GCP KMS configuration details and whether Encryption at Rest is enabled for an Atlas project. ### aws_kms_config Refer to the example in the [official github repository](https://github.com/mongodb/terraform-provider-mongodbatlas/tree/master/examples) to implement Encryption at Rest