internal/services/azapi_resource_action_ephemeral.go (179 lines of code) (raw):

package services import ( "context" "fmt" "slices" "time" "github.com/Azure/terraform-provider-azapi/internal/clients" "github.com/Azure/terraform-provider-azapi/internal/docstrings" "github.com/Azure/terraform-provider-azapi/internal/locks" "github.com/Azure/terraform-provider-azapi/internal/retry" "github.com/Azure/terraform-provider-azapi/internal/services/myvalidator" "github.com/Azure/terraform-provider-azapi/internal/services/parse" "github.com/hashicorp/terraform-plugin-framework-timeouts/resource/timeouts" "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator" "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" "github.com/hashicorp/terraform-plugin-framework/ephemeral" "github.com/hashicorp/terraform-plugin-framework/ephemeral/schema" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types/basetypes" "github.com/hashicorp/terraform-plugin-log/tflog" ) type ActionEphemeralModel struct { ID types.String `tfsdk:"id"` Type types.String `tfsdk:"type"` ResourceId types.String `tfsdk:"resource_id"` Action types.String `tfsdk:"action"` Method types.String `tfsdk:"method"` Body types.Dynamic `tfsdk:"body"` Locks types.List `tfsdk:"locks"` ResponseExportValues types.Dynamic `tfsdk:"response_export_values"` Output types.Dynamic `tfsdk:"output"` Timeouts timeouts.Value `tfsdk:"timeouts"` Retry retry.RetryValue `tfsdk:"retry"` Headers types.Map `tfsdk:"headers"` QueryParameters types.Map `tfsdk:"query_parameters"` } type ActionEphemeral struct { ProviderData *clients.Client } var _ ephemeral.EphemeralResource = &ActionEphemeral{} var _ ephemeral.EphemeralResourceWithConfigure = &ActionEphemeral{} func (r *ActionEphemeral) Metadata(ctx context.Context, request ephemeral.MetadataRequest, response *ephemeral.MetadataResponse) { response.TypeName = request.ProviderTypeName + "_resource_action" } func (r *ActionEphemeral) Configure(ctx context.Context, request ephemeral.ConfigureRequest, response *ephemeral.ConfigureResponse) { if v, ok := request.ProviderData.(*clients.Client); ok { r.ProviderData = v } } func (r *ActionEphemeral) Schema(ctx context.Context, request ephemeral.SchemaRequest, response *ephemeral.SchemaResponse) { response.Schema = schema.Schema{ MarkdownDescription: "Performs an action on an existing Azure resource. ", Attributes: map[string]schema.Attribute{ "id": schema.StringAttribute{ Computed: true, MarkdownDescription: docstrings.ID(), }, "type": schema.StringAttribute{ Required: true, Validators: []validator.String{ myvalidator.StringIsResourceType(), }, MarkdownDescription: docstrings.Type(), }, "resource_id": schema.StringAttribute{ Required: true, Validators: []validator.String{ myvalidator.StringIsResourceID(), }, MarkdownDescription: "The ID of an existing Azure source.", }, "action": schema.StringAttribute{ Optional: true, MarkdownDescription: docstrings.ResourceAction(), }, "method": schema.StringAttribute{ Optional: true, Computed: true, Validators: []validator.String{ stringvalidator.OneOf("POST", "PATCH", "PUT", "DELETE", "GET", "HEAD"), }, MarkdownDescription: "Specifies the HTTP method of the azure resource action. Allowed values are `POST`, `PATCH`, `PUT` and `DELETE`. Defaults to `POST`.", }, // The body attribute is a dynamic attribute that only allows users to specify the resource body as an HCL object "body": schema.DynamicAttribute{ Optional: true, MarkdownDescription: docstrings.Body(), Validators: []validator.Dynamic{ myvalidator.DynamicIsNotStringValidator(), }, }, "locks": schema.ListAttribute{ ElementType: types.StringType, Optional: true, Validators: []validator.List{ listvalidator.ValueStringsAre(myvalidator.StringIsNotEmpty()), }, MarkdownDescription: docstrings.Locks(), }, "response_export_values": schema.DynamicAttribute{ Optional: true, MarkdownDescription: docstrings.ResponseExportValues(), }, "output": schema.DynamicAttribute{ Computed: true, MarkdownDescription: docstrings.Output("ephemeral.azapi_resource_action"), }, "retry": retry.RetrySchema(ctx), "headers": schema.MapAttribute{ ElementType: types.StringType, Optional: true, MarkdownDescription: "A map of headers to include in the request", }, "query_parameters": schema.MapAttribute{ ElementType: types.ListType{ ElemType: types.StringType, }, Optional: true, MarkdownDescription: "A map of query parameters to include in the request", }, }, Blocks: map[string]schema.Block{ "timeouts": timeouts.Block(ctx, timeouts.Opts{ Read: true, }), }, } } func (r *ActionEphemeral) Open(ctx context.Context, request ephemeral.OpenRequest, response *ephemeral.OpenResponse) { var model ActionEphemeralModel if response.Diagnostics.Append(request.Config.Get(ctx, &model)...); response.Diagnostics.HasError() { return } readTimeout, diags := model.Timeouts.Read(ctx, 5*time.Minute) if response.Diagnostics.Append(diags...); response.Diagnostics.HasError() { return } ctx, cancel := context.WithTimeout(ctx, readTimeout) defer cancel() id, err := parse.ResourceIDWithResourceType(model.ResourceId.ValueString(), model.Type.ValueString()) if err != nil { response.Diagnostics.AddError("Invalid configuration", err.Error()) return } ctx = tflog.SetField(ctx, "resource_id", id.ID()) var requestBody interface{} if err := unmarshalBody(model.Body, &requestBody); err != nil { response.Diagnostics.AddError("Invalid body", fmt.Sprintf(`The argument "body" is invalid: %s`, err.Error())) return } method := model.Method.ValueString() if method == "" { method = "POST" } lockIds := AsStringList(model.Locks) slices.Sort(lockIds) for _, lockId := range lockIds { locks.ByID(lockId) defer locks.UnlockByID(lockId) } // Ensure the context deadline has been set before calling ConfigureClientWithCustomRetry(). client := r.ProviderData.ResourceClient.ConfigureClientWithCustomRetry(ctx, model.Retry, false) responseBody, err := client.Action(ctx, id.AzureResourceId, model.Action.ValueString(), id.ApiVersion, method, requestBody, clients.NewRequestOptions(AsMapOfString(model.Headers), AsMapOfLists(model.QueryParameters))) if err != nil { response.Diagnostics.AddError("Failed to perform action", fmt.Errorf("performing action %s of %q: %+v", model.Action.ValueString(), id, err).Error()) return } resourceId := id.ID() if actionName := model.Action.ValueString(); actionName != "" { resourceId = fmt.Sprintf("%s/%s", id.ID(), actionName) } model.ID = basetypes.NewStringValue(resourceId) output, err := buildOutputFromBody(responseBody, model.ResponseExportValues, responseBody) if err != nil { response.Diagnostics.AddError("Failed to build output", err.Error()) return } model.Output = output response.Diagnostics.Append(response.Result.Set(ctx, model)...) }