Skip to content

Commit

Permalink
bugfix: preflight request doesn not include identity (#613)
Browse files Browse the repository at this point in the history
* bugfix: preflight request doesn not include identity

* add test

* fix test
  • Loading branch information
ms-henglu authored Sep 13, 2024
1 parent ddb2daf commit 712f9ab
Show file tree
Hide file tree
Showing 5 changed files with 131 additions and 34 deletions.
71 changes: 56 additions & 15 deletions docs/guides/2.0-upgrade-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ Below is an overview of the changes coming 2.0. Each topic is covered in more de
* [Headers and Query Parameters Support](#headers-and-query-parameters-support)
* [Replacement Triggers](#replacement-triggers)
* [JMESPath Query Support](#jmespath-query-support)
* [Preflight Validation](#preflight-validation)
* [Breaking Changes](#breaking-changes)


Expand Down Expand Up @@ -211,14 +212,14 @@ The AzAPI Provider now supports customized retriable configuration for Azure API
```hcl
data "azapi_resource" "test" {
type = "Microsoft.Resources/resourceGroups@2024-03-01"
name = "example"
type = "Microsoft.Resources/resourceGroups@2024-03-01"
name = "example"
retry = {
error_message_regex = ["ResourceGroupNotFound"]
interval_seconds = 5
error_message_regex = ["ResourceGroupNotFound"]
interval_seconds = 5
max_interval_seconds = 30
multiplier = 1.5
multiplier = 1.5
randomization_factor = 0.5
}
}
Expand Down Expand Up @@ -293,7 +294,7 @@ resource "azapi_resource" "example" {
name = var.name
type = "Microsoft.Network/publicIPAddresses@2023-11-01"
parent_id = "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/example"
body = {
body = {
properties = {
sku = var.sku
zones = var.zones
Expand All @@ -315,7 +316,7 @@ resource "azapi_resource" "example" {
name = var.name
type = "Microsoft.Network/publicIPAddresses@2023-11-01"
parent_id = "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/example"
body = {
body = {
properties = {
sku = var.sku
zones = var.zones
Expand All @@ -339,13 +340,13 @@ The `response_export_values` attribute accepts a map where the key is the name f
```hcl
data "azapi_resource" "example" {
type = "Microsoft.ContainerRegistry/registries@2020-11-01-preview"
parent_id = "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/example"
name = "example"
response_export_values = {
login_server = "properties.loginServer"
quarantine_status = "properties.policies.quarantinePolicy.status"
}
type = "Microsoft.ContainerRegistry/registries@2020-11-01-preview"
parent_id = "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/example"
name = "example"
response_export_values = {
login_server = "properties.loginServer"
quarantine_status = "properties.policies.quarantinePolicy.status"
}
}
// it will output below object
Expand All @@ -366,6 +367,46 @@ output "quarantine_status" {
```

## Preflight Validation

Preflight validation is a new feature in the AzAPI Provider that helps ensure your configuration will deploy successfully. To enable preflight validation, set the `enable_preflight` attribute to `true` in the provider block.

In the example below, the provider block is configured to enable preflight validation. The `azapi_resource` block defines a virtual network resource with an invalid address prefix. When you run `terraform plan`, the preflight validation will fail and display an error message.

```hcl
provider "azapi" {
enable_preflight = true
}
resource "azapi_resource" "vnet" {
type = "Microsoft.Network/virtualNetworks@2024-01-01"
parent_id = azapi_resource.resourceGroup.id
name = "example-vnet"
location = "westus"
body = {
properties = {
addressSpace = {
addressPrefixes = [
"10.0.0.0/160",
]
}
}
}
}
```

When you run `terraform plan`, you will see the following error message:

```json
{
"code": "InvalidAddressPrefixFormat",
"target": "/subscriptions/....../resourceGroups/olgdwamq/providers/Microsoft.Network/virtualNetworks/example-vnet",
"message": "Address prefix 10.0.0.0/160 of resource /subscriptions/....../resourceGroups/olgdwamq/providers/Microsoft.Network/virtualNetworks/example-vnet is not formatted correctly. It should follow CIDR notation, for example 10.0.0.0/24.",
"details": []
}
```


## Breaking Changes

- Provider field `default_naming_prefix` and `default_naming_suffix` are removed.
Expand Down Expand Up @@ -453,4 +494,4 @@ output "quarantine_status" {
- The `use_msi` field now defaults to `false`.
How to fix:
Please set it to `true` explicitly if you want to authenticate using Managed Service Identity.
Please set it to `true` explicitly if you want to authenticate using Managed Service Identity.
2 changes: 1 addition & 1 deletion internal/services/azapi_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -561,7 +561,7 @@ func (r *AzapiResource) ModifyPlan(ctx context.Context, request resource.ModifyP
name = preflight.NamePlaceholder()
}

err = preflight.Validate(ctx, r.ProviderData.ResourceClient, plan.Type.ValueString(), parentId, name, plan.Location.ValueString(), plan.Body)
err = preflight.Validate(ctx, r.ProviderData.ResourceClient, plan.Type.ValueString(), parentId, name, plan.Location.ValueString(), plan.Body, plan.Identity)
if err != nil {
response.Diagnostics.AddError("Preflight Validation: Invalid configuration", err.Error())
return
Expand Down
46 changes: 46 additions & 0 deletions internal/services/azapi_resource_preflight_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,18 @@ func TestAccGenericResource_preflightExtensionResourceValidation(t *testing.T) {
})
}

func TestAccGenericResource_preflightWithIdentity(t *testing.T) {
data := acceptance.BuildTestData(t, "azapi_resource", "test")
r := GenericResource{}
data.ResourceTest(t, r, []resource.TestStep{
{
Config: r.preflightWithIdentity(data),
PlanOnly: true,
ExpectNonEmptyPlan: true,
},
})
}

func (r GenericResource) preflightMockPropertyValue(data acceptance.TestData) string {
return fmt.Sprintf(`
provider "azapi" {
Expand Down Expand Up @@ -160,3 +172,37 @@ provider "azapi" {
%s
`, r.extensionScope(data))
}

func (r GenericResource) preflightWithIdentity(data acceptance.TestData) string {
return fmt.Sprintf(`
provider "azapi" {
enable_preflight = true
}
%[1]s
resource "azapi_resource" "aksCluster" {
type = "Microsoft.ContainerService/managedClusters@2024-06-02-preview"
parent_id = azapi_resource.resourceGroup.id
name = azapi_resource.resourceGroup.id
location = "westus"
identity {
type = "SystemAssigned"
}
body = {
properties = {
agentPoolProfiles = [
{
count = 1
mode = "System"
name = "default"
vmSize = "Standard_DS2_v2"
},
]
dnsPrefix = "exampleaks"
}
}
schema_validation_enabled = false
}
`, r.template(data))
}
16 changes: 13 additions & 3 deletions internal/services/preflight/preflight.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (

"github.com/Azure/azure-sdk-for-go/sdk/azcore/arm"
"github.com/Azure/terraform-provider-azapi/internal/azure"
"github.com/Azure/terraform-provider-azapi/internal/azure/identity"
aztypes "github.com/Azure/terraform-provider-azapi/internal/azure/types"
"github.com/Azure/terraform-provider-azapi/internal/clients"
"github.com/Azure/terraform-provider-azapi/internal/services/dynamic"
Expand Down Expand Up @@ -90,7 +91,7 @@ func IsSupported(resourceType string, parentId string) bool {
}

// Validate validates the resource using the preflight API
func Validate(ctx context.Context, client *clients.ResourceClient, resourceType string, parentId string, name string, location string, body types.Dynamic) error {
func Validate(ctx context.Context, client *clients.ResourceClient, resourceType string, parentId string, name string, location string, body types.Dynamic, identity types.List) error {
azureResourceType, apiVersion, err := utils.GetAzureResourceTypeApiVersion(resourceType)
if err != nil {
return err
Expand All @@ -104,7 +105,7 @@ func Validate(ctx context.Context, client *clients.ResourceClient, resourceType
}

resource := make(map[string]interface{})
err = unmarshalPreflightBody(body, &resource)
err = unmarshalPreflightBody(body, identity, &resource)
if err != nil {
tflog.Warn(ctx, fmt.Sprintf("Skipping preflight validation for resource %s because the body is invalid: %v", resourceType, err))
return nil
Expand All @@ -119,7 +120,7 @@ func Validate(ctx context.Context, client *clients.ResourceClient, resourceType
return err
}

func unmarshalPreflightBody(input types.Dynamic, out *map[string]interface{}) error {
func unmarshalPreflightBody(input types.Dynamic, identityList types.List, out *map[string]interface{}) error {
if input.IsNull() || input.IsUnknown() || input.IsUnderlyingValueUnknown() {
return fmt.Errorf("input is null or unknown")
}
Expand Down Expand Up @@ -150,6 +151,15 @@ func unmarshalPreflightBody(input types.Dynamic, out *map[string]interface{}) er
return fmt.Errorf("unknown value found outside the properties bag")
}
}

if (*out)["identity"] == nil && !identityList.IsNull() && !identityList.IsUnknown() {
identityModel := identity.FromList(identityList)
expandedIdentity, err := identity.ExpandIdentity(identityModel)
if err != nil {
return err
}
(*out)["identity"] = expandedIdentity
}
return nil
}

Expand Down
30 changes: 15 additions & 15 deletions templates/guides/2.0-upgrade-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -212,14 +212,14 @@ The AzAPI Provider now supports customized retriable configuration for Azure API
```hcl
data "azapi_resource" "test" {
type = "Microsoft.Resources/resourceGroups@2024-03-01"
name = "example"
type = "Microsoft.Resources/resourceGroups@2024-03-01"
name = "example"
retry = {
error_message_regex = ["ResourceGroupNotFound"]
interval_seconds = 5
error_message_regex = ["ResourceGroupNotFound"]
interval_seconds = 5
max_interval_seconds = 30
multiplier = 1.5
multiplier = 1.5
randomization_factor = 0.5
}
}
Expand Down Expand Up @@ -294,7 +294,7 @@ resource "azapi_resource" "example" {
name = var.name
type = "Microsoft.Network/publicIPAddresses@2023-11-01"
parent_id = "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/example"
body = {
body = {
properties = {
sku = var.sku
zones = var.zones
Expand All @@ -316,7 +316,7 @@ resource "azapi_resource" "example" {
name = var.name
type = "Microsoft.Network/publicIPAddresses@2023-11-01"
parent_id = "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/example"
body = {
body = {
properties = {
sku = var.sku
zones = var.zones
Expand All @@ -340,13 +340,13 @@ The `response_export_values` attribute accepts a map where the key is the name f
```hcl
data "azapi_resource" "example" {
type = "Microsoft.ContainerRegistry/registries@2020-11-01-preview"
parent_id = "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/example"
name = "example"
response_export_values = {
login_server = "properties.loginServer"
quarantine_status = "properties.policies.quarantinePolicy.status"
}
type = "Microsoft.ContainerRegistry/registries@2020-11-01-preview"
parent_id = "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/example"
name = "example"
response_export_values = {
login_server = "properties.loginServer"
quarantine_status = "properties.policies.quarantinePolicy.status"
}
}
// it will output below object
Expand Down Expand Up @@ -494,4 +494,4 @@ When you run `terraform plan`, you will see the following error message:
- The `use_msi` field now defaults to `false`.
How to fix:
Please set it to `true` explicitly if you want to authenticate using Managed Service Identity.
Please set it to `true` explicitly if you want to authenticate using Managed Service Identity.

0 comments on commit 712f9ab

Please sign in to comment.