@@ -359,6 +359,7 @@ func (r *ServiceResource) Schema(_ context.Context, _ resource.SchemaRequest, re
359
359
"encryption_key" : schema.StringAttribute {
360
360
Description : "Custom encryption key ARN." ,
361
361
Optional : true ,
362
+ Computed : true ,
362
363
},
363
364
"encryption_assumed_role_identifier" : schema.StringAttribute {
364
365
Description : "Custom role identifier ARN." ,
@@ -372,19 +373,13 @@ func (r *ServiceResource) Schema(_ context.Context, _ resource.SchemaRequest, re
372
373
"role_id" : schema.StringAttribute {
373
374
Computed : true ,
374
375
Description : "ID of Role to be used for granting access to the Encryption Key. This is an ARN for AWS services and a Service Account Identifier for GCP." ,
375
- PlanModifiers : []planmodifier.String {
376
- stringplanmodifier .UseStateForUnknown (),
377
- },
378
376
},
379
377
"enabled" : schema.BoolAttribute {
380
378
Optional : true ,
381
379
Computed : true , // To allow client side defaulting.
382
380
Description : "If true, TDE is enabled for the service." ,
383
381
},
384
382
},
385
- PlanModifiers : []planmodifier.Object {
386
- objectplanmodifier .UseStateForUnknown (),
387
- },
388
383
Validators : []validator.Object {
389
384
objectvalidator .ConflictsWith (path.Expressions {path .MatchRoot ("warehouse_id" )}... ),
390
385
},
@@ -557,7 +552,7 @@ func (r *ServiceResource) ModifyPlan(ctx context.Context, req resource.ModifyPla
557
552
}
558
553
}
559
554
560
- if ! plan .EncryptionKey .IsNull () && plan .EncryptionKey != state .EncryptionKey {
555
+ if ! config .EncryptionKey .IsNull () && plan .EncryptionKey != state .EncryptionKey {
561
556
resp .Diagnostics .AddAttributeError (
562
557
path .Root ("encryption_key" ),
563
558
"Invalid Update" ,
@@ -597,7 +592,7 @@ func (r *ServiceResource) ModifyPlan(ctx context.Context, req resource.ModifyPla
597
592
resp .Diagnostics .AddAttributeError (
598
593
path .Root ("transparent_data_encryption.enabled" ),
599
594
"Invalid Update" ,
600
- "It is not possible to disable TDE (Transparent data encryption) on an existing service. " ,
595
+ "This service has TDE enabled, but your clickhouse_service resource is not setting it as enabled. Please ensure you have set the transparent_data_encryption.enabled attribute to true " ,
601
596
)
602
597
}
603
598
@@ -610,15 +605,6 @@ func (r *ServiceResource) ModifyPlan(ctx context.Context, req resource.ModifyPla
610
605
)
611
606
}
612
607
613
- if ! wantEnabled {
614
- // Mark RoleID as known null.
615
- planTDE := models.TransparentEncryptionData {}
616
- plan .TransparentEncryptionData .As (ctx , & planTDE , basetypes.ObjectAsOptions {UnhandledNullAsEmpty : false , UnhandledUnknownAsEmpty : false })
617
- planTDE .RoleID = types .StringNull ()
618
- plan .TransparentEncryptionData = planTDE .ObjectValue ()
619
- resp .Plan .Set (ctx , plan )
620
- }
621
-
622
608
if config .IdleTimeoutMinutes .IsNull () {
623
609
plan .IdleTimeoutMinutes = types .Int64Null ()
624
610
resp .Plan .Set (ctx , plan )
@@ -666,7 +652,7 @@ func (r *ServiceResource) ModifyPlan(ctx context.Context, req resource.ModifyPla
666
652
)
667
653
}
668
654
669
- if ! plan .EncryptionKey .IsNull () || ! plan .EncryptionAssumedRoleIdentifier .IsNull () {
655
+ if ( ! plan .EncryptionKey .IsNull () && ! plan . EncryptionKey . IsUnknown () ) || ! plan .EncryptionAssumedRoleIdentifier .IsNull () {
670
656
resp .Diagnostics .AddError (
671
657
"Invalid Configuration" ,
672
658
"encryption_key and encryption_assumed_role_identifier cannot be defined if the service tier is development" ,
@@ -718,17 +704,17 @@ func (r *ServiceResource) ModifyPlan(ctx context.Context, req resource.ModifyPla
718
704
)
719
705
}
720
706
721
- if ! plan .EncryptionAssumedRoleIdentifier .IsNull () && plan .EncryptionKey .IsNull () {
707
+ if ! plan .EncryptionAssumedRoleIdentifier .IsNull () && ( plan .EncryptionKey .IsNull () || plan . EncryptionKey . IsUnknown () ) {
722
708
resp .Diagnostics .AddError (
723
709
"Invalid Configuration" ,
724
710
"encryption_assumed_role_identifier cannot be defined without encryption_key as well" ,
725
711
)
726
712
}
727
713
728
- if ! plan .EncryptionKey .IsNull () && strings .Compare (plan .CloudProvider .ValueString (), "aws" ) != 0 {
714
+ if ! plan .EncryptionKey .IsNull () && ! plan . EncryptionKey . IsUnknown () && strings .Compare (plan .CloudProvider .ValueString (), "aws" ) != 0 {
729
715
resp .Diagnostics .AddError (
730
716
"Invalid Configuration" ,
731
- "encryption_key and the encryption_assumed_role_identifier is only available for aws services" ,
717
+ "encryption_key and the encryption_assumed_role_identifier is only available for AWS services" ,
732
718
)
733
719
}
734
720
@@ -758,6 +744,18 @@ func (r *ServiceResource) ModifyPlan(ctx context.Context, req resource.ModifyPla
758
744
}
759
745
}
760
746
}
747
+
748
+ // CMEK->TDE migration.
749
+ // if config.encryption_key is null, we need to wipe it out from the state even if the API returns it.
750
+ // This happens when a service is migrated from CMEK to TDE.
751
+ if config .EncryptionKey .IsNull () {
752
+ if ! state .EncryptionKey .IsNull () {
753
+ plan .EncryptionKey = state .EncryptionKey
754
+ resp .Plan .Set (ctx , plan )
755
+ } else {
756
+ plan .EncryptionKey = types .StringNull ()
757
+ }
758
+ }
761
759
}
762
760
763
761
if ! plan .MinTotalMemoryGb .IsNull () && ! plan .MinReplicaMemoryGb .IsUnknown () {
@@ -873,25 +871,31 @@ func (r *ServiceResource) ModifyPlan(ctx context.Context, req resource.ModifyPla
873
871
}
874
872
}
875
873
876
- defaultTDE := false
877
874
{
878
- if config .TransparentEncryptionData .IsNull () || config .TransparentEncryptionData .IsUnknown () {
879
- defaultTDE = true
880
- } else {
881
- cfg := models.TransparentEncryptionData {}
882
- diag := config .TransparentEncryptionData .As (ctx , & cfg , basetypes.ObjectAsOptions {UnhandledNullAsEmpty : false , UnhandledUnknownAsEmpty : false })
883
- if diag .HasError () {
884
- return
885
- }
886
- defaultTDE = cfg .Enabled .IsUnknown () || cfg .Enabled .IsNull ()
875
+ // Default value if there is no TDE attribute in the state nor in the config.
876
+ tde := models.TransparentEncryptionData {
877
+ Enabled : types .BoolValue (false ),
887
878
}
888
- if defaultTDE {
889
- plan .TransparentEncryptionData = models.TransparentEncryptionData {
890
- Enabled : types .BoolValue (false ),
891
- RoleID : types .StringNull (),
892
- }.ObjectValue ()
893
- resp .Plan .Set (ctx , plan )
879
+
880
+ // Read the TDE config if present to check if TDE is enabled or not.
881
+ if ! config .TransparentEncryptionData .IsNull () {
882
+ configTDE := models.TransparentEncryptionData {}
883
+ config .TransparentEncryptionData .As (ctx , & configTDE , basetypes.ObjectAsOptions {UnhandledNullAsEmpty : false , UnhandledUnknownAsEmpty : false })
884
+
885
+ tde .Enabled = configTDE .Enabled
886
+ tde .RoleID = types .StringUnknown ()
887
+ }
888
+
889
+ // Read the Role ID from the state if set
890
+ if ! req .State .Raw .IsNull () && ! state .TransparentEncryptionData .IsNull () {
891
+ stateTDE := models.TransparentEncryptionData {}
892
+ state .TransparentEncryptionData .As (ctx , & stateTDE , basetypes.ObjectAsOptions {UnhandledNullAsEmpty : false , UnhandledUnknownAsEmpty : false })
893
+
894
+ tde .RoleID = stateTDE .RoleID
894
895
}
896
+
897
+ plan .TransparentEncryptionData = tde .ObjectValue ()
898
+ resp .Plan .Set (ctx , plan )
895
899
}
896
900
897
901
if ! config .DataWarehouseID .IsNull () {
@@ -964,7 +968,7 @@ func (r *ServiceResource) Create(ctx context.Context, req resource.CreateRequest
964
968
service .MinReplicaMemoryGb = & minReplicaMemoryGb
965
969
service .MaxReplicaMemoryGb = & maxReplicaMemoryGb
966
970
967
- if ! plan .EncryptionKey .IsNull () {
971
+ if ! plan .EncryptionKey .IsNull () && ! plan . EncryptionKey . IsUnknown () {
968
972
service .EncryptionKey = plan .EncryptionKey .ValueString ()
969
973
}
970
974
if ! plan .EncryptionAssumedRoleIdentifier .IsNull () {
@@ -1205,11 +1209,13 @@ func (r *ServiceResource) Read(ctx context.Context, req resource.ReadRequest, re
1205
1209
// Update updates the resource and sets the updated Terraform state on success.
1206
1210
func (r * ServiceResource ) Update (ctx context.Context , req resource.UpdateRequest , resp * resource.UpdateResponse ) {
1207
1211
// Retrieve values from plan
1208
- var plan , state models.ServiceResourceModel
1212
+ var plan , state , config models.ServiceResourceModel
1209
1213
diags := req .Plan .Get (ctx , & plan )
1210
1214
resp .Diagnostics .Append (diags ... )
1211
1215
diags = req .State .Get (ctx , & state )
1212
1216
resp .Diagnostics .Append (diags ... )
1217
+ req .Config .Get (ctx , & config )
1218
+ resp .Diagnostics .Append (diags ... )
1213
1219
1214
1220
if resp .Diagnostics .HasError () {
1215
1221
return
@@ -1539,6 +1545,13 @@ func (r *ServiceResource) Update(ctx context.Context, req resource.UpdateRequest
1539
1545
return
1540
1546
}
1541
1547
1548
+ // CMEK->TDE migration.
1549
+ // if config.encryption_key is null, we need to wipe it out from the state even if the API returns it.
1550
+ // This happens when a service is migrated from CMEK to TDE.
1551
+ if config .EncryptionKey .IsNull () {
1552
+ plan .EncryptionKey = types .StringNull ()
1553
+ }
1554
+
1542
1555
diags = resp .State .Set (ctx , plan )
1543
1556
resp .Diagnostics .Append (diags ... )
1544
1557
if resp .Diagnostics .HasError () {
@@ -2009,7 +2022,7 @@ func (r *ServiceResource) syncServiceState(ctx context.Context, state *models.Se
2009
2022
Enabled : types .BoolValue (service .HasTransparentDataEncryption ),
2010
2023
}
2011
2024
2012
- if service .HasTransparentDataEncryption && service . EncryptionRoleID != "" {
2025
+ if service .EncryptionRoleID != "" {
2013
2026
tde .RoleID = types .StringValue (service .EncryptionRoleID )
2014
2027
} else {
2015
2028
tde .RoleID = types .StringNull ()
0 commit comments