Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
135 changes: 135 additions & 0 deletions docs/data-sources/data_plane_resource.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
---
page_title: "azapi_data_plane_resource Data Source - terraform-provider-azapi"
subcategory: ""
description: |-
This data source can read some Azure data plane resources.
---

# azapi_data_plane_resource (Data Source)

This data source can read some Azure data plane resources.

<!-- schema generated by tfplugindocs -->
## Schema

### Required

- `name` (String) Specifies the name of the Azure data plane resource.
- `parent_id` (String) The ID of the Azure resource to get the data from.
- `type` (String) In a format like `<resource-type>@<api-version>`. `<resource-type>` is the Azure resource type, for example, `Microsoft.Storage/storageAccounts`. `<api-version>` is version of the API used to manage this azure resource.

### Optional

- `headers` (Map of String) A mapping of headers to be sent with the read request.
- `locks` (List of String) A list of ARM resource IDs which are used to avoid create/modify/delete azapi resources at the same time.
- `query_parameters` (Map of List of String) A mapping of query parameters to be sent with the read request.
- `response_export_values` (Dynamic) The attribute can accept either a list or a map.

- **List**: A list of paths that need to be exported from the response body. Setting it to `["*"]` will export the full response body. Here's an example. If it sets to `["properties.loginServer", "properties.policies.quarantinePolicy.status"]`, it will set the following HCL object to the computed property output.

```text
{
properties = {
loginServer = "registry1.azurecr.io"
policies = {
quarantinePolicy = {
status = "disabled"
}
}
}
}
```

- **Map**: A map where the key is the name for the result and the value is a JMESPath query string to filter the response. Here's an example. If it sets to `{"login_server": "properties.loginServer", "quarantine_status": "properties.policies.quarantinePolicy.status"}`, it will set the following HCL object to the computed property output.

```text
{
"login_server" = "registry1.azurecr.io"
"quarantine_status" = "disabled"
}
```

