@@ -26,6 +26,7 @@ import (
2626 "github.com/hashicorp/terraform-plugin-framework/resource/schema/listplanmodifier"
2727 "github.com/hashicorp/terraform-plugin-framework/resource/schema/objectplanmodifier"
2828 "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
29+ "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault"
2930 "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
3031 "github.com/hashicorp/terraform-plugin-framework/schema/validator"
3132 "github.com/hashicorp/terraform-plugin-framework/types"
@@ -65,6 +66,7 @@ type Model struct {
6566 ServerId types.String `tfsdk:"server_id"`
6667 MachineType types.String `tfsdk:"machine_type"`
6768 Name types.String `tfsdk:"name"`
69+ Agent types.Object `tfsdk:"agent"`
6870 AvailabilityZone types.String `tfsdk:"availability_zone"`
6971 BootVolume types.Object `tfsdk:"boot_volume"`
7072 ImageId types.String `tfsdk:"image_id"`
@@ -79,6 +81,12 @@ type Model struct {
7981 DesiredStatus types.String `tfsdk:"desired_status"`
8082}
8183
84+ // Struct corresponding to Model.Agent
85+ type agentModel struct {
86+ Provisioned types.Bool `tfsdk:"provisioned"`
87+ ProvisioningPolicy types.String `tfsdk:"provisioning_policy"`
88+ }
89+
8290// Struct corresponding to Model.BootVolume
8391type bootVolumeModel struct {
8492 Id types.String `tfsdk:"id"`
@@ -99,6 +107,12 @@ var bootVolumeTypes = map[string]attr.Type{
99107 "id" : basetypes.StringType {},
100108}
101109
110+ // Types corresponding to agentModel
111+ var agentTypes = map [string ]attr.Type {
112+ "provisioned" : basetypes.BoolType {},
113+ "provisioning_policy" : basetypes.StringType {},
114+ }
115+
102116// NewServerResource is a helper function to simplify the provider implementation.
103117func NewServerResource () resource.Resource {
104118 return & serverResource {}
@@ -277,6 +291,35 @@ func (r *serverResource) Schema(_ context.Context, _ resource.SchemaRequest, res
277291 Optional : true ,
278292 Computed : true ,
279293 },
294+ "agent" : schema.SingleNestedAttribute {
295+ Description : "The STACKIT Server Agent configured for the server" ,
296+ Optional : true ,
297+ Computed : true ,
298+ PlanModifiers : []planmodifier.Object {
299+ objectplanmodifier .RequiresReplace (),
300+ },
301+ Attributes : map [string ]schema.Attribute {
302+ "provisioned" : schema.BoolAttribute {
303+ Description : "Whether a STACKIT Server Agent is provisioned at the server" ,
304+ Computed : true ,
305+ PlanModifiers : []planmodifier.Bool {
306+ boolplanmodifier .UseStateForUnknown (),
307+ },
308+ },
309+ "provisioning_policy" : schema.StringAttribute {
310+ Description : "Agent provisioning policy: `ALWAYS`, `NEVER`, or `INHERIT`. `INHERIT` follows the image default value." ,
311+ Optional : true ,
312+ Computed : true ,
313+ Default : stringdefault .StaticString ("INHERIT" ),
314+ Validators : []validator.String {
315+ stringvalidator .OneOf ("ALWAYS" , "NEVER" , "INHERIT" ),
316+ },
317+ PlanModifiers : []planmodifier.String {
318+ stringplanmodifier .RequiresReplace (), // trigger recreation on change
319+ },
320+ },
321+ },
322+ },
280323 "boot_volume" : schema.SingleNestedAttribute {
281324 Description : "The boot volume for the server" ,
282325 Optional : true ,
@@ -993,6 +1036,33 @@ func mapFields(ctx context.Context, serverResp *iaas.Server, model *Model, regio
9931036 model .NetworkInterfaces = types .ListNull (types .StringType )
9941037 }
9951038
1039+ // agent{...} block, determine the intent policy from Terraform state
1040+ currentPolicy := types .StringValue ("INHERIT" )
1041+ if ! (model .Agent .IsNull () || model .Agent .IsUnknown ()) {
1042+ var currentAgent agentModel
1043+ diags := model .Agent .As (ctx , & currentAgent , basetypes.ObjectAsOptions {})
1044+ if diags .HasError () {
1045+ return fmt .Errorf ("failed to map agent: %w" , core .DiagsToError (diags ))
1046+ }
1047+ if ! currentAgent .ProvisioningPolicy .IsNull () {
1048+ currentPolicy = currentAgent .ProvisioningPolicy
1049+ }
1050+ }
1051+ // agent{...} block, determine the 'provisioned' field from API response
1052+ agentProvisioned := types .BoolNull ()
1053+ if serverResp .Agent != nil && serverResp .Agent .Provisioned != nil {
1054+ agentProvisioned = types .BoolPointerValue (serverResp .Agent .Provisioned )
1055+ }
1056+ // agent{...} block, finalizing
1057+ agent , diags := types .ObjectValue (agentTypes , map [string ]attr.Value {
1058+ "provisioned" : agentProvisioned ,
1059+ "provisioning_policy" : currentPolicy ,
1060+ })
1061+ if diags .HasError () {
1062+ return fmt .Errorf ("failed to map agent: %w" , core .DiagsToError (diags ))
1063+ }
1064+ model .Agent = agent
1065+
9961066 if serverResp .BootVolume != nil {
9971067 // convert boot volume model
9981068 var bootVolumeModel = & bootVolumeModel {}
@@ -1061,6 +1131,14 @@ func toCreatePayload(ctx context.Context, model *Model) (*iaas.CreateServerPaylo
10611131 }
10621132 }
10631133
1134+ var agent = & agentModel {}
1135+ if ! (model .Agent .IsNull () || model .Agent .IsUnknown ()) {
1136+ diags := model .Agent .As (ctx , agent , basetypes.ObjectAsOptions {})
1137+ if diags .HasError () {
1138+ return nil , fmt .Errorf ("convert agent object to struct: %w" , core .DiagsToError (diags ))
1139+ }
1140+ }
1141+
10641142 labels , err := conversion .ToStringInterfaceMap (ctx , model .Labels )
10651143 if err != nil {
10661144 return nil , fmt .Errorf ("converting to Go map: %w" , err )
@@ -1082,6 +1160,16 @@ func toCreatePayload(ctx context.Context, model *Model) (*iaas.CreateServerPaylo
10821160 }
10831161 }
10841162
1163+ var agentPayload * iaas.ServerAgent
1164+ switch agent .ProvisioningPolicy .ValueString () {
1165+ case "ALWAYS" :
1166+ agentPayload = & iaas.ServerAgent {Provisioned : conversion .BoolValueToPointer (types .BoolValue (true ))}
1167+ case "NEVER" :
1168+ agentPayload = & iaas.ServerAgent {Provisioned : conversion .BoolValueToPointer (types .BoolValue (false ))}
1169+ case "INHERIT" :
1170+ agentPayload = nil // "agent" key is omitted from JSON thanks to omitempty
1171+ }
1172+
10851173 var userData * []byte
10861174 if ! model .UserData .IsNull () && ! model .UserData .IsUnknown () {
10871175 src := []byte (model .UserData .ValueString ())
@@ -1111,6 +1199,7 @@ func toCreatePayload(ctx context.Context, model *Model) (*iaas.CreateServerPaylo
11111199 return & iaas.CreateServerPayload {
11121200 AffinityGroup : conversion .StringValueToPointer (model .AffinityGroup ),
11131201 AvailabilityZone : conversion .StringValueToPointer (model .AvailabilityZone ),
1202+ Agent : agentPayload ,
11141203 BootVolume : bootVolumePayload ,
11151204 ImageId : conversion .StringValueToPointer (model .ImageId ),
11161205 KeypairName : conversion .StringValueToPointer (model .KeypairName ),
0 commit comments