internal/services/azapi_resource_id_data_source.go (176 lines of code) (raw):

package services import ( "context" "strings" "time" "github.com/Azure/azure-sdk-for-go/sdk/azcore/arm" "github.com/Azure/terraform-provider-azapi/internal/docstrings" "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/attr" "github.com/hashicorp/terraform-plugin-framework/datasource" "github.com/hashicorp/terraform-plugin-framework/datasource/schema" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types/basetypes" ) type ResourceIdDataSourceModel struct { ID types.String `tfsdk:"id"` Type types.String `tfsdk:"type"` Name types.String `tfsdk:"name"` ParentID types.String `tfsdk:"parent_id"` ResourceID types.String `tfsdk:"resource_id"` ResourceGroupName types.String `tfsdk:"resource_group_name"` SubscriptionID types.String `tfsdk:"subscription_id"` ProviderNamespace types.String `tfsdk:"provider_namespace"` Parts types.Map `tfsdk:"parts"` Timeouts timeouts.Value `tfsdk:"timeouts"` } type ResourceIdDataSource struct { } var _ datasource.DataSource = &ResourceIdDataSource{} var _ datasource.DataSourceWithValidateConfig = &ResourceIdDataSource{} func (r *ResourceIdDataSource) Metadata(ctx context.Context, request datasource.MetadataRequest, response *datasource.MetadataResponse) { response.TypeName = request.ProviderTypeName + "_resource_id" } func (r *ResourceIdDataSource) Schema(ctx context.Context, request datasource.SchemaRequest, response *datasource.SchemaResponse) { response.Schema = schema.Schema{ 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(), }, "parent_id": schema.StringAttribute{ Optional: true, Computed: true, Validators: []validator.String{ myvalidator.StringIsResourceID(), }, MarkdownDescription: docstrings.ParentID(), }, "name": schema.StringAttribute{ Optional: true, Computed: true, MarkdownDescription: "The name of the Azure resource.", }, "resource_id": schema.StringAttribute{ Optional: true, Computed: true, Validators: []validator.String{ myvalidator.StringIsResourceID(), }, MarkdownDescription: "The ID of an existing Azure source.", }, "resource_group_name": schema.StringAttribute{ Computed: true, MarkdownDescription: "The resource group name of the Azure resource.", }, "subscription_id": schema.StringAttribute{ Computed: true, MarkdownDescription: "The subscription ID of the Azure resource.", }, "provider_namespace": schema.StringAttribute{ Computed: true, MarkdownDescription: "The Azure resource provider namespace of the Azure resource.", }, "parts": schema.MapAttribute{ Computed: true, ElementType: types.StringType, MarkdownDescription: "A map of the resource ID parts, where the key is the part name and the value is the part value. e.g. `virtualNetworks=myVnet`.", }, }, Blocks: map[string]schema.Block{ "timeouts": timeouts.Block(ctx, timeouts.Opts{ Read: true, }), }, } } func (r *ResourceIdDataSource) ValidateConfig(ctx context.Context, request datasource.ValidateConfigRequest, response *datasource.ValidateConfigResponse) { var config *ResourceIdDataSourceModel if response.Diagnostics.Append(request.Config.Get(ctx, &config)...); response.Diagnostics.HasError() { return } if config == nil { return } if config.Name.IsNull() && !config.ParentID.IsNull() { response.Diagnostics.AddError("Invalid configuration", `The argument "name" is required when the argument "parent_id" is set`) } if !config.Name.IsNull() && config.ParentID.IsNull() { response.Diagnostics.AddError("Invalid configuration", `The argument "parent_id" is required when the argument "name" is set`) } if config.Name.IsNull() && config.ResourceID.IsNull() { response.Diagnostics.AddError("Invalid configuration", `One of the arguments "name" or "resource_id" must be set`) } if !config.Name.IsNull() && !config.ResourceID.IsNull() { response.Diagnostics.AddError("Invalid configuration", `Only one of the arguments "name" or "resource_id" can be set`) } if response.Diagnostics.HasError() { return } } func (r *ResourceIdDataSource) Read(ctx context.Context, request datasource.ReadRequest, response *datasource.ReadResponse) { var model ResourceIdDataSourceModel if response.Diagnostics.Append(request.Config.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 } ctx, cancel := context.WithTimeout(ctx, readTimeout) defer cancel() var id parse.ResourceId if name := model.Name.ValueString(); len(name) != 0 { buildId, err := parse.NewResourceID(model.Name.ValueString(), model.ParentID.ValueString(), model.Type.ValueString()) if err != nil { response.Diagnostics.AddError("Invalid configuration", err.Error()) return } id = buildId } else { buildId, err := parse.ResourceIDWithResourceType(model.ResourceID.ValueString(), model.Type.ValueString()) if err != nil { response.Diagnostics.AddError("Invalid configuration", err.Error()) return } id = buildId } model.ID = basetypes.NewStringValue(id.ID()) model.Name = basetypes.NewStringValue(id.Name) model.ParentID = basetypes.NewStringValue(id.ParentId) model.ResourceID = basetypes.NewStringValue(id.AzureResourceId) armId, err := arm.ParseResourceID(id.AzureResourceId) if id.AzureResourceId == "/" { armId, err = &arm.ResourceID{ ResourceType: arm.TenantResourceType, }, nil } if err != nil { response.Diagnostics.AddError("Invalid configuration", err.Error()) return } model.ResourceGroupName = basetypes.NewStringValue(armId.ResourceGroupName) model.SubscriptionID = basetypes.NewStringValue(armId.SubscriptionID) model.ProviderNamespace = basetypes.NewStringValue(armId.ResourceType.Namespace) path := id.AzureResourceId path = strings.TrimPrefix(path, "/") path = strings.TrimSuffix(path, "/") components := strings.Split(path, "/") parts := make(map[string]attr.Value) for i := 0; i < len(components)-1; i += 2 { parts[components[i]] = basetypes.NewStringValue(components[i+1]) } model.Parts = basetypes.NewMapValueMust(types.StringType, parts) response.Diagnostics.Append(response.State.Set(ctx, &model)...) }