diff --git a/.changelog/18712.txt b/.changelog/18712.txt new file mode 100644 index 00000000000..99d6b7d7de7 --- /dev/null +++ b/.changelog/18712.txt @@ -0,0 +1,4 @@ +```release-note:new-resource +aws_route53_resolver_firewall_rule +``` + diff --git a/aws/internal/service/route53resolver/finder/finder.go b/aws/internal/service/route53resolver/finder/finder.go index 892ad88eea6..82a94981889 100644 --- a/aws/internal/service/route53resolver/finder/finder.go +++ b/aws/internal/service/route53resolver/finder/finder.go @@ -3,6 +3,7 @@ package finder import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/route53resolver" + tfroute53resolver "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/route53resolver" ) // ResolverQueryLogConfigAssociationByID returns the query logging configuration association corresponding to the specified ID. @@ -114,3 +115,44 @@ func FirewallDomainListByID(conn *route53resolver.Route53Resolver, firewallDomai return output.FirewallDomainList, nil } + +// FirewallRuleByID returns the DNS Firewall rule corresponding to the specified rule group and domain list IDs. +// Returns nil if no DNS Firewall rule is found. +func FirewallRuleByID(conn *route53resolver.Route53Resolver, firewallRuleId string) (*route53resolver.FirewallRule, error) { + firewallRuleGroupId, firewallDomainListId, err := tfroute53resolver.FirewallRuleParseID(firewallRuleId) + + if err != nil { + return nil, err + } + + var rule *route53resolver.FirewallRule + + input := &route53resolver.ListFirewallRulesInput{ + FirewallRuleGroupId: aws.String(firewallRuleGroupId), + } + + err = conn.ListFirewallRulesPages(input, func(page *route53resolver.ListFirewallRulesOutput, lastPage bool) bool { + if page == nil { + return !lastPage + } + + for _, r := range page.FirewallRules { + if aws.StringValue(r.FirewallDomainListId) == firewallDomainListId { + rule = r + return false + } + } + + return !lastPage + }) + + if err != nil { + return nil, err + } + + if rule == nil { + return nil, nil + } + + return rule, nil +} diff --git a/aws/internal/service/route53resolver/id.go b/aws/internal/service/route53resolver/id.go new file mode 100644 index 00000000000..41da738ab64 --- /dev/null +++ b/aws/internal/service/route53resolver/id.go @@ -0,0 +1,25 @@ +package route53resolver + +import ( + "fmt" + "strings" +) + +const ruleIdSeparator = ":" + +func FirewallRuleCreateID(firewallRuleGroupId, firewallDomainListId string) string { + parts := []string{firewallRuleGroupId, firewallDomainListId} + id := strings.Join(parts, ruleIdSeparator) + + return id +} + +func FirewallRuleParseID(id string) (string, string, error) { + parts := strings.SplitN(id, ruleIdSeparator, 2) + + if len(parts) < 2 || parts[0] == "" || parts[1] == "" { + return "", "", fmt.Errorf("unexpected format of ID (%s), expected firewall_rule_group_id%sfirewall_domain_list_id", id, ruleIdSeparator) + } + + return parts[0], parts[1], nil +} diff --git a/aws/provider.go b/aws/provider.go index 9c48d48ddf1..d52ba0b0450 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -922,6 +922,7 @@ func Provider() *schema.Provider { "aws_route53_resolver_dnssec_config": resourceAwsRoute53ResolverDnssecConfig(), "aws_route53_resolver_endpoint": resourceAwsRoute53ResolverEndpoint(), "aws_route53_resolver_firewall_domain_list": resourceAwsRoute53ResolverFirewallDomainList(), + "aws_route53_resolver_firewall_rule": resourceAwsRoute53ResolverFirewallRule(), "aws_route53_resolver_firewall_rule_group": resourceAwsRoute53ResolverFirewallRuleGroup(), "aws_route53_resolver_query_log_config": resourceAwsRoute53ResolverQueryLogConfig(), "aws_route53_resolver_query_log_config_association": resourceAwsRoute53ResolverQueryLogConfigAssociation(), diff --git a/aws/resource_aws_route53_resolver_firewall_domain_list_test.go b/aws/resource_aws_route53_resolver_firewall_domain_list_test.go index 1b7f0549ebb..f0897972006 100644 --- a/aws/resource_aws_route53_resolver_firewall_domain_list_test.go +++ b/aws/resource_aws_route53_resolver_firewall_domain_list_test.go @@ -18,6 +18,9 @@ func init() { resource.AddTestSweepers("aws_route53_resolver_firewall_domain_list", &resource.Sweeper{ Name: "aws_route53_resolver_firewall_domain_list", F: testSweepRoute53ResolverFirewallDomainLists, + Dependencies: []string{ + "aws_route53_resolver_firewall_rule", + }, }) } diff --git a/aws/resource_aws_route53_resolver_firewall_rule.go b/aws/resource_aws_route53_resolver_firewall_rule.go new file mode 100644 index 00000000000..c2ab8108d3e --- /dev/null +++ b/aws/resource_aws_route53_resolver_firewall_rule.go @@ -0,0 +1,213 @@ +package aws + +import ( + "fmt" + "log" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/route53resolver" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + tfroute53resolver "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/route53resolver" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/route53resolver/finder" +) + +func resourceAwsRoute53ResolverFirewallRule() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsRoute53ResolverFirewallRuleCreate, + Read: resourceAwsRoute53ResolverFirewallRuleRead, + Update: resourceAwsRoute53ResolverFirewallRuleUpdate, + Delete: resourceAwsRoute53ResolverFirewallRuleDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validateRoute53ResolverName, + }, + + "action": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice(route53resolver.Action_Values(), false), + }, + + "block_override_dns_type": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice(route53resolver.BlockOverrideDnsType_Values(), false), + }, + + "block_override_domain": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringLenBetween(1, 255), + }, + + "block_override_ttl": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(0, 604800), + }, + + "block_response": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice(route53resolver.BlockResponse_Values(), false), + }, + + "firewall_domain_list_id": { + Type: schema.TypeString, + ForceNew: true, + Required: true, + ValidateFunc: validation.StringLenBetween(1, 64), + }, + + "firewall_rule_group_id": { + Type: schema.TypeString, + ForceNew: true, + Required: true, + ValidateFunc: validation.StringLenBetween(1, 64), + }, + + "priority": { + Type: schema.TypeInt, + Required: true, + }, + }, + } +} + +func resourceAwsRoute53ResolverFirewallRuleCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).route53resolverconn + + firewallRuleGroupId := d.Get("firewall_rule_group_id").(string) + firewallDomainListId := d.Get("firewall_domain_list_id").(string) + input := &route53resolver.CreateFirewallRuleInput{ + CreatorRequestId: aws.String(resource.PrefixedUniqueId("tf-r53-resolver-firewall-rule-")), + Name: aws.String(d.Get("name").(string)), + Action: aws.String(d.Get("action").(string)), + FirewallRuleGroupId: aws.String(firewallRuleGroupId), + FirewallDomainListId: aws.String(firewallDomainListId), + Priority: aws.Int64(int64(d.Get("priority").(int))), + } + + if v, ok := d.GetOk("block_override_dns_type"); ok { + input.BlockOverrideDnsType = aws.String(v.(string)) + } + + if v, ok := d.GetOk("block_override_domain"); ok { + input.BlockOverrideDomain = aws.String(v.(string)) + } + + if v, ok := d.GetOk("block_override_ttl"); ok { + input.BlockOverrideTtl = aws.Int64(int64(v.(int))) + } + + if v, ok := d.GetOk("block_response"); ok { + input.BlockResponse = aws.String(v.(string)) + } + + log.Printf("[DEBUG] Creating Route 53 Resolver DNS Firewall rule: %#v", input) + _, err := conn.CreateFirewallRule(input) + if err != nil { + return fmt.Errorf("error creating Route 53 Resolver DNS Firewall rule: %w", err) + } + + d.SetId(tfroute53resolver.FirewallRuleCreateID(firewallRuleGroupId, firewallDomainListId)) + + return resourceAwsRoute53ResolverFirewallRuleRead(d, meta) +} + +func resourceAwsRoute53ResolverFirewallRuleRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).route53resolverconn + + rule, err := finder.FirewallRuleByID(conn, d.Id()) + + if isAWSErr(err, route53resolver.ErrCodeResourceNotFoundException, "") { + log.Printf("[WARN] Route53 Resolver DNS Firewall rule (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + + if err != nil { + return fmt.Errorf("error getting Route 53 Resolver DNS Firewall rule (%s): %w", d.Id(), err) + } + + if rule == nil { + log.Printf("[WARN] Route 53 Resolver DNS Firewall rule (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + + d.Set("name", rule.Name) + d.Set("action", rule.Action) + d.Set("block_override_dns_type", rule.BlockOverrideDnsType) + d.Set("block_override_domain", rule.BlockOverrideDomain) + d.Set("block_override_ttl", rule.BlockOverrideTtl) + d.Set("block_response", rule.BlockResponse) + d.Set("firewall_rule_group_id", rule.FirewallRuleGroupId) + d.Set("firewall_domain_list_id", rule.FirewallDomainListId) + d.Set("priority", rule.Priority) + + return nil +} + +func resourceAwsRoute53ResolverFirewallRuleUpdate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).route53resolverconn + + input := &route53resolver.UpdateFirewallRuleInput{ + Name: aws.String(d.Get("name").(string)), + Action: aws.String(d.Get("action").(string)), + FirewallRuleGroupId: aws.String(d.Get("firewall_rule_group_id").(string)), + FirewallDomainListId: aws.String(d.Get("firewall_domain_list_id").(string)), + Priority: aws.Int64(int64(d.Get("priority").(int))), + } + + if v, ok := d.GetOk("block_override_dns_type"); ok { + input.BlockOverrideDnsType = aws.String(v.(string)) + } + + if v, ok := d.GetOk("block_override_domain"); ok { + input.BlockOverrideDomain = aws.String(v.(string)) + } + + if v, ok := d.GetOk("block_override_ttl"); ok { + input.BlockOverrideTtl = aws.Int64(int64(v.(int))) + } + + if v, ok := d.GetOk("block_response"); ok { + input.BlockResponse = aws.String(v.(string)) + } + + log.Printf("[DEBUG] Updating Route 53 Resolver DNS Firewall rule: %#v", input) + _, err := conn.UpdateFirewallRule(input) + if err != nil { + return fmt.Errorf("error updating Route 53 Resolver DNS Firewall rule (%s): %w", d.Id(), err) + } + + return resourceAwsRoute53ResolverFirewallRuleRead(d, meta) +} + +func resourceAwsRoute53ResolverFirewallRuleDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).route53resolverconn + + _, err := conn.DeleteFirewallRule(&route53resolver.DeleteFirewallRuleInput{ + FirewallRuleGroupId: aws.String(d.Get("firewall_rule_group_id").(string)), + FirewallDomainListId: aws.String(d.Get("firewall_domain_list_id").(string)), + }) + + if isAWSErr(err, route53resolver.ErrCodeResourceNotFoundException, "") { + return nil + } + + if err != nil { + return fmt.Errorf("error deleting Route 53 Resolver DNS Firewall rule (%s): %w", d.Id(), err) + } + + return nil +} diff --git a/aws/resource_aws_route53_resolver_firewall_rule_group_test.go b/aws/resource_aws_route53_resolver_firewall_rule_group_test.go index e12f282f011..7092d0d8bd1 100644 --- a/aws/resource_aws_route53_resolver_firewall_rule_group_test.go +++ b/aws/resource_aws_route53_resolver_firewall_rule_group_test.go @@ -19,6 +19,7 @@ func init() { Name: "aws_route53_resolver_firewall_rule_group", F: testSweepRoute53ResolverFirewallRuleGroups, Dependencies: []string{ + "aws_route53_resolver_firewall_rule", "aws_route53_resolver_firewall_rule_group_association", }, }) diff --git a/aws/resource_aws_route53_resolver_firewall_rule_test.go b/aws/resource_aws_route53_resolver_firewall_rule_test.go new file mode 100644 index 00000000000..3255ba01cba --- /dev/null +++ b/aws/resource_aws_route53_resolver_firewall_rule_test.go @@ -0,0 +1,293 @@ +package aws + +import ( + "fmt" + "log" + "testing" + + "github.com/aws/aws-sdk-go/service/route53resolver" + "github.com/hashicorp/go-multierror" + "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" + tfroute53resolver "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/route53resolver" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/route53resolver/finder" +) + +func init() { + resource.AddTestSweepers("aws_route53_resolver_firewall_rule", &resource.Sweeper{ + Name: "aws_route53_resolver_firewall_rule", + F: testSweepRoute53ResolverFirewallRules, + Dependencies: []string{ + "aws_route53_resolver_firewall_rule_association", + }, + }) +} + +func testSweepRoute53ResolverFirewallRules(region string) error { + client, err := sharedClientForRegion(region) + if err != nil { + return fmt.Errorf("error getting client: %s", err) + } + conn := client.(*AWSClient).route53resolverconn + var sweeperErrs *multierror.Error + + err = conn.ListFirewallRulesPages(&route53resolver.ListFirewallRulesInput{}, func(page *route53resolver.ListFirewallRulesOutput, lastPage bool) bool { + if page == nil { + return !lastPage + } + + for _, firewallRule := range page.FirewallRules { + id := tfroute53resolver.FirewallRuleCreateID(*firewallRule.FirewallRuleGroupId, *firewallRule.FirewallDomainListId) + + log.Printf("[INFO] Deleting Route53 Resolver DNS Firewall rule: %s", id) + r := resourceAwsRoute53ResolverFirewallRule() + d := r.Data(nil) + d.SetId(id) + err := r.Delete(d, client) + + if err != nil { + log.Printf("[ERROR] %s", err) + sweeperErrs = multierror.Append(sweeperErrs, err) + continue + } + } + + return !lastPage + }) + if testSweepSkipSweepError(err) { + log.Printf("[WARN] Skipping Route53 Resolver DNS Firewall rules sweep for %s: %s", region, err) + return sweeperErrs.ErrorOrNil() // In case we have completed some pages, but had errors + } + if err != nil { + sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error retrieving Route53 Resolver DNS Firewall rules: %w", err)) + } + + return sweeperErrs.ErrorOrNil() +} + +func TestAccAWSRoute53ResolverFirewallRule_basic(t *testing.T) { + var v route53resolver.FirewallRule + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_route53_resolver_firewall_rule.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSRoute53Resolver(t) }, + ErrorCheck: testAccErrorCheck(t, route53resolver.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckRoute53ResolverFirewallRuleDestroy, + Steps: []resource.TestStep{ + { + Config: testAccRoute53ResolverFirewallRuleConfig(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckRoute53ResolverFirewallRuleExists(resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "action", "ALLOW"), + resource.TestCheckResourceAttrPair(resourceName, "firewall_rule_group_id", "aws_route53_resolver_firewall_rule_group.test", "id"), + resource.TestCheckResourceAttrPair(resourceName, "firewall_domain_list_id", "aws_route53_resolver_firewall_domain_list.test", "id"), + resource.TestCheckResourceAttr(resourceName, "priority", "100"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAWSRoute53ResolverFirewallRule_block(t *testing.T) { + var v route53resolver.FirewallRule + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_route53_resolver_firewall_rule.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSRoute53Resolver(t) }, + ErrorCheck: testAccErrorCheck(t, route53resolver.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckRoute53ResolverFirewallRuleDestroy, + Steps: []resource.TestStep{ + { + Config: testAccRoute53ResolverFirewallRuleConfig_block(rName, "NODATA"), + Check: resource.ComposeTestCheckFunc( + testAccCheckRoute53ResolverFirewallRuleExists(resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "action", "BLOCK"), + resource.TestCheckResourceAttr(resourceName, "block_response", "NODATA"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAWSRoute53ResolverFirewallRule_blockOverride(t *testing.T) { + var v route53resolver.FirewallRule + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_route53_resolver_firewall_rule.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSRoute53Resolver(t) }, + ErrorCheck: testAccErrorCheck(t, route53resolver.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckRoute53ResolverFirewallRuleDestroy, + Steps: []resource.TestStep{ + { + Config: testAccRoute53ResolverFirewallRuleConfig_blockOverride(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckRoute53ResolverFirewallRuleExists(resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "action", "BLOCK"), + resource.TestCheckResourceAttr(resourceName, "block_override_dns_type", "CNAME"), + resource.TestCheckResourceAttr(resourceName, "block_override_domain", "example.com."), + resource.TestCheckResourceAttr(resourceName, "block_override_ttl", "60"), + resource.TestCheckResourceAttr(resourceName, "block_response", "OVERRIDE"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAWSRoute53ResolverFirewallRule_disappears(t *testing.T) { + var v route53resolver.FirewallRule + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_route53_resolver_firewall_rule.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSRoute53Resolver(t) }, + ErrorCheck: testAccErrorCheck(t, route53resolver.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckRoute53ResolverFirewallRuleDestroy, + Steps: []resource.TestStep{ + { + Config: testAccRoute53ResolverFirewallRuleConfig(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckRoute53ResolverFirewallRuleExists(resourceName, &v), + testAccCheckResourceDisappears(testAccProvider, resourceAwsRoute53ResolverFirewallRule(), resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func testAccCheckRoute53ResolverFirewallRuleDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).route53resolverconn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_route53_resolver_firewall_rule" { + continue + } + + // Try to find the resource + _, err := finder.FirewallRuleByID(conn, rs.Primary.ID) + // Verify the error is what we want + if isAWSErr(err, route53resolver.ErrCodeResourceNotFoundException, "") { + continue + } + if err != nil { + return err + } + return fmt.Errorf("Route 53 Resolver DNS Firewall rule still exists: %s", rs.Primary.ID) + } + + return nil +} + +func testAccCheckRoute53ResolverFirewallRuleExists(n string, v *route53resolver.FirewallRule) 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 Route 53 Resolver DNS Firewall rule ID is set") + } + + conn := testAccProvider.Meta().(*AWSClient).route53resolverconn + out, err := finder.FirewallRuleByID(conn, rs.Primary.ID) + if err != nil { + return err + } + + *v = *out + + return nil + } +} + +func testAccRoute53ResolverFirewallRuleConfig(rName string) string { + return fmt.Sprintf(` +resource "aws_route53_resolver_firewall_rule_group" "test" { + name = %[1]q +} + +resource "aws_route53_resolver_firewall_domain_list" "test" { + name = %[1]q +} + +resource "aws_route53_resolver_firewall_rule" "test" { + name = %[1]q + action = "ALLOW" + firewall_rule_group_id = aws_route53_resolver_firewall_rule_group.test.id + firewall_domain_list_id = aws_route53_resolver_firewall_domain_list.test.id + priority = 100 +} +`, rName) +} + +func testAccRoute53ResolverFirewallRuleConfig_block(rName, blockResponse string) string { + return fmt.Sprintf(` +resource "aws_route53_resolver_firewall_rule_group" "test" { + name = %[1]q +} + +resource "aws_route53_resolver_firewall_domain_list" "test" { + name = %[1]q +} + +resource "aws_route53_resolver_firewall_rule" "test" { + name = %[1]q + action = "BLOCK" + block_response = %[2]q + firewall_rule_group_id = aws_route53_resolver_firewall_rule_group.test.id + firewall_domain_list_id = aws_route53_resolver_firewall_domain_list.test.id + priority = 100 +} +`, rName, blockResponse) +} + +func testAccRoute53ResolverFirewallRuleConfig_blockOverride(rName string) string { + return fmt.Sprintf(` +resource "aws_route53_resolver_firewall_rule_group" "test" { + name = %[1]q +} + +resource "aws_route53_resolver_firewall_domain_list" "test" { + name = %[1]q +} + +resource "aws_route53_resolver_firewall_rule" "test" { + name = %[1]q + action = "BLOCK" + block_override_dns_type = "CNAME" + block_override_domain = "example.com." + block_override_ttl = 60 + block_response = "OVERRIDE" + firewall_rule_group_id = aws_route53_resolver_firewall_rule_group.test.id + firewall_domain_list_id = aws_route53_resolver_firewall_domain_list.test.id + priority = 100 +} +`, rName) +} diff --git a/website/docs/r/route53_resolver_firewall_rule.markdown b/website/docs/r/route53_resolver_firewall_rule.markdown new file mode 100644 index 00000000000..09befa2a96f --- /dev/null +++ b/website/docs/r/route53_resolver_firewall_rule.markdown @@ -0,0 +1,66 @@ +--- +subcategory: "Route53 Resolver" +layout: "aws" +page_title: "AWS: aws_route53_resolver_firewall_rule" +description: |- + Provides a Route 53 Resolver DNS Firewall rule resource. +--- + +# Resource: aws_route53_resolver_firewall_rule + +Provides a Route 53 Resolver DNS Firewall rule resource. + +## Example Usage + +```terraform +resource "aws_route53_resolver_firewall_domain_list" "example" { + name = "example" + domains = ["example.com"] + tags = {} +} + +resource "aws_route53_resolver_firewall_rule_group" "example" { + name = "example" + tags = {} +} + +resource "aws_route53_resolver_firewall_rule" "example" { + name = "example" + action = "BLOCK" + block_override_dns_type = "CNAME" + block_override_domain = "example.com" + block_override_ttl = 1 + block_response = "OVERRIDE" + firewall_domain_list_id = aws_route53_resolver_firewall_domain_list.example.id + firewall_rule_group_id = aws_route53_resolver_firewall_rule_group.example.id + priority = 100 +} +``` + +## Argument Reference + +The following argument is supported: + +* `name` - (Required) A name that lets you identify the rule, to manage and use it. +* `action` - (Required) The action that DNS Firewall should take on a DNS query when it matches one of the domains in the rule's domain list. Valid values: `ALLOW`, `BLOCK`, `ALERT`. +* `block_override_dns_type` - (Required if `block_response` is `OVERRIDE`) The DNS record's type. This determines the format of the record value that you provided in BlockOverrideDomain. Value values: `CNAME`. +* `block_override_domain` - (Required if `block_response` is `OVERRIDE`) The custom DNS record to send back in response to the query. +* `block_override_ttl` - (Required if `block_response` is `OVERRIDE`) The recommended amount of time, in seconds, for the DNS resolver or web browser to cache the provided override record. Minimum value of 0. Maximum value of 604800. +* `block_response` - (Required if `action` is `BLOCK`) The way that you want DNS Firewall to block the request. Valid values: `NODATA`, `NXDOMAIN`, `OVERRIDE`. +* `firewall_domain_list_id` - (Required) The ID of the domain list that you want to use in the rule. +* `firewall_rule_group_id` - (Required) The unique identifier of the firewall rule group where you want to create the rule. +* `priority` - (Required) The setting that determines the processing order of the rule in the rule group. DNS Firewall processes the rules in a rule group by order of priority, starting from the lowest setting. + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +* `id` - The ID of the rule. + +## Import + + Route 53 Resolver DNS Firewall rules can be imported using the Route 53 Resolver DNS Firewall rule group ID and domain list ID separated by ':', e.g. + +``` +$ terraform import aws_route53_resolver_firewall_rule.example rslvr-frg-0123456789abcdef:rslvr-fdl-0123456789abcdef +```