diff --git a/aws/provider.go b/aws/provider.go index 977e63f7cbe..8d90cebed97 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -783,6 +783,7 @@ func Provider() *schema.Provider { "aws_networkfirewall_firewall": resourceAwsNetworkFirewallFirewall(), "aws_networkfirewall_firewall_policy": resourceAwsNetworkFirewallFirewallPolicy(), "aws_networkfirewall_logging_configuration": resourceAwsNetworkFirewallLoggingConfiguration(), + "aws_networkfirewall_resource_policy": resourceAwsNetworkFirewallResourcePolicy(), "aws_networkfirewall_rule_group": resourceAwsNetworkFirewallRuleGroup(), "aws_opsworks_application": resourceAwsOpsworksApplication(), "aws_opsworks_stack": resourceAwsOpsworksStack(), diff --git a/aws/resource_aws_networkfirewall_resource_policy.go b/aws/resource_aws_networkfirewall_resource_policy.go new file mode 100644 index 00000000000..288c9026f8c --- /dev/null +++ b/aws/resource_aws_networkfirewall_resource_policy.go @@ -0,0 +1,110 @@ +package aws + +import ( + "context" + "fmt" + "log" + + "github.com/aws/aws-sdk-go/aws" + "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/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/networkfirewall/finder" +) + +func resourceAwsNetworkFirewallResourcePolicy() *schema.Resource { + return &schema.Resource{ + CreateContext: resourceAwsNetworkFirewallResourcePolicyPut, + ReadContext: resourceAwsNetworkFirewallResourcePolicyRead, + UpdateContext: resourceAwsNetworkFirewallResourcePolicyPut, + DeleteContext: resourceAwsNetworkFirewallResourcePolicyDelete, + + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + + Schema: map[string]*schema.Schema{ + "policy": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringIsJSON, + DiffSuppressFunc: suppressEquivalentJsonDiffs, + }, + "resource_arn": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validateArn, + }, + }, + } +} + +func resourceAwsNetworkFirewallResourcePolicyPut(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*AWSClient).networkfirewallconn + resourceArn := d.Get("resource_arn").(string) + input := &networkfirewall.PutResourcePolicyInput{ + ResourceArn: aws.String(resourceArn), + Policy: aws.String(d.Get("policy").(string)), + } + + log.Printf("[DEBUG] Putting NetworkFirewall Resource Policy for resource: %s", resourceArn) + + _, err := conn.PutResourcePolicyWithContext(ctx, input) + if err != nil { + return diag.FromErr(fmt.Errorf("error putting NetworkFirewall Resource Policy (for resource: %s): %w", resourceArn, err)) + } + + d.SetId(resourceArn) + + return resourceAwsNetworkFirewallResourcePolicyRead(ctx, d, meta) +} + +func resourceAwsNetworkFirewallResourcePolicyRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*AWSClient).networkfirewallconn + resourceArn := d.Id() + + log.Printf("[DEBUG] Reading NetworkFirewall Resource Policy for resource: %s", resourceArn) + + policy, err := finder.ResourcePolicy(ctx, conn, resourceArn) + if !d.IsNewResource() && tfawserr.ErrCodeEquals(err, networkfirewall.ErrCodeResourceNotFoundException) { + log.Printf("[WARN] NetworkFirewall Resource Policy (for resource: %s) not found, removing from state", resourceArn) + d.SetId("") + return nil + } + if err != nil { + return diag.FromErr(fmt.Errorf("error reading NetworkFirewall Resource Policy (for resource: %s): %w", resourceArn, err)) + } + + if policy == nil { + return diag.FromErr(fmt.Errorf("error reading NetworkFirewall Resource Policy (for resource: %s): empty output", resourceArn)) + } + + d.Set("policy", policy) + d.Set("resource_arn", resourceArn) + + return nil +} + +func resourceAwsNetworkFirewallResourcePolicyDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*AWSClient).networkfirewallconn + + log.Printf("[DEBUG] Deleting NetworkFirewall Resource Policy for resource: %s", d.Id()) + + input := &networkfirewall.DeleteResourcePolicyInput{ + ResourceArn: aws.String(d.Id()), + } + + _, err := conn.DeleteResourcePolicyWithContext(ctx, input) + + if err != nil { + if tfawserr.ErrCodeEquals(err, networkfirewall.ErrCodeResourceNotFoundException) { + return nil + } + return diag.FromErr(fmt.Errorf("error deleting NetworkFirewall Resource Policy (for resource: %s): %w", d.Id(), err)) + } + + return nil +} diff --git a/aws/resource_aws_networkfirewall_resource_policy_test.go b/aws/resource_aws_networkfirewall_resource_policy_test.go new file mode 100644 index 00000000000..2ba8e477d4f --- /dev/null +++ b/aws/resource_aws_networkfirewall_resource_policy_test.go @@ -0,0 +1,311 @@ +package aws + +import ( + "context" + "fmt" + "regexp" + "testing" + + "github.com/aws/aws-sdk-go/service/networkfirewall" + "github.com/hashicorp/aws-sdk-go-base/tfawserr" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/networkfirewall/finder" +) + +func TestAccAwsNetworkFirewallResourcePolicy_firewallPolicy(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_networkfirewall_resource_policy.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsNetworkFirewallResourcePolicyDestroy, + Steps: []resource.TestStep{ + { + Config: testAccNetworkFirewallResourcePolicy_firewallPolicy(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsNetworkFirewallResourcePolicyExists(resourceName), + resource.TestCheckResourceAttrPair(resourceName, "resource_arn", "aws_networkfirewall_firewall_policy.test", "arn"), + resource.TestMatchResourceAttr(resourceName, "policy", regexp.MustCompile(`"Action":"network-firewall:ListFirewallPolicies"`)), + ), + }, + { + Config: testAccNetworkFirewallResourcePolicy_firewallPolicy_updatePolicy(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsNetworkFirewallResourcePolicyExists(resourceName), + resource.TestMatchResourceAttr(resourceName, "policy", regexp.MustCompile(`"Action":\["network-firewall:ListFirewallPolicies","network-firewall:AssociateFirewallPolicy"\]`)), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAwsNetworkFirewallResourcePolicy_ruleGroup(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_networkfirewall_resource_policy.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsNetworkFirewallResourcePolicyDestroy, + Steps: []resource.TestStep{ + { + Config: testAccNetworkFirewallResourcePolicy_ruleGroup(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsNetworkFirewallResourcePolicyExists(resourceName), + resource.TestCheckResourceAttrPair(resourceName, "resource_arn", "aws_networkfirewall_rule_group.test", "arn"), + resource.TestMatchResourceAttr(resourceName, "policy", regexp.MustCompile(`"Action":"network-firewall:ListRuleGroups"`)), + ), + }, + { + Config: testAccNetworkFirewallResourcePolicy_ruleGroup_updatePolicy(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsNetworkFirewallResourcePolicyExists(resourceName), + resource.TestMatchResourceAttr(resourceName, "policy", regexp.MustCompile(`"Action":\["network-firewall:ListRuleGroups","network-firewall:CreateFirewallPolicy"\]`)), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAwsNetworkFirewallResourcePolicy_disappears(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_networkfirewall_resource_policy.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsNetworkFirewallResourcePolicyDestroy, + Steps: []resource.TestStep{ + { + Config: testAccNetworkFirewallResourcePolicy_firewallPolicy(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsNetworkFirewallResourcePolicyExists(resourceName), + testAccCheckResourceDisappears(testAccProvider, resourceAwsNetworkFirewallResourcePolicy(), resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func TestAccAwsNetworkFirewallResourcePolicy_firewallPolicy_disappears(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_networkfirewall_resource_policy.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsNetworkFirewallResourcePolicyDestroy, + Steps: []resource.TestStep{ + { + Config: testAccNetworkFirewallResourcePolicy_firewallPolicy(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsNetworkFirewallResourcePolicyExists(resourceName), + testAccCheckResourceDisappears(testAccProvider, resourceAwsNetworkFirewallFirewallPolicy(), "aws_networkfirewall_firewall_policy.test"), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func TestAccAwsNetworkFirewallResourcePolicy_ruleGroup_disappears(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_networkfirewall_resource_policy.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsNetworkFirewallResourcePolicyDestroy, + Steps: []resource.TestStep{ + { + Config: testAccNetworkFirewallResourcePolicy_ruleGroup(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsNetworkFirewallResourcePolicyExists(resourceName), + testAccCheckResourceDisappears(testAccProvider, resourceAwsNetworkFirewallRuleGroup(), "aws_networkfirewall_rule_group.test"), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func testAccCheckAwsNetworkFirewallResourcePolicyDestroy(s *terraform.State) error { + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_networkfirewall_resource_policy" { + continue + } + + conn := testAccProvider.Meta().(*AWSClient).networkfirewallconn + policy, err := finder.ResourcePolicy(context.Background(), conn, rs.Primary.ID) + if tfawserr.ErrCodeEquals(err, networkfirewall.ErrCodeResourceNotFoundException) { + continue + } + if err != nil { + return err + } + if policy != nil { + return fmt.Errorf("NetworkFirewall Resource Policy (for resource: %s) still exists", rs.Primary.ID) + } + } + + return nil +} + +func testAccCheckAwsNetworkFirewallResourcePolicyExists(n string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No NetworkFirewall Resource Policy ID is set") + } + + conn := testAccProvider.Meta().(*AWSClient).networkfirewallconn + policy, err := finder.ResourcePolicy(context.Background(), conn, rs.Primary.ID) + if err != nil { + return err + } + + if policy == nil { + return fmt.Errorf("NetworkFirewall Resource Policy (for resource: %s) not found", rs.Primary.ID) + } + + return nil + + } +} + +func testAccNetworkFirewallResourcePolicyFirewallPolicyBaseConfig(rName string) string { + return fmt.Sprintf(` +data "aws_partition" "current" {} + +data "aws_caller_identity" "current" {} + +resource "aws_networkfirewall_firewall_policy" "test" { + name = %q + firewall_policy { + stateless_fragment_default_actions = ["aws:drop"] + stateless_default_actions = ["aws:pass"] + } +} +`, rName) +} + +func testAccNetworkFirewallResourcePolicy_firewallPolicy(rName string) string { + return composeConfig( + testAccNetworkFirewallResourcePolicyFirewallPolicyBaseConfig(rName), ` +resource "aws_networkfirewall_resource_policy" "test" { + resource_arn = aws_networkfirewall_firewall_policy.test.arn + policy = jsonencode({ + Statement = [{ + Action = "network-firewall:ListFirewallPolicies" + Effect = "Allow" + Resource = aws_networkfirewall_firewall_policy.test.arn + Principal = { + AWS = "arn:${data.aws_partition.current.partition}:iam::${data.aws_caller_identity.current.account_id}:root" + } + }] + Version = "2012-10-17" + }) +} +`) +} + +func testAccNetworkFirewallResourcePolicy_firewallPolicy_updatePolicy(rName string) string { + return composeConfig( + testAccNetworkFirewallResourcePolicyFirewallPolicyBaseConfig(rName), ` +resource "aws_networkfirewall_resource_policy" "test" { + resource_arn = aws_networkfirewall_firewall_policy.test.arn + policy = jsonencode({ + Statement = [{ + Action = ["network-firewall:ListFirewallPolicies", "network-firewall:AssociateFirewallPolicy"] + Effect = "Allow" + Resource = aws_networkfirewall_firewall_policy.test.arn + Principal = { + AWS = "arn:${data.aws_partition.current.partition}:iam::${data.aws_caller_identity.current.account_id}:root" + } + }] + Version = "2012-10-17" + }) +} +`) +} + +func testAccNetworkFirewallResourcePolicyRuleGroupBaseConfig(rName string) string { + return fmt.Sprintf(` +data "aws_partition" "current" {} + +data "aws_caller_identity" "current" {} + +resource "aws_networkfirewall_rule_group" "test" { + capacity = 100 + name = %q + type = "STATEFUL" + rule_group { + rules_source { + rules_source_list { + generated_rules_type = "ALLOWLIST" + target_types = ["HTTP_HOST"] + targets = ["test.example.com"] + } + } + } +} +`, rName) +} + +func testAccNetworkFirewallResourcePolicy_ruleGroup(rName string) string { + return composeConfig( + testAccNetworkFirewallResourcePolicyRuleGroupBaseConfig(rName), ` +resource "aws_networkfirewall_resource_policy" "test" { + resource_arn = aws_networkfirewall_rule_group.test.arn + policy = jsonencode({ + Statement = [{ + Action = "network-firewall:ListRuleGroups" + Effect = "Allow" + Resource = aws_networkfirewall_rule_group.test.arn + Principal = { + AWS = "arn:${data.aws_partition.current.partition}:iam::${data.aws_caller_identity.current.account_id}:root" + } + }] + Version = "2012-10-17" + }) +} +`) +} + +func testAccNetworkFirewallResourcePolicy_ruleGroup_updatePolicy(rName string) string { + return composeConfig( + testAccNetworkFirewallResourcePolicyRuleGroupBaseConfig(rName), ` +resource "aws_networkfirewall_resource_policy" "test" { + resource_arn = aws_networkfirewall_rule_group.test.arn + policy = jsonencode({ + Statement = [{ + Action = ["network-firewall:ListRuleGroups", "network-firewall:CreateFirewallPolicy"] + Effect = "Allow" + Resource = aws_networkfirewall_rule_group.test.arn + Principal = { + AWS = "arn:${data.aws_partition.current.partition}:iam::${data.aws_caller_identity.current.account_id}:root" + } + }] + Version = "2012-10-17" + }) +} +`) +} diff --git a/website/docs/r/networkfirewall_resource_policy.html.markdown b/website/docs/r/networkfirewall_resource_policy.html.markdown new file mode 100644 index 00000000000..a456f502267 --- /dev/null +++ b/website/docs/r/networkfirewall_resource_policy.html.markdown @@ -0,0 +1,73 @@ +--- +subcategory: "Network Firewall" +layout: "aws" +page_title: "AWS: aws_networkfirewall_resource_policy" +description: |- + Provides an AWS Network Firewall Resource Policy resource. +--- + +# Resource: aws_networkfirewall_resource_policy + +Provides an AWS Network Firewall Resource Policy Resource for a rule group or firewall policy. + +## Example Usage + +### For a Firewall Policy resource + +```hcl +resource "aws_networkfirewall_resource_policy" "example" { + resource_arn = aws_networkfirewall_firewall_policy.example.arn + policy = jsonencode({ + Statement = [{ + Action = "network-firewall:ListFirewallPolicies" + Effect = "Allow" + Resource = aws_networkfirewall_firewall_policy.example.arn + Principal = { + AWS = "arn:aws:iam::123456789012:root" + } + }] + Version = "2012-10-17" + }) +} +``` + +### For a Rule Group resource + +```hcl +resource "aws_networkfirewall_resource_policy" "example" { + resource_arn = aws_networkfirewall_rule_group.example.arn + policy = jsonencode({ + Statement = [{ + Action = "network-firewall:ListRuleGroups" + Effect = "Allow" + Resource = aws_networkfirewall_rule_group.example.arn + Principal = { + AWS = "arn:aws:iam::123456789012:root" + } + }] + Version = "2012-10-17" + }) +} +``` + +## Argument Reference + +The following arguments are supported: + +* `policy` - (Required) JSON formatted policy document that controls access to the Network Firewall resource. The policy must be provided **without whitespaces**. It is recommended to use [jsonencode](https://www.terraform.io/docs/configuration/functions/jsonencode.html) for formatting as seen in the examples above. For more details, including available policy statement Actions, see the [Policy](https://docs.aws.amazon.com/network-firewall/latest/APIReference/API_PutResourcePolicy.html#API_PutResourcePolicy_RequestSyntax) parameter in the AWS API documentation. + +* `resource_arn` - (Required, Forces new resource) The Amazon Resource Name (ARN) of the rule group or firewall policy. + +## Attribute Reference + +In addition to all arguments above, the following attribute is exported: + +* `id` - The Amazon Resource Name (ARN) of the rule group or firewall policy associated with the resource policy. + +## Import + +Network Firewall Resource Policies can be imported using the `resource_arn` e.g. + +``` +$ terraform import aws_networkfirewall_resource_policy.example aws_networkfirewall_rule_group.example arn:aws:network-firewall:us-west-1:123456789012:stateful-rulegroup/example +```