MalformedPolicyDocument error on aws policy creation - terraform apply - terraform

While inserting new aws IAM policy rule on terraform, terraform plan passes
as terraform apply fails on the statement ID.
data "aws_iam_policy_document" "db_iam_policy_document" {
version = "2012-10-17"
statement {
actions = ["rds:DeleteDBInstance"]
effect = "Deny"
resources = [
"arn:aws:rds:us-west-2:123456789:db:*"
]
condition {
test = "StringEquals"
variable = "rds:db-tag/environment"
values = [
"production"
]
}
sid = "don't_delete_production_dbs !"
}
}
The error presented on my CI/CD pipeline as the following:
Error: error updating IAM policy arn:aws:iam::123456789:policy/my_policy_name:
MalformedPolicyDocument: Statement IDs (SID) must be alpha-numeric.
Check that your input satisfies the regular expression [0-9A-Za-z]*

According to aws iam documentation The Sid element on aws policy ( statement id - which helps us to improve and convey a better documenation and readability to our iam rules ) supports ASCII uppercase letters (A-Z), lowercase letters (a-z), and numbers (0-9).
we need to change the sid attibute following the right rules and get rid of the incorrect chars ( on this example space, underscore,comma and exclamation mark ).
sid = "productionDBDeletionIsProhibited"

Related

variable scanning procedure of tflint

I'm newbie to tflint and other scanning tools. I'm trying to understand the scanning procedure.
In my example, I have variable.tf file where i'm passing variable names like azure storage account name, account tier etc.
my variable.tf file has which i gave intentionally.
variable "storage_account_name"{
type = string
default = "test-sa-123"
}
and in main.tf, i'm using as var.storage_account_name.
if I do tflint, normally it should throw error as storage account name should not have special characters but it is not throwing any errors.
So I want to understand whether tflint is capable to take that variable from variables.tf file and throw error in main.tf?
I tried checkov also but it is not throwing error for this mistake.
Is there any other tool which can scan the variables.tf and throw error in main.tf? Or do we need write our own rule for this in tflint?
thanks,
Santosh
Underscores in Terraform variables are valid. Tflint won't identify that as far as I know.
It won't throw an error with this. Even if the variable is passed through to the provider and references a storage account I wouldn't expect that to throw an error.
There are plugins as rulsets like this azure one for tflint which might increase what it catches but I still wouldn't rely on it for this purpose.
The way I'd handle this would be through Terraform variable validation.
The rules around storage accounts seem to be (source):
length: 3-24
type: Lowercase letters and numbers.
To identify that as a regex value you could use: ^[a-z0-9]*$
See tests
Here's a a very rough example:
variable "storage_account_name" {
type = string
validation {
condition = (
length(var.storage_account_name) > 3 && length(var.storage_account_name) < 25
)
error_message = "The storage_account_name value must be between 3 and 24 characters."
}
validation {
condition = (
regex('/^[a-z0-9]*$/g', var.storage_account_name)
)
error_message = "The storage account name must be only lowercase letters and numbers."
}
}

How to pass list of s3 arns inside the terraform data resource aws_iam_policy_document

I am trying to pass multiple values to pricipals's identifiers in the data resource "aws_iam_policy_document". getting the following error
Inappropriate value for attribute "identifiers": element 0: string required.
s3_values variable is defined type = any and set the values as
....
s3_values:
bucket: bucketname1
s3_arns:
- arn:aws:iam::1234567890:root
- arn:aws:iam::2345678901:role/s3-read-role
data "aws_iam_policy_document" "s3_policy" {
count = length(var.s3_arns)
statement {
sid = "1"
effect = "Allow"
principals {
type = "AWS"
identifiers = ["${var.s3_values[count.index]["s3_arns"]}"]
}
actions = ["s3:PutObject"]
resources = ["arn:aws:s3:::${var.s3_values[count.index]["bucket"]}/*"]
}
}
I get the following error
Inappropriate value for attribute "identifiers": element 0: string required.
its working , when only one value is passed , but not working when we pass multiple values to the variable s3_arns.
It looks like you're trying to create multiple policy documents for a single S3 bucket. Rather than using count to create many documents, it would be best if you created a single policy document that gives access to each ARN you pass.
Currently it works for one ARN because the identifiers field gets passed a single string and creates a list with one string element. When you pass a list of ARNs, the identifiers field is instead creating a list with a list element that contains the ARN strings.
I would fix this by making the s3_arns field always be a list of strings, and removing the count field on the data resource. Once you do that you can change the line identifiers to be identifiers = var.s3_values.s3_arns and the resources line to be resources = ["arn:aws:s3:::${var.s3_values.bucket}/*"]

Get a list of possible outbound ip addresses in terraform

