in internal/services/azapi_resource.go [786:924]
func (r *AzapiResource) Read(ctx context.Context, request resource.ReadRequest, response *resource.ReadResponse) {
var model AzapiResourceModel
if response.Diagnostics.Append(request.State.Get(ctx, &model)...); response.Diagnostics.HasError() {
return
}
readTimeout, diags := model.Timeouts.Read(ctx, 5*time.Minute)
response.Diagnostics.Append(diags...)
if response.Diagnostics.HasError() {
return
}
// Ensure the context deadline has been set before calling ConfigureClientWithCustomRetry().
ctx, cancel := context.WithTimeout(ctx, readTimeout)
defer cancel()
id, err := parse.ResourceIDWithResourceType(model.ID.ValueString(), model.Type.ValueString())
if err != nil {
response.Diagnostics.AddError("Error parsing ID", err.Error())
return
}
ctx = tflog.SetField(ctx, "resource_id", id.ID())
client := r.ProviderData.ResourceClient.ConfigureClientWithCustomRetry(ctx, model.Retry, false)
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()))
response.State.RemoveResource(ctx)
return
}
response.Diagnostics.AddError("Failed to retrieve resource", fmt.Errorf("reading %s: %+v", id, err).Error())
return
}
state := model
state.Name = types.StringValue(id.Name)
state.ParentID = types.StringValue(id.ParentId)
state.Type = types.StringValue(fmt.Sprintf("%s@%s", id.AzureResourceType, id.ApiVersion))
requestBody := make(map[string]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
}
if bodyMap, ok := responseBody.(map[string]interface{}); ok {
if v, ok := bodyMap["location"]; ok && v != nil && location.Normalize(v.(string)) != location.Normalize(model.Location.ValueString()) {
state.Location = types.StringValue(v.(string))
}
if output := tags.FlattenTags(bodyMap["tags"]); len(output.Elements()) != 0 || len(state.Tags.Elements()) != 0 {
state.Tags = output
}
if requestBody["identity"] == nil {
// The following codes are used to reflect the actual changes of identity when it's not configured inside the body.
// And it suppresses the diff of nil identity and identity whose type is none.
identityFromResponse := identity.FlattenIdentity(bodyMap["identity"])
switch {
// Identity is not specified in config, and it's not in the response
case state.Identity.IsNull() && (identityFromResponse == nil || identityFromResponse.Type.ValueString() == string(identity.None)):
state.Identity = basetypes.NewListNull(identity.Model{}.ModelType())
// Identity is not specified in config, but it's in the response
case state.Identity.IsNull() && identityFromResponse != nil && identityFromResponse.Type.ValueString() != string(identity.None):
state.Identity = identity.ToList(*identityFromResponse)
// Identity is specified in config, but it's not in the response
case !state.Identity.IsNull() && identityFromResponse == nil:
stateIdentity := identity.FromList(state.Identity)
// skip when the configured identity type is none
if stateIdentity.Type.ValueString() == string(identity.None) {
// do nothing
} else {
state.Identity = basetypes.NewListNull(identity.Model{}.ModelType())
}
// Identity is specified in config, and it's in the response
case !state.Identity.IsNull() && identityFromResponse != nil:
stateIdentity := identity.FromList(state.Identity)
// suppress the diff of identity_ids = [] and identity_ids = null
if len(stateIdentity.IdentityIDs.Elements()) == 0 && len(identityFromResponse.IdentityIDs.Elements()) == 0 {
// to suppress the diff of identity_ids = [] and identity_ids = null
identityFromResponse.IdentityIDs = stateIdentity.IdentityIDs
}
state.Identity = identity.ToList(*identityFromResponse)
}
}
}
option := utils.UpdateJsonOption{
IgnoreCasing: model.IgnoreCasing.ValueBool(),
IgnoreMissingProperty: model.IgnoreMissingProperty.ValueBool(),
}
body := utils.UpdateObject(requestBody, responseBody, option)
data, err := json.Marshal(body)
if err != nil {
response.Diagnostics.AddError("Invalid body", err.Error())
return
}
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 {
response.Diagnostics.AddError("Failed to build output", err.Error())
return
}
state.Output = output
if !model.Body.IsNull() {
payload, err := dynamic.FromJSON(data, model.Body.UnderlyingValue().Type(ctx))
if err != nil {
tflog.Warn(ctx, fmt.Sprintf("Failed to parse payload: %s", err.Error()))
payload, err = dynamic.FromJSONImplied(data)
if err != nil {
response.Diagnostics.AddError("Invalid payload", err.Error())
return
}
}
state.Body = payload
}
if v, _ := request.Private.GetKey(ctx, FlagMoveState); v != nil && string(v) == "true" {
payload, err := flattenBody(responseBody, id.ResourceDef)
if err != nil {
response.Diagnostics.AddError("Invalid body", err.Error())
return
}
state.Body = payload
response.Diagnostics.Append(response.Private.SetKey(ctx, FlagMoveState, []byte("false"))...)
}
response.Diagnostics.Append(response.State.Set(ctx, state)...)
}