Terraform multiple blocks inside dynamic block? - terraform

I'm trying to create a module for the aws_wafv2_web_acl resource and I can't figure out how to add multiple 'excluded_rule' blocks inside a dynamic block. Is this possible? Here is the resource:
resource "aws_wafv2_web_acl" "web-acl" {
name = var.name
description = ""
scope = "REGIONAL"
default_action {
allow {}
}
dynamic "rule" {
for_each = var.rules
content {
name = rule.value["name"]
priority = rule.value["priority"]
override_action {
count {}
}
statement {
managed_rule_group_statement {
name = rule.value["name"]
vendor_name = "AWS"
excluded_rule {
name = "excluded rule"
}
}
}
visibility_config {
cloudwatch_metrics_enabled = false
sampled_requests_enabled = false
metric_name = rule.value["name"]
}
}
}
visibility_config {
cloudwatch_metrics_enabled = false
sampled_requests_enabled = false
metric_name = "webaclmetric"
}
}
And here are the variables being passed:
name = "test"
rules = [
{"name": "AWSManagedRulesLinuxRuleSet", "priority": 0, "exclusions": "LFI_QUERYARGUMENTS,LFI_URIPATH"},
{"name": "AWSManagedRulesWindowsRuleSet", "priority": 1, "exclusions": "PowerShellCommands_Set1_QUERYARGUMENTS"}
]

it is possible. You may want to take a look at the terraform module I have written for WafV2 web acl -> https://github.com/umotif-public/terraform-aws-waf-webaclv2
Going back to your question you can solve it with the following block:
dynamic "excluded_rule" {
for_each = length(lookup(managed_rule_group_statement.value, "excluded_rule", {})) == 0 ? [] : toset(lookup(managed_rule_group_statement.value, "excluded_rule"))
content {
name = excluded_rule.value
}
}
and then you can pass in the following into your module
managed_rule_group_statement = {
name = "AWSManagedRulesCommonRuleSet"
vendor_name = "AWS"
excluded_rule = [
"SizeRestrictions_QUERYSTRING",
"SizeRestrictions_BODY",
"GenericRFI_QUERYARGUMENTS"
]
}

Related

Terraform Count for conditional cdn delivery rule

I create my cdn endpoints as with the code below. Also I have one variable "environment" which either has the value "dev" or "prod". The delivery rule "DevRedirectToWWW" should only be applied when the variable has the value "dev". Im looking for a kind of if/else statement which allowes me to do that.
resource "azurerm_cdn_endpoint" "cdn_endpoint" {
name = "${var.environment}-test"
profile_name = azurerm_cdn_profile.cdn_profile.name
location = azurerm_cdn_profile.cdn_profile.location
resource_group_name = data.azurerm_resource_group.rg.name
origin_host_header = azurerm_storage_account.frontend_storage.primary_web_host
is_https_allowed = true
origin {
name = "${var.environment}-origin"
host_name = azurerm_storage_account.frontend_storage.primary_web_host
}
delivery_rule {
name = "EnforceHTTPS"
order = "1"
request_scheme_condition {
operator = "Equal"
match_values = ["HTTP"]
}
url_redirect_action {
redirect_type = "Found"
protocol = "Https"
}
}
delivery_rule {
name = "redirectToIndex"
order = "2"
url_file_extension_condition {
match_values = [
"0",
]
negate_condition = true
operator = "GreaterThan"
transforms = []
}
url_rewrite_action {
destination = "/index.html"
preserve_unmatched_path = false
source_pattern = "/"
}
}
delivery_rule {
name = "DevRedirectToWWW"
order = "3"
request_uri_condition {
operator = "BeginsWith"
match_values = [ "https://dev.test.app/" ]
}
url_redirect_action {
redirect_type = "Moved"
protocol = "Https"
hostname = "www.google.com"
}
}
depends_on = [
azurerm_cdn_profile.cdn_profile
]
}
You can achieve that by using a dynamic block [1] with for_each [2]. The way you would do that would be:
dynamic "delivery_rule" {
for_each = var.environment == "dev" ? [1] : []
content {
name = "DevRedirectToWWW"
order = "3"
request_uri_condition {
operator = "BeginsWith"
match_values = [ "https://dev.test.app/" ]
}
url_redirect_action {
redirect_type = "Moved"
protocol = "Https"
hostname = "www.google.com"
}
}
}
[1] https://developer.hashicorp.com/terraform/language/expressions/dynamic-blocks
[2] https://developer.hashicorp.com/terraform/language/meta-arguments/for_each