I'm trying to use the export from a azure function app in terraform to get the possible outbound ip addresses that I can add to a whitelist for a firewall
The parameter returned is a string of ips comma separated.
I have tried using the split function within terraform but it doesn't give a list, it gives an interface which can't be used as a list. I've tried using local scopes to add square brackets around it but still the same.
Let me just add this is terraform 11 not 12.
resource "azurerm_key_vault" "keyvault" {
name = "${var.project_name}-${var.environment}-kv"
location = "${azurerm_resource_group.environment.location}"
resource_group_name = "${azurerm_resource_group.environment.name}"
enabled_for_disk_encryption = true
tenant_id = "${var.tenant_id}"
sku_name = "standard"
network_acls {
bypass = "AzureServices"
default_action = "Deny"
ip_rules = "${split(",", azurerm_function_app.function.possible_outbound_ip_addresses)}"
}
tags = {
asset-code = "${var.storage_tags["asset_code"]}"
module-code = "${var.storage_tags["module_code"]}"
environment = "${var.environment}"
instance-code = "${var.storage_tags["instance_code"]}"
source = "terraform"
}
}
This comes back with the error "ip_rules must be a list".
Thanks
I think what you are seeing here is a classic Terraform 0.11 design flaw: when a value is unknown at plan time (because it will be decided only during apply), Terraform 0.11 can't properly track the type information for it.
Because possible_outbound_ip_addresses is an unknown value at planning time, the result of split with that string is also unknown. Because Terraform doesn't track type information for that result, the provider SDK code rejects that unknown value because it isn't a list.
To address this in Terraform 0.11 requires doing your initial run with the -target argument so that Terraform can focus on creating the function (and thus allocating its outbound IP addresses) first, and then deal with the processing of that string separately once it's known:
terraform apply -target=azurerm_function_app.function
terraform apply # to complete the rest of the work that -target excluded
Terraform 0.12 addressed this limitation by tracking type information for both known and unknown values, so in Terraform 0.12 the split function would see that you gave it an unknown string and accept that as being correctly typed, and then it would return an unknown list of strings to serve as a placeholder for the result that will be finally determined during the apply phase.
If is var.string is 1.2.3.4,5.6.7.8-
split(',', var.string)[0] should give you back 1.2.3.4 as a string. Your questions is difficult without an example.
Here is an example of how I can get a list of possible IPs
create a data source and then a locals var
app_services = [ "app1", "app2", "app3" ]
data "azurerm_app_service" "outbound_ips" {
count = length(var.app_services)
name = var.app_services[count.index]
resource_group_name = var.server_resource_group_name
}
locals {
apps_outbound_ips = distinct(flatten(concat(data.azurerm_app_service.outbound_ips.*.possible_outbound_ip_address_list)))
}
You don't have to use a data source either, if you are building the resource just use the outputs instead of a data source, in my case I use a data source as I build my apps separately.
Works flawlessly for me and produces a list of strings (Strings being each unique outbound IP of the set of app services / function apps) in the form of local.apps_outbound_ips
Enjoy :)

Handling Terraform AMI looking returning an empty list

Is there a better way than the following to handle a Terraform data resource aws_ami_ids returning an empty list?
Always want the module to return the latest AMI's ID if found.
If the list was empty I was getting a "list "data.aws_ami_ids.full_unencrypted_ami.ids" does not have any elements so cannot determine type." error, so this was the workaround.
data "aws_ami_ids" "full_unencrypted_ami" {
name_regex = "${var.ami_unencrypted_regex}"
owners = ["123456789","self"]
}
locals {
notfound = "${list("AMI Not Found")}"
unencrypted_ami = "${concat(data.aws_ami_ids.full_unencrypted_ami.ids,local.notfound)}"
}
output "full_ami_unencrypted_id" {
description = "Full Unencrypted AMI ID"
value = "${local.full_unencrypted_ami[0]}"
}
1) Use aws_ami_id instead of aws_ami_ids so that terraform apply fails if the AMI is gone, forcing you to update your Terraform solution.
OR
2) Create two aws_ami_ids data sources (the second being a fallback), concat the results and take the first item. But, as ydaetskcoR hinted at, why would you want this implicit (possibly undetected) fallback?

how to filter the associated security groups of an ec2 instance using terraform

I am trying to get the security groups associated with the instance with id "i-0abcdefgh1234" but the output gives no result.
terraform.tf
data "aws_instance" "ec2" {
instance_id = "i-0abcdefgh1234"
filter {
name = "tag:Name"
values = ["name-of-the-server"]
}
}
output "sg" {
value = "${data.aws_instance.ec2.*.security_groups}"
}
Output
data.aws_instance.ec2: Refreshing state...
------------------------------------------------------------------------
No changes. Infrastructure is up-to-date.
This means that Terraform did not detect any differences between your
configuration and real physical resources that exist. As a result, no
actions need to be performed.
Tried with and without * below
value = "${data.aws_instance.ec2.*.security_groups}"
The interpolation syntax for data sources is data.TYPE.NAME.ATTRIBUTE. See this
In your case it will be ${data.aws_instance.ec2.security_groups}
However, as the documentation states - "Some values are not always set and may not be available for interpolation."

Resources