From 13a9edb18a42b32d5f1a97510895ddd5bd93ec4b Mon Sep 17 00:00:00 2001 From: Angie Pinilla Date: Tue, 24 Nov 2020 01:26:24 -0500 Subject: [PATCH 1/2] add sync-state information --- .../service/networkfirewall/waiter/waiter.go | 1 + aws/resource_aws_networkfirewall_firewall.go | 79 +++++++++++++++++++ ...ource_aws_networkfirewall_firewall_test.go | 29 +++++++ .../r/networkfirewall_firewall.html.markdown | 7 ++ 4 files changed, 116 insertions(+) diff --git a/aws/internal/service/networkfirewall/waiter/waiter.go b/aws/internal/service/networkfirewall/waiter/waiter.go index 903fb38bcba..716e8d9abeb 100644 --- a/aws/internal/service/networkfirewall/waiter/waiter.go +++ b/aws/internal/service/networkfirewall/waiter/waiter.go @@ -40,6 +40,7 @@ func FirewallUpdated(ctx context.Context, conn *networkfirewall.NetworkFirewall, Target: []string{networkfirewall.FirewallStatusValueReady}, Refresh: FirewallUpdatedStatus(ctx, conn, arn), Timeout: FirewallTimeout, + Delay: 30 * time.Second, } outputRaw, err := stateConf.WaitForState() diff --git a/aws/resource_aws_networkfirewall_firewall.go b/aws/resource_aws_networkfirewall_firewall.go index 2f8c1805623..fa724f846e1 100644 --- a/aws/resource_aws_networkfirewall_firewall.go +++ b/aws/resource_aws_networkfirewall_firewall.go @@ -50,6 +50,42 @@ func resourceAwsNetworkFirewallFirewall() *schema.Resource { Type: schema.TypeBool, Optional: true, }, + "firewall_status": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "sync_states": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "availability_zone": { + Type: schema.TypeString, + Computed: true, + }, + "attachment": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "endpoint_id": { + Type: schema.TypeString, + Computed: true, + }, + "subnet_id": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, "name": { Type: schema.TypeString, Required: true, @@ -162,6 +198,7 @@ func resourceAwsNetworkFirewallFirewallRead(ctx context.Context, d *schema.Resou d.Set("name", firewall.FirewallName) d.Set("firewall_policy_arn", firewall.FirewallPolicyArn) d.Set("firewall_policy_change_protection", firewall.FirewallPolicyChangeProtection) + d.Set("firewall_status", flattenNetworkFirewallFirewallStatus(output.FirewallStatus)) d.Set("subnet_change_protection", firewall.SubnetChangeProtection) d.Set("update_token", output.UpdateToken) d.Set("vpc_id", firewall.VpcId) @@ -380,6 +417,48 @@ func expandNetworkFirewallSubnetMappingIds(l []interface{}) []string { return ids } +func flattenNetworkFirewallFirewallStatus(status *networkfirewall.FirewallStatus) []interface{} { + if status == nil { + return nil + } + + m := map[string]interface{}{ + "sync_states": flattenNetworkFirewallSyncStates(status.SyncStates), + } + + return []interface{}{m} +} + +func flattenNetworkFirewallSyncStates(s map[string]*networkfirewall.SyncState) []interface{} { + if s == nil { + return nil + } + + syncStates := make([]interface{}, 0, len(s)) + for k, v := range s { + m := map[string]interface{}{ + "availability_zone": k, + "attachment": flattenNetworkFirewallSyncStateAttachment(v.Attachment), + } + syncStates = append(syncStates, m) + } + + return syncStates +} + +func flattenNetworkFirewallSyncStateAttachment(a *networkfirewall.Attachment) []interface{} { + if a == nil { + return nil + } + + m := map[string]interface{}{ + "endpoint_id": aws.StringValue(a.EndpointId), + "subnet_id": aws.StringValue(a.SubnetId), + } + + return []interface{}{m} +} + func flattenNetworkFirewallSubnetMappings(sm []*networkfirewall.SubnetMapping) []interface{} { mappings := make([]interface{}, 0, len(sm)) for _, s := range sm { diff --git a/aws/resource_aws_networkfirewall_firewall_test.go b/aws/resource_aws_networkfirewall_firewall_test.go index 9f5366702f8..97db1dd6dc9 100644 --- a/aws/resource_aws_networkfirewall_firewall_test.go +++ b/aws/resource_aws_networkfirewall_firewall_test.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "log" + "regexp" "testing" "github.com/aws/aws-sdk-go/aws" @@ -97,11 +98,19 @@ func TestAccAwsNetworkFirewallFirewall_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "delete_protection", "false"), resource.TestCheckResourceAttr(resourceName, "description", ""), resource.TestCheckResourceAttrPair(resourceName, "firewall_policy_arn", policyResourceName, "arn"), + resource.TestCheckResourceAttr(resourceName, "firewall_status.#", "1"), + resource.TestCheckResourceAttr(resourceName, "firewall_status.0.sync_states.#", "1"), + resource.TestCheckTypeSetElemAttrPair(resourceName, "firewall_status.0.sync_states.*.availability_zone", subnetResourceName, "availability_zone"), + resource.TestMatchTypeSetElemNestedAttrs(resourceName, "firewall_status.0.sync_states.*", map[string]*regexp.Regexp{ + "attachment.0.endpoint_id": regexp.MustCompile(`vpce-`), + }), + resource.TestCheckTypeSetElemAttrPair(resourceName, "firewall_status.0.sync_states.*.attachment.0.subnet_id", subnetResourceName, "id"), resource.TestCheckResourceAttr(resourceName, "name", rName), resource.TestCheckResourceAttrPair(resourceName, "vpc_id", vpcResourceName, "id"), resource.TestCheckResourceAttr(resourceName, "subnet_mapping.#", "1"), tfawsresource.TestCheckTypeSetElemAttrPair(resourceName, "subnet_mapping.*.subnet_id", subnetResourceName, "id"), resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + resource.TestCheckResourceAttrSet(resourceName, "update_token"), ), }, { @@ -216,6 +225,13 @@ func TestAccAwsNetworkFirewallFirewall_subnetMappings_updateSubnet(t *testing.T) Config: testAccNetworkFirewallFirewall_updateSubnet(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAwsNetworkFirewallFirewallExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "firewall_status.#", "1"), + resource.TestCheckResourceAttr(resourceName, "firewall_status.0.sync_states.#", "1"), + resource.TestCheckTypeSetElemAttrPair(resourceName, "firewall_status.0.sync_states.*.availability_zone", updateSubnetResourceName, "availability_zone"), + resource.TestMatchTypeSetElemNestedAttrs(resourceName, "firewall_status.0.sync_states.*", map[string]*regexp.Regexp{ + "attachment.0.endpoint_id": regexp.MustCompile(`vpce-`), + }), + resource.TestCheckTypeSetElemAttrPair(resourceName, "firewall_status.0.sync_states.*.attachment.0.subnet_id", updateSubnetResourceName, "id"), resource.TestCheckResourceAttr(resourceName, "subnet_mapping.#", "1"), tfawsresource.TestCheckTypeSetElemAttrPair(resourceName, "subnet_mapping.*.subnet_id", updateSubnetResourceName, "id"), ), @@ -252,6 +268,12 @@ func TestAccAwsNetworkFirewallFirewall_subnetMappings_updateMultipleSubnets(t *t Config: testAccNetworkFirewallFirewall_updateMultipleSubnets(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAwsNetworkFirewallFirewallExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "firewall_status.#", "1"), + resource.TestCheckResourceAttr(resourceName, "firewall_status.0.sync_states.#", "2"), + resource.TestCheckTypeSetElemAttrPair(resourceName, "firewall_status.0.sync_states.*.availability_zone", subnetResourceName, "availability_zone"), + resource.TestCheckTypeSetElemAttrPair(resourceName, "firewall_status.0.sync_states.*.attachment.0.subnet_id", subnetResourceName, "id"), + resource.TestCheckTypeSetElemAttrPair(resourceName, "firewall_status.0.sync_states.*.availability_zone", updateSubnetResourceName, "availability_zone"), + resource.TestCheckTypeSetElemAttrPair(resourceName, "firewall_status.0.sync_states.*.attachment.0.subnet_id", updateSubnetResourceName, "id"), resource.TestCheckResourceAttr(resourceName, "subnet_mapping.#", "2"), tfawsresource.TestCheckTypeSetElemAttrPair(resourceName, "subnet_mapping.*.subnet_id", subnetResourceName, "id"), tfawsresource.TestCheckTypeSetElemAttrPair(resourceName, "subnet_mapping.*.subnet_id", updateSubnetResourceName, "id"), @@ -261,6 +283,13 @@ func TestAccAwsNetworkFirewallFirewall_subnetMappings_updateMultipleSubnets(t *t Config: testAccNetworkFirewallFirewall_basic(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAwsNetworkFirewallFirewallExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "firewall_status.#", "1"), + resource.TestCheckResourceAttr(resourceName, "firewall_status.0.sync_states.#", "1"), + resource.TestCheckTypeSetElemAttrPair(resourceName, "firewall_status.0.sync_states.*.availability_zone", subnetResourceName, "availability_zone"), + resource.TestMatchTypeSetElemNestedAttrs(resourceName, "firewall_status.0.sync_states.*", map[string]*regexp.Regexp{ + "attachment.0.endpoint_id": regexp.MustCompile(`vpce-`), + }), + resource.TestCheckTypeSetElemAttrPair(resourceName, "firewall_status.0.sync_states.*.attachment.0.subnet_id", subnetResourceName, "id"), resource.TestCheckResourceAttr(resourceName, "subnet_mapping.#", "1"), tfawsresource.TestCheckTypeSetElemAttrPair(resourceName, "subnet_mapping.*.subnet_id", subnetResourceName, "id"), ), diff --git a/website/docs/r/networkfirewall_firewall.html.markdown b/website/docs/r/networkfirewall_firewall.html.markdown index caa5196277a..9e8e7c7ec39 100644 --- a/website/docs/r/networkfirewall_firewall.html.markdown +++ b/website/docs/r/networkfirewall_firewall.html.markdown @@ -64,6 +64,13 @@ In addition to all arguments above, the following attributes are exported: * `arn` - The Amazon Resource Name (ARN) that identifies the firewall. +* `firewall_status` - Nested list of information about the current status of the firewall. + * `sync_states` - List of subnets configured for use by the firewall. + * `attachment` - Nested list describing the attachment status of the firewall's association with a single VPC subnet. + * `endpoint_id` - The identifier of the firewall endpoint that AWS Network Firewall has instantiated in the subnet. You use this to identify the firewall endpoint in the VPC route tables, when you redirect the VPC traffic through the endpoint. + * `subnet_id` - The unique identifier of the subnet that you've specified to be used for a firewall endpoint. + * `availability_zone` - The Availability Zone where the subnet is configured. + * `update_token` - A string token used when updating a firewall. ## Import From 754d2153e06ed33a829900817572c3b164530d04 Mon Sep 17 00:00:00 2001 From: Angie Pinilla Date: Wed, 25 Nov 2020 14:20:33 -0500 Subject: [PATCH 2/2] CR updates --- aws/internal/service/networkfirewall/waiter/waiter.go | 5 ++++- aws/resource_aws_networkfirewall_firewall.go | 9 ++++++++- website/docs/r/networkfirewall_firewall.html.markdown | 2 +- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/aws/internal/service/networkfirewall/waiter/waiter.go b/aws/internal/service/networkfirewall/waiter/waiter.go index 716e8d9abeb..9cc70453e70 100644 --- a/aws/internal/service/networkfirewall/waiter/waiter.go +++ b/aws/internal/service/networkfirewall/waiter/waiter.go @@ -40,7 +40,10 @@ func FirewallUpdated(ctx context.Context, conn *networkfirewall.NetworkFirewall, Target: []string{networkfirewall.FirewallStatusValueReady}, Refresh: FirewallUpdatedStatus(ctx, conn, arn), Timeout: FirewallTimeout, - Delay: 30 * time.Second, + // Delay added to account for Associate/DisassociateSubnet calls that return + // a READY status immediately after the method is called instead of immediately + // returning PROVISIONING + Delay: 30 * time.Second, } outputRaw, err := stateConf.WaitForState() diff --git a/aws/resource_aws_networkfirewall_firewall.go b/aws/resource_aws_networkfirewall_firewall.go index fa724f846e1..b5820d4ab1d 100644 --- a/aws/resource_aws_networkfirewall_firewall.go +++ b/aws/resource_aws_networkfirewall_firewall.go @@ -10,6 +10,7 @@ import ( "github.com/aws/aws-sdk-go/service/networkfirewall" "github.com/hashicorp/aws-sdk-go-base/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/terraform-providers/terraform-provider-aws/aws/internal/hashcode" "github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags" @@ -27,6 +28,12 @@ func resourceAwsNetworkFirewallFirewall() *schema.Resource { StateContext: schema.ImportStatePassthroughContext, }, + CustomizeDiff: customdiff.Sequence( + customdiff.ComputedIf("firewall_status", func(ctx context.Context, diff *schema.ResourceDiff, meta interface{}) bool { + return diff.HasChange("subnet_mapping") + }), + ), + Schema: map[string]*schema.Schema{ "arn": { Type: schema.TypeString, @@ -56,7 +63,7 @@ func resourceAwsNetworkFirewallFirewall() *schema.Resource { Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "sync_states": { - Type: schema.TypeList, + Type: schema.TypeSet, Computed: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ diff --git a/website/docs/r/networkfirewall_firewall.html.markdown b/website/docs/r/networkfirewall_firewall.html.markdown index 9e8e7c7ec39..ca93fdabb73 100644 --- a/website/docs/r/networkfirewall_firewall.html.markdown +++ b/website/docs/r/networkfirewall_firewall.html.markdown @@ -65,7 +65,7 @@ In addition to all arguments above, the following attributes are exported: * `arn` - The Amazon Resource Name (ARN) that identifies the firewall. * `firewall_status` - Nested list of information about the current status of the firewall. - * `sync_states` - List of subnets configured for use by the firewall. + * `sync_states` - Set of subnets configured for use by the firewall. * `attachment` - Nested list describing the attachment status of the firewall's association with a single VPC subnet. * `endpoint_id` - The identifier of the firewall endpoint that AWS Network Firewall has instantiated in the subnet. You use this to identify the firewall endpoint in the VPC route tables, when you redirect the VPC traffic through the endpoint. * `subnet_id` - The unique identifier of the subnet that you've specified to be used for a firewall endpoint.