How to Exclude list of variablized rules dynamically from AWS WAF Terraform resource aws_wafv2_web_acl

I am trying to Create an AWS WEB-ACL using Terraform having multiple rules, also want to exclude multiple rules from AWS Managed rulset. but I am not able to exclude multiple rules dynamically coming from variables. here is my code.
variable.tfvars
# Region
region="us-east-1"
# Environment
environment="nonprod"
# ACLs Definations
acls = {
web: {
AWSManagedRuleSets: [
{
name: "AWSManagedRulesCommonRuleSet",
vendor_name: "AWS",
excluded_rule: [
"SizeRestrictions_QUERYSTRING",
"NoUserAgent_HEADER"
]
}
]
},
api: {
AWSManagedRuleSets: [
{
name: "AWSManagedRulesCommonRuleSet",
vendor_name: "AWS",
excluded_rule: [
"SizeRestrictions_QUERYSTRING",
"NoUserAgent_HEADER"
]
},
{
name: "AWSManagedRulesLinuxRuleSet",
vendor_name: "AWS",
excluded_rule: []
}
]
},
}
main.tf
resource "aws_wafv2_web_acl" "web_acl" {
for_each = var.acls
name = "waf-web-acl-${lower(var.environment)}-${each.key}"
description = "WAF ACL ap-${each.key} for env ${lower(var.environment)}"
scope = "REGIONAL"
default_action {
allow {}
}
dynamic "rule" {
for_each = var.acls[each.key]["AWSManagedRuleSets"]
content {
name = rule.value.name
priority = 0
override_action {
count {}
}
statement {
managed_rule_group_statement {
name = rule.value.name
vendor_name = rule.value.vendor_name
// HERE I WANNA EXCLUDE ALL THE RULE LISTED IN VARIABLE
// excluded_rule = rule.value.excluded_rule
// excluded_rule {
// name = "SizeRestrictions_QUERYSTRING"
// }
// excluded_rule {
// name = "NoUserAgent_HEADER"
// }
}
}
visibility_config {
cloudwatch_metrics_enabled = true
metric_name = "waf-rule-${lower(var.environment)}-${each.key}-${rule.value.name}"
sampled_requests_enabled = true
}
}
}
tags = {
ManagedBy = "Terraform"
}
visibility_config {
cloudwatch_metrics_enabled = true
metric_name = "waf-web-acl-${lower(var.environment)}-${each.key}"
sampled_requests_enabled = true
}
}
I don't have access to AWS but I think it's possible with a nested block like below..
dynamic "rule" {
for_each = var.acls[each.key]["AWSManagedRuleSets"]
content {
name = rule.value.name
priority = 0
override_action {
count {}
}
statement {
managed_rule_group_statement {
name = rule.value.name
vendor_name = rule.value.vendor_name
}
// another for_each loop to iterate over excluded_rule list
dynamic "excluded_rule" {
for_each = rule.value.excluded_rule
content {
name = excluded_rule.value
}
}
}
visibility_config {
cloudwatch_metrics_enabled = true
metric_name = "waf-rule-${lower(var.environment)}-${each.key}-${rule.value.name}"
sampled_requests_enabled = true
}
}
}

depends_on "A static list expression is required."

