in internal/services/azapi_update_resource.go [344:479]
func (r *AzapiUpdateResource) CreateUpdate(ctx context.Context, requestConfig tfsdk.Config, plan tfsdk.Plan, state *tfsdk.State, diagnostics *diag.Diagnostics, privateData PrivateData) {
var config, model AzapiUpdateResourceModel
diagnostics.Append(requestConfig.Get(ctx, &config)...)
if diagnostics.Append(plan.Get(ctx, &model)...); diagnostics.HasError() {
return
}
isNewResource := state == nil || state.Raw.IsNull()
var timeout time.Duration
var diags diag.Diagnostics
if isNewResource {
timeout, diags = model.Timeouts.Create(ctx, 30*time.Minute)
if diagnostics.Append(diags...); diagnostics.HasError() {
return
}
} else {
timeout, diags = model.Timeouts.Update(ctx, 30*time.Minute)
if diagnostics.Append(diags...); diagnostics.HasError() {
return
}
}
ctx, cancel := context.WithTimeout(ctx, timeout)
defer cancel()
var id parse.ResourceId
// We need to ensure that the ID parsed in create and update is the same to produce consistent results.
// In update, all these fields are set, using resource_id and type is able to parse the parent_id and name which are used to build it.
// But using parent_id, name and type is not able to parse the original resource_id, because the last resource type segment comes from the type instead of the resource_id.
if resourceId := model.ResourceID.ValueString(); len(resourceId) != 0 {
buildId, err := parse.ResourceIDWithResourceType(model.ResourceID.ValueString(), model.Type.ValueString())
if err != nil {
diagnostics.AddError("Invalid configuration", err.Error())
return
}
id = buildId
} else {
buildId, err := parse.NewResourceID(model.Name.ValueString(), model.ParentID.ValueString(), model.Type.ValueString())
if err != nil {
diagnostics.AddError("Invalid configuration", err.Error())
return
}
id = buildId
}
ctx = tflog.SetField(ctx, "resource_id", id.ID())
// Ensure the context deadline has been set before calling ConfigureClientWithCustomRetry().
client := r.ProviderData.ResourceClient.ConfigureClientWithCustomRetry(ctx, model.Retry, false)
existing, err := client.Get(ctx, id.AzureResourceId, id.ApiVersion, clients.NewRequestOptions(AsMapOfString(model.ReadHeaders), AsMapOfLists(model.ReadQueryParameters)))
if err != nil {
diagnostics.AddError("Failed to retrieve resource", fmt.Errorf("checking for presence of existing %s: %+v", id, err).Error())
return
}
if utils.GetId(existing) == nil {
diagnostics.AddError("Failed to retrieve resource", fmt.Errorf("update target does not exist %s", id).Error())
return
}
var requestBody interface{}
if err := unmarshalBody(model.Body, &requestBody); err != nil {
diagnostics.AddError("Invalid body", fmt.Sprintf(`The argument "body" is invalid: err: %+v`, err))
return
}
if requestBody != nil {
requestBody = utils.MergeObject(existing, requestBody)
} else {
requestBody = existing
}
SensitiveBody := make(map[string]interface{})
if err := unmarshalBody(config.SensitiveBody, &SensitiveBody); err != nil {
diagnostics.AddError("Invalid sensitive_body", fmt.Sprintf(`The argument "sensitive_body" is invalid: %s`, err.Error()))
return
}
if SensitiveBody != nil {
requestBody = utils.MergeObject(requestBody, SensitiveBody)
}
if id.ResourceDef != nil {
requestBody = (*id.ResourceDef).GetWriteOnly(utils.NormalizeObject(requestBody))
}
lockIds := AsStringList(model.Locks)
slices.Sort(lockIds)
for _, lockId := range lockIds {
locks.ByID(lockId)
defer locks.UnlockByID(lockId)
}
_, err = client.CreateOrUpdate(ctx, id.AzureResourceId, id.ApiVersion, requestBody, clients.NewRequestOptions(AsMapOfString(model.UpdateHeaders), AsMapOfLists(model.UpdateQueryParameters)))
if err != nil {
diagnostics.AddError("Failed to update resource", fmt.Errorf("updating %q: %+v", id, err).Error())
return
}
responseBody, err := client.Get(ctx, id.AzureResourceId, id.ApiVersion, clients.NewRequestOptions(AsMapOfString(model.ReadHeaders), AsMapOfLists(model.ReadQueryParameters)))
if err != nil {
if utils.ResponseErrorWasNotFound(err) {
tflog.Info(ctx, fmt.Sprintf("Error reading %q - removing from state", id.ID()))
state.RemoveResource(ctx)
return
}
diagnostics.AddError("Failed to retrieve resource", fmt.Errorf("reading %s: %+v", id, err).Error())
return
}
model.ID = basetypes.NewStringValue(id.ID())
model.Name = basetypes.NewStringValue(id.Name)
model.ParentID = basetypes.NewStringValue(id.ParentId)
model.ResourceID = basetypes.NewStringValue(id.AzureResourceId)
var defaultOutput interface{}
if !r.ProviderData.Features.DisableDefaultOutput {
defaultOutput = id.ResourceDef.GetReadOnly(responseBody)
defaultOutput = utils.RemoveFields(defaultOutput, volatileFieldList())
}
output, err := buildOutputFromBody(responseBody, model.ResponseExportValues, defaultOutput)
if err != nil {
diagnostics.AddError("Failed to build output", err.Error())
return
}
model.Output = output
diagnostics.Append(state.Set(ctx, model)...)
writeOnlyBytes, err := dynamic.ToJSON(config.SensitiveBody)
if err != nil {
diagnostics.AddError("Invalid sensitive_body", err.Error())
return
}
diagnostics.Append(ephemeralBodyPrivateMgr.Set(ctx, privateData, writeOnlyBytes)...)
}