To learn more about JMESPath, visit [JMESPath](https://jmespath.org/).
- `retry` (Attributes) The retry object supports the following attributes: (see [below for nested schema](#nestedatt--retry))
- `sensitive_response_export_values` (Dynamic) The attribute can accept either a list or a map.

- **List**: A list of paths that need to be exported from the response body. Setting it to `["*"]` will export the full response body. Here's an example. If it sets to `["properties.loginServer", "properties.policies.quarantinePolicy.status"]`, it will set the following HCL object to the computed property sensitive_output.

```text
{
properties = {
loginServer = "registry1.azurecr.io"
policies = {
quarantinePolicy = {
status = "disabled"
}
}
}
}
```

- **Map**: A map where the key is the name for the result and the value is a JMESPath query string to filter the response. Here's an example. If it sets to `{"login_server": "properties.loginServer", "quarantine_status": "properties.policies.quarantinePolicy.status"}`, it will set the following HCL object to the computed property sensitive_output.

```text
{
"login_server" = "registry1.azurecr.io"
"quarantine_status" = "disabled"
}
```

To learn more about JMESPath, visit [JMESPath](https://jmespath.org/).
- `timeouts` (Block, Optional) (see [below for nested schema](#nestedblock--timeouts))

### Read-Only

- `id` (String) The ID of the Azure resource.
- `output` (Dynamic) The output HCL object containing the properties specified in `response_export_values`. Here are some examples to use the values.

```terraform
// it will output "registry1.azurecr.io"
output "login_server" {
value = data.azapi_data_plane_data_source.example.output.properties.loginServer
}

// it will output "disabled"
output "quarantine_policy" {
value = data.azapi_data_plane_data_source.example.output.properties.policies.quarantinePolicy.status
}
```
- `sensitive_output` (Dynamic, Sensitive) The output HCL object containing the properties specified in `sensitive_response_export_values`. Here are some examples to use the values.

```terraform
// it will output "registry1.azurecr.io"
output "login_server" {
value = data.azapi_data_plane_data_source.example.sensitive_output.properties.loginServer
sensitive = true
}

// it will output "disabled"
output "quarantine_policy" {
value = data.azapi_data_plane_data_source.example.sensitive_output.properties.policies.quarantinePolicy.status
sensitive = true
}
```

<a id="nestedatt--retry"></a>
### Nested Schema for `retry`

Required:

- `error_message_regex` (List of String) A list of regular expressions to match against error messages. If any of the regular expressions match, the request will be retried.

Optional:

- `interval_seconds` (Number) The base number of seconds to wait between retries. Default is `10`.
- `max_interval_seconds` (Number) The maximum number of seconds to wait between retries. Default is `180`.
- `multiplier` (Number) The multiplier to apply to the interval between retries. Default is `1.5`.
- `randomization_factor` (Number) The randomization factor to apply to the interval between retries. The formula for the randomized interval is: `RetryInterval * (random value in range [1 - RandomizationFactor, 1 + RandomizationFactor])`. Therefore set to zero `0.0` for no randomization. Default is `0.5`.


<a id="nestedblock--timeouts"></a>
### Nested Schema for `timeouts`

Optional:

- `read` (String) A string that can be [parsed as a duration](https://pkg.go.dev/time#ParseDuration) consisting of numbers and unit suffixes, such as "30s" or "2h45m". Valid time units are "s" (seconds), "m" (minutes), "h" (hours). Read operations occur during any refresh or planning operation when refresh is enabled.
3 changes: 3 additions & 0 deletions internal/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -732,6 +732,9 @@ func (p Provider) DataSources(ctx context.Context) []func() datasource.DataSourc
func() datasource.DataSource {
return &services.ClientConfigDataSource{}
},
func() datasource.DataSource {
return &services.DataPlaneDataSource{}
},
}

}
Expand Down
205 changes: 205 additions & 0 deletions internal/services/azapi_data_plane_data_source.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
package services

import (
"context"
"encoding/json"
"fmt"
"time"

"github.com/Azure/terraform-provider-azapi/internal/clients"
"github.com/Azure/terraform-provider-azapi/internal/docstrings"
"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/Azure/terraform-provider-azapi/utils"
"github.com/hashicorp/terraform-plugin-framework-timeouts/resource/timeouts"
"github.com/hashicorp/terraform-plugin-framework-validators/listvalidator"
"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"
"github.com/hashicorp/terraform-plugin-log/tflog"
)

type DataPlaneDataSourceModel struct {
ID types.String `tfsdk:"id"`
Name types.String `tfsdk:"name"`
ParentID types.String `tfsdk:"parent_id"`
Type types.String `tfsdk:"type"`
ResponseExportValues types.Dynamic `tfsdk:"response_export_values"`
SensitiveResponseExportValues types.Dynamic `tfsdk:"sensitive_response_export_values"`
Retry retry.RetryValue `tfsdk:"retry"`
Locks types.List `tfsdk:"locks"`
Output types.Dynamic `tfsdk:"output"`
SensitiveOutput types.Dynamic `tfsdk:"sensitive_output"`
Timeouts timeouts.Value `tfsdk:"timeouts"`
Headers types.Map `tfsdk:"headers"`
QueryParameters types.Map `tfsdk:"query_parameters"`
}

type DataPlaneDataSource struct {
ProviderData *clients.Client
}

var _ datasource.DataSource = &DataPlaneDataSource{}
var _ datasource.DataSourceWithConfigure = &DataPlaneDataSource{}

func (r *DataPlaneDataSource) Configure(ctx context.Context, request datasource.ConfigureRequest, response *datasource.ConfigureResponse) {
tflog.Debug(ctx, "Configuring azapi_data_plane_data_source")
if v, ok := request.ProviderData.(*clients.Client); ok {
r.ProviderData = v
}
}

func (r *DataPlaneDataSource) Metadata(ctx context.Context, request datasource.MetadataRequest, response *datasource.MetadataResponse) {
response.TypeName = request.ProviderTypeName + "_data_plane_resource"
}

func (r *DataPlaneDataSource) Schema(ctx context.Context, request datasource.SchemaRequest, response *datasource.SchemaResponse) {
response.Schema = schema.Schema{
MarkdownDescription: "This data source can read some Azure data plane resources.",
Attributes: map[string]schema.Attribute{
"id": schema.StringAttribute{
Computed: true,
MarkdownDescription: docstrings.ID(),
},

"name": schema.StringAttribute{
Required: true,
MarkdownDescription: "Specifies the name of the Azure data plane resource.",
},

"parent_id": schema.StringAttribute{
Required: true,
Validators: []validator.String{
myvalidator.StringIsNotEmpty(),
},
MarkdownDescription: "The ID of the Azure resource to get the data from.",
},

"type": schema.StringAttribute{
Required: true,
Validators: []validator.String{
myvalidator.StringIsResourceType(),
},
MarkdownDescription: docstrings.Type(),
},

"response_export_values": schema.DynamicAttribute{
Optional: true,
MarkdownDescription: docstrings.ResponseExportValues(),
},

"sensitive_response_export_values": schema.DynamicAttribute{
Optional: true,
MarkdownDescription: docstrings.SensitiveResponseExportValues(),
},

"retry": retry.RetrySchema(ctx),

"locks": schema.ListAttribute{
ElementType: types.StringType,
Optional: true,
Validators: []validator.List{
listvalidator.ValueStringsAre(myvalidator.StringIsNotEmpty()),
},
MarkdownDescription: docstrings.Locks(),
},

"output": schema.DynamicAttribute{
Computed: true,
MarkdownDescription: docstrings.Output("data.azapi_data_plane_data_source"),
},

"sensitive_output": schema.DynamicAttribute{
Computed: true,
Sensitive: true,
MarkdownDescription: docstrings.SensitiveOutput("data.azapi_data_plane_data_source"),
},

"headers": schema.MapAttribute{
ElementType: types.StringType,
Optional: true,
MarkdownDescription: "A mapping of headers to be sent with the read request.",
},

"query_parameters": schema.MapAttribute{
ElementType: types.ListType{
ElemType: types.StringType,
},
Optional: true,
MarkdownDescription: "A mapping of query parameters to be sent with the read request.",
},
},

Blocks: map[string]schema.Block{
"timeouts": timeouts.Block(ctx, timeouts.Opts{
Read: true,
}),
},
}
}

func (r *DataPlaneDataSource) Read(ctx context.Context, request datasource.ReadRequest, response *datasource.ReadResponse) {
var model *DataPlaneDataSourceModel
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()

id, err := parse.NewDataPlaneResourceId(model.Name.ValueString(), model.ParentID.ValueString(), model.Type.ValueString())
if err != nil {
response.Diagnostics.AddError("Error parsing ID", err.Error())
return
}
ctx = tflog.SetField(ctx, "resource_id", id.ID())

// Ensure that the context deadline has been set before calling ConfigureClientWithCustomRetry().
client := r.ProviderData.DataPlaneClient.ConfigureClientWithCustomRetry(ctx, model.Retry, false)

responseBody, err := client.Get(ctx, id, clients.NewRequestOptions(AsMapOfString(model.Headers), AsMapOfLists(model.QueryParameters)))
if err != nil {
if utils.ResponseErrorWasNotFound(err) {
response.Diagnostics.AddError("Resource not found", fmt.Errorf("resource %q not found", id).Error())
return
}
response.Diagnostics.AddError("Failed to retrieve resource", fmt.Errorf("retrieving resource %q: %+v", id, err).Error())
return
}

if _, err := json.Marshal(responseBody); err != nil {
response.Diagnostics.AddError("Invalid body", err.Error())
return
}

model.ID = basetypes.NewStringValue(id.ID())

output, err := buildOutputFromBody(responseBody, model.ResponseExportValues, nil)
if err != nil {
response.Diagnostics.AddError("Failed to build output", err.Error())
return
}
model.Output = output

sensitiveOutput, err := buildOutputFromBody(responseBody, model.SensitiveResponseExportValues, nil)
if err != nil {
response.Diagnostics.AddError("Failed to build sensitive output", err.Error())
return
}
model.SensitiveOutput = sensitiveOutput

model.Name = basetypes.NewStringValue(id.Name)
model.ParentID = basetypes.NewStringValue(id.ParentId)
model.Type = basetypes.NewStringValue(fmt.Sprintf("%s@%s", id.AzureResourceType, id.ApiVersion))

response.Diagnostics.Append(response.State.Set(ctx, model)...)
}
1 change: 1 addition & 0 deletions internal/services/azapi_data_plane_data_source_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package services
Loading