I'm using the depends_on block with a condition check while creating object storage. Surprisingly, I saw the following error. Any pointers on how to resolve it?
code:
locals {
is_gov = local.realm == "oc2" || local.realm == "oc3" ? true : false
}
resource "oci_identity_compartment" "gov_comp" {
compartment_id = var.comp1
description = "GOV COMP"
name = "gov_comp"
defined_tags = { "Operations.CostCenter" = "001" }
freeform_tags = { "Department" = "Executives" }
}
resource "oci_identity_compartment" "non_gov_comp" {
compartment_id = var.comp3
description = "commerical comp"
name = "non_gov_cmop"
defined_tags = { "Operations.CostCenter" = "000" }
freeform_tags = { "Department" = "Non-Executives" }
}
resource "oci_objectstorage_bucket" "test_bucket" {
compartment_id = var.compartment_id
name = var.bucket_name
namespace = var.bucket_namespace
depends_on = is_gov ? [oci_identity_compartment.gov_comp] : [oci_identity_compartment.non_gov_comp]
}
Error:
depends_on = local.is_gov ? [oci_identity_compartment.gov_comp] : [
A static list expression is required.
Your gov_comp and non_gov_comp are always created toghether. They are not exclusive. Thus your test_bucket should be create when both these resources get created:
resource "oci_objectstorage_bucket" "test_bucket" {
compartment_id = var.compartment_id
name = var.bucket_name
namespace = var.bucket_namespace
depends_on = [oci_identity_compartment.gov_com, oci_identity_compartment.non_gov_comp]
}

Creating a dynamic topic block within aws_s3_bucket_notification resource (Terraform)

I am creating a Terraform module that allows users to specify a map of s3 buckets and the properties of the event notifications they wish to add to those buckets.
The variable that they will pass to the module will look something like the following:
input = {
bucket_1 = {
name = "name-of-bucket-1"
filters = [
{
name = "filter1"
filter_prefix = "/test"
filter_suffix = ".txt"
},
{
name = "filter2"
filter_prefix = ""
filter_suffix = ".gz"
}
]
},
bucket_2 = {
name = "name-of-bucket-2"
log_source_type = "aws:cloudtrail:sandbox"
filters = [
{
name = "filter1"
filter_prefix = ""
filter_suffix = ".gz"
}
]
}
}
The resource block will be created as follows:
resource "aws_s3_bucket_notification" "notification" {
for_each = var.input
bucket = each.value.name
dynamic "topic" {
for_each = each.value.filters
content {
topic_arn = aws_sns_topic.sns_topic_s3[each.key].arn
events = ["s3:ObjectCreated:*"]
filter_prefix = each.value.filters.filter_prefix
filter_suffix = each.value.filters.filter_suffix
}
}
}
Unfortunately, when attempting to run a plan, I am getting the following error:
Error: Unsupported attribute
on modules/aws-splunk-forwarder-s3/main.tf line 26, in resource "aws_s3_bucket_notification" >"splunk_forwarder_s3":
26: filter_suffix = each.value.filters.filter_suffix
|----------------
| each.value.filters is tuple with 2 elements
This value does not have any attributes.
Does anyone have idea how I can achieve this?
Thanks,
Adam
Nevermind everyone... I managed to work it out.
Posting my answer in case anybody else is in the same predicament.
resource "aws_s3_bucket_notification" "notification" {
for_each = var.splunk_s3_input
bucket = each.value.name
dynamic "topic" {
for_each = [for s in each.value.filters: {
suffix = s.filter_suffix
prefix = s.filter_prefix
}]
content {
topic_arn = aws_sns_topic. sns_topic_s3[each.key].arn
events = ["s3:ObjectCreated:*"]
filter_prefix = topic.value.suffix
filter_suffix = topic.value.prefix
}
}
}
My issue was using "each.value" instead of "topic.value" when attempting to reference the values of the dynamic block loop.

Terraform .12 nested loop

variables.tf
variable "teams" {
type = map(any)
default = {}
}
input_value:
teams = {
{
team_id = "abc"
role_names = ["owner"]
},
{
team_id = "bcd"
role_names = ["read", "write"]
}
}
}
main.tf:
resource "mongodbatlas_project" "project" {
name = "testing"
org_id = "123456"
dynamic "teams" {
for_each = var.teams
content {
id = teams.value.team_id
names = [teams.value.role_names]
}
}
}
I have been trying the above code and it is not working. Is there an easier way to assign nested team value to the variable?
The teams variable does not seem to be correct for me and there are syntax errors (e.g. extra }in teams). I think it should be list, not map:
variable "teams" {
type = list(any)
default = []
}
and then
teams = [
{
team_id = "abc"
role_names = ["owner"]
},
{
team_id = "bcd"
role_names = ["read", "write"]
}
]
Then your resource could be:
resource "mongodbatlas_project" "project" {
name = "testing"
org_id = "123456"
dynamic "teams" {
for_each = toset(var.teams)
content {
id = teams.value.team_id
names = teams.value.role_names
}
}
}
When using dynamic blocks the iterator is called same as the block name.

Resources