I have a Vault instance and I manage policies and secrets in it with Terraform. There are a couple of repeated steps when creating approle authentication, policy and policy documents for newly onboarded teams, because each team has several applications they work on. I'd like to modularize the repeated parts ( policy document, policy creation and approle for the team-app), though each application has a slightly different rule set.
Is there a way to create policy documents in a way that some rules are only included if a bool is set to true?
for example:
I have a module that creates policies and policy documents as below:
I would pass a bool variable named enable_metadata_rule and based on it's value I would create the 2nd rule or not:
resource "vault_policy" "example_policy" {
for_each = var.environments
provider = vault
name = "${var.team}-${var.application}-${each.key}"
policy = data.vault_policy_document.policy_document["${each.key}"].hcl
}
data "vault_policy_document" "policy_document" {
for_each = var.environments
rule {
path = "engines/${var.team}-kv/data/${each.key}/services/${var.application}/*"
capabilities = ["read", "list"]
description = "Read secrets for ${var.application}"
}
rule {
# IF enable_metadata_rule == true
path = "engines/${var.team}-kv/metadata/*"
capabilities = ["list"]
description = "List metadata for kv store"
}
}
If there isn't such thing, is there an option for merging separately created policy documents?
You should be able to do it using dynamic blocks:
data "vault_policy_document" "policy_document" {
for_each = var.environments
rule {
path = "engines/${var.team}-kv/data/${each.key}/services/${var.application}/*"
capabilities = ["read", "list"]
description = "Read secrets for ${var.application}"
}
dynamic "rule" {
for_each = var.enable_metadata_rule == true ? [1]: []
content {
path = "engines/${var.team}-kv/metadata/*"
capabilities = ["list"]
description = "List metadata for kv store"
}
}
}
Related
I realised that terraform modules are recreating its resources per module declaration. So the way to reference a resource created in a module can only be done from the module, if it's defined as output. I'm looking for a way where I can reuse a module not in the way so it's recreating resources.
Imagine a scenario where I have three terraform modules.
One is creating an IAM policy (AWS), second is creating an IAM role, third is creating a different IAM role, and both roles share the same IAM policy.
In code:
# policy
resource "aws_iam_policy" "secrets_manager_read_policy" {
name = "SecretsManagerRead"
description = "Read only access to secrets manager"
policy = {} # just to shorten demonstration
}
output "policy" {
value = aws_iam_policy.secrets_manager_read_policy
}
# test-role-1
resource "aws_iam_role" "test_role_1" {
name = "test-role-1"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "sts:AssumeRole"
Effect = "Allow"
Sid = ""
Principal = {
Service = "ecs-tasks.amazonaws.com"
}
},
]
})
}
module "policy" {
source = "../test-policy"
}
resource "aws_iam_role_policy_attachment" "attach_secrets_manager_read_to_role" {
role = aws_iam_role.test_role_1.name
policy_arn = module.policy.policy.arn
}
# test-role-2
resource "aws_iam_role" "test_role_2" {
name = "test-role-2"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "sts:AssumeRole"
Effect = "Allow"
Sid = ""
Principal = {
Service = "ecs-tasks.amazonaws.com"
}
},
]
})
}
module "policy" {
source = "../test-policy"
}
resource "aws_iam_role_policy_attachment" "attach_secrets_manager_read_to_role" {
role = aws_iam_role.test_role_2.name
policy_arn = module.policy.policy.arn
}
# create-roles
module "role-1" {
source = "../../../modules/resources/test-role-1"
}
module "role-2" {
source = "../../../modules/resources/test-role-2"
}
In this scenario terraform is trying to create two policies for each user, but I want them to use the same resource.
Is there a way to keep the code clean, so not all resources are in the same file so that a resource is identified, and the same resource can be used in multiple modules? Or it's a tree like structure where sibling modules cannot share the same child? Yes, I could define the policy first, and pass down the properties needed to child modules where I create the users, but what if I want to have a many to many relationship between them so multiple roles share the same multiple policies?
I can think of a few ways to do this:
Option 1: Move the use of the policy module up to the parent level, and have your parent (root) Terraform code look like this:
# create-policy
module "my-policy" {
source = "../../../modules/resources/policy"
}
# create-roles
module "role-1" {
source = "../../../modules/resources/test-role-1"
policy = module.my-policy.policy
}
module "role-2" {
source = "../../../modules/resources/test-role-2"
policy = module.my-policy.policy
}
Option 2: Output the policy from the role modules, and also make it an optional input variable of the modules:
variable "policy" {
default = null # Make the variable optional
}
module "policy" {
# Create the policy, only if one wasn't passed in
count = var.policy == null ? 1 : 0
source = "../test-policy"
}
locals {
# Create a variable with the value of either the passed-in policy,
# or the one we are creating
my-policy = var.policy == null ? module.policy[0].policy : var.policy
}
resource "aws_iam_role_policy_attachment" "attach_secrets_manager_read_to_role" {
role = aws_iam_role.test_role_2.name
policy_arn = local.my-policy
}
output "policy" {
value = locals.my-policy
}
Then your root code could look like this:
module "role-1" {
source = "../../../modules/resources/test-role-1"
}
module "role-2" {
source = "../../../modules/resources/test-role-2"
policy = module.role-1.policy
}
The first module wouldn't get an input, so it would create a new policy. The second module would get an input, so it would use it instead of re-creating the policy.
I also highly recommend looking at the source code for some of the official AWS Terraform modules, like this one. Reading the source code for those really helped me understand how to create reusable Terraform modules.
I have the following terraform code to create KMS Key. The My.tf file is using organization level common cmk core module that creates a key using aws_kms_key resource. This core module also attach a default key policy to the newly created Key.
my.tf file
//create key using core module
module "cmk" {
source = "git::https://company-repository-url/cmk?ref=v1.0.0"
name = "test"
enable_key_rotation = true
}
I don't have access to the core module. In My.tf file, after the Key is created I want to append the Key policy with the following policy document
data "aws_caller_identity" "current" {}
data "aws_iam_policy_document" "default" {
statement {
sid = "Some Sid"
effect = "Allow"
principals {
type = "AWS"
identifiers = [
"arn:aws:iam::123456789:root", //hardcoded. this is a cross account user
"arn:aws:iam::${data.aws_caller_identity.current.id}:role/service-role/SomeAWSRole"]
}
actions = [
"kms:CreateGrant",
"kms:ListGrants",
"kms:RevokeGrant"
]
resources = ["arn:aws:kms:us-west-2:${data.aws_caller_identity.current.id}:key/*"]
condition {
test = "Bool"
variable = "kms:GrantIsForAWSResource"
values = ["true"]
}
}
}
Is it possible to attach this policy to Key using aws_iam_policy_attachment or some other way?
I have a custom policy initiative I would like to assign using terraform, one of the policies does have parameters.
I know that for single policy assignment, we could use something like this, and pass variable as parameter.
parameters = jsonencode({
"operationName": {
"value": var.allowed_values,
}
})
however, I tried this with initiative, this doesn't seem to like it keeps complaining parameter is missing.
Infact, I tried to hardcode parameter into the body of azurerm_policy_assignment, I don't think even this is working.
resource "azurerm_policy_assignment" "example" {
name = "mcs_governance_policy_assignment"
scope = var.scopes
policy_definition_id = azurerm_policy_set_definition.iam.id
description = "example"
display_name = "Governance Policy"
metadata = <<METADATA
{
"category": "General"
}
METADATA
parameters = <<PARAMETERS
{
"operationName": {
"value": "Microsoft.Authorization/policyAssignments/write"
}
}
PARAMETERS
}
the policy initiative definition policy is as following, it uses built in policy, I have not included all the policies in this initiative, just for the one that complaining about the parameter
resource "azurerm_policy_set_definition" "iam" {
name = "mcs_iam"
policy_type = "Custom"
display_name = "MCS IAM Governance Policy Set"
description = "Contains MCS Governance policies"
metadata = <<METADATA
{
"category": "General"
}
METADATA
policy_definition_reference {
# An activity log alert should exist for specific Policy operations
policy_definition_id = "/providers/Microsoft.Authorization/policyDefinitions/c5447c04-a4d7-4ba8-a263-c9ee321a6858"
reference_id = "MCS IAM 3.1"
}
As I see, the azurerm_policy_set_definition iam only reference the policy definition, but it does not set the parameters in the policy_definition_reference block for the property parameter_values. You don't show how the policy definition configured, so you need to check how do the parameters setting in the policy definition and then add the parameter setting in the policy set definition.
I am particularly looking for way to have the following settings as shown in the image bellow. I want to make the S3 bucket restricted and choose to create new origin access identity as shown bellow.
Also it should make the update in S3 bucket policy, the settings might look different in image though.
In nutshell, I could not find or may be I didn't understand the official terraform documentations for achieving it.
You can use the below one for the reference,
resource "aws_cloudfront_distribution" "www" {
origin {
domain_name = "${var.bucket_name}.s3.amazonaws.com"
origin_id = "wwwS3Origin"
s3_origin_config {
origin_access_identity = "${aws_cloudfront_origin_access_identity.origin_access_identity.cloudfront_access_identity_path}"
}
}
enabled = true
is_ipv6_enabled = true
comment = "Some comment"
default_root_object = "index.html"
......
resource "aws_cloudfront_origin_access_identity" "origin_access_identity" {
comment = "S3 bucket OAI"
}
Update bucket policy
data "aws_iam_policy_document" "s3_policy" {
statement {
actions = ["s3:GetObject"]
resources = ["${aws_s3_bucket.example.arn}/*"]
principals {
type = "AWS"
identifiers = ["${aws_cloudfront_origin_access_identity.origin_access_identity.iam_arn}"]
}
}
statement {
actions = ["s3:ListBucket"]
resources = ["${aws_s3_bucket.example.arn}"]
principals {
type = "AWS"
identifiers = ["${aws_cloudfront_origin_access_identity.origin_access_identity.iam_arn}"]
}
}
}
resource "aws_s3_bucket_policy" "example" {
bucket = "${aws_s3_bucket.example.id}"
policy = "${data.aws_iam_policy_document.s3_policy.json}"
}
Refer the below link for more details
https://www.terraform.io/docs/providers/aws/r/cloudfront_origin_access_identity.html#updating-your-bucket-policy
I wish to attach an IAM policy to a subset of IAM roles, not all of them. This is documented below and wondering if it is possible to use an inline resource for loop? Running AWS provider, in Terraform v11.13.
Full list
variable "full_list" {
description = "List of the roles to be created"
default = ["put_log_a","put_log_b","put_log_c","put_log_d","put_log_e"]
}
Sub list
variable "sub_list" {
description = "Sub list of the roles"
default = ["put_log_c","put_log_e"]
}
First create a list of IAM roles.
resource "aws_iam_role" "iam_roles" {
count = "${length(var.full_list)}"
name = "${var.role_list[count.index]}_${var.environment}"
assume_role_policy = "${data.template_file.iam_role_trust_policy.rendered}"
force_detach_policies = "true"
tags = "${var.full_list_tags}"
}
Then create an IAM policy.
resource "aws_iam_policy" "s3_permissions_policy" {
name = "S3_Policy_${var.environment}"
description = "S3 policy ${var.environment}"
policy = "${file("${path.module}/files/policies/${var.environment}/s3_policy.json")}"
}
Then attach the policy to a subset list of IAM roles.
Example -
resource "aws_iam_role_policy_attachment" "s3_policy_attachment" {
count = "${length(var.sub_list)}"
role = "${aws_iam_role.iam_roles.*.name[count.index]}"
policy_arn = "${aws_iam_policy.s3_permissions_policy.arn}"
}
The generates the wrong result, sub_list has 2 items, positioned at 2 and 4 in the full_list. Rather than picking their correct index positions in the full_list, it takes the first two index positions in the full_list. In other words it attaches the policy to roles "put_log_a" and "put_log_b" rather than "put_log_c" and "put_log_e.
Is it possible to do something like -
resource "aws_iam_role_policy_attachment" "s3_policy_attachment" {
for i "${sub_list}"
if i in "${full_list}"
then
sub_list_item_index_in_full_list = "${full_list[i]}"
role = "${aws_iam_role.iam_roles.*.name[sub_list_item_index_in_full_list]}"
policy_arn = "${aws_iam_policy.s3_permissions_policy.arn}"
}
Okay - so after some playing around this solution works.
resource "aws_iam_role_policy_attachment" "s3_policy_attachment" {
count = "${length(var.sub_list)}"
role = "${aws_iam_role.iam_roles.*.name[index(var.full_list, element(var.sub_list, count.index))]}"
policy_arn = "${aws_iam_policy.s3_permissions_policy.arn}"
}