Terraform - Manipulate local variables - variables

I am trying to setup some aws_ssoadmin_managed_policy_attachments (there will end up being a lot) so ideally I just want to be able to update a local variable and then use for_each to go through said variable and churn out a bunch of resources the other side.
I currently have something like the following:
locals {
roles = {
admin = {
//managed_policy_name = toset(["AdministratorAccess", "AWSSupportAccess"])
managed_policy_name = "AWSSupportAccess"
perm_set_name = "admin"
}
auditor = { ...
}
}
There will be a bunch more roles within this. I use the below to transform it to something more usable by the for_each.
managed_policy_map = [for keys, managed_policy_names in local.roles : {
for managed_policy_name in managed_policy_names :
format("%s-%s", keys, managed_policy_name) => { "managed_policy_name" : managed_policy_name }
}]
perm_set_to_managed_policy_map = merge(local.managed_policy_map...)
Output from this:
a = [{
"admin-AWSSupportAccess" = {
"managed_policy_name" = "AWSSupportAccess"
"perm_set_name" = "admin"
}
"admin-admin" = {
"managed_policy_name" = "admin"
"perm_set_name" = "admin"
}
"auditor-ReadOnlyAccess" = {
"managed_policy_name" = "ReadOnlyAccess"
"perm_set_name" = "auditor"
}
"auditor-auditor" = {
"managed_policy_name" = "auditor"
"perm_set_name" = "auditor"
}
"auditor-auditor-permission-set" = {
"managed_policy_name" = "auditor-permission-set"
"perm_set_name" = "auditor"
}
},]
Now, ideally, I would like to use the commented managed_policy_name which uses a list (or set to avoid dupes) //managed_policy_name = toset(["AdministratorAccess", "AWSSupportAccess"]) and cycle through that to end up with something like.
a = [{
"admin-AdministratorAccess" = {
"managed_policy_name" = "AdministratorAccess"
"perm_set_name" = "admin"
}
"admin-AWSSupportAccess" = {
"managed_policy_name" = "AWSSupportAccess"
"perm_set_name" = "admin"
}
"auditor-ReadOnlyAccess" = {
"managed_policy_name" = "ReadOnlyAccess"
"perm_set_name" = "auditor"
} ...
Is this doable? My assumption is that it will be fairly complicated or I'm missing some Terraform function that makes it easy. Any help would be greatly appreciated.

You can simplify your variable structure:
locals {
roles = {
admin = toset(["AdministratorAccess", "AWSSupportAccess"])
auditor = ...
}
}
and then simplify your for expression:
managed_policy_map = [for role, managed_policy_names in local.roles : {
for policy in managed_policy_names : "${role}-${policy}" => {
"managed_policy_name" = policy
"perm_set_name" = role
}
}]
to easily achieve the same output structure with the set type instead of the string type:
[
{
"admin-AWSSupportAccess" = {
"managed_policy_name" = "AWSSupportAccess"
"perm_set_name" = "admin"
}
"admin-AdministratorAccess" = {
"managed_policy_name" = "AdministratorAccess"
"perm_set_name" = "admin"
}
},
]
I would also recommend simplifying the output structure for easier use in the for_each meta-argument according to the intention stated in the question.

Related

Terraform Object Lock Configuration: AccessDenied

I have this terraform script that works perfectly fine for the whole s3 module but it cannot create the Object lock configuration resource and returns the message :
error creating S3 bucket (bucket-name) Object lock configuration: AccessDenied: AccessDenied
Status code 403, request id: ..., host id: ...
Desite the message, the S3 bucket is actually created, but I still get this error, maybe there is something missing in the policy ?
Here is my code.
module "s3_bucket" {
source = "terraform-aws-modules/s3-bucket/aws"
version = "3.4.0"
bucket = local.bucket_name
...
object_lock_enabled = true
attach_policy = true
policy = data.aws_iam_policy_document.voucher_s3_bucket.json
versioning = {
status = var.status
mfa_delete = var.mfa_delete
}
server_side_encryption_configuration = {
rule = {
apply_server_side_encryption_by_default = {
kms_master_key_id = aws_kms_key.voucher_s3_bucket.arn
sse_algorithm = "aws:kms"
}
}
}
}
data "aws_iam_policy_document" "s3_bucket_kms_key" {
statement {
sid = "AllowPutRoles"
effect = "Allow"
actions = ["kms:GenerateDataKey"]
principals {
identifiers = local.put_object_roles #we can use event_gateway iam_role for now
type = "AWS"
}
resources = ["*"]
}
statement {
sid = "AllowAdmin"
effect = "Allow"
actions = [
"kms:*",
]
principals {
identifiers = [data.aws_iam_role.admin_role.arn, data.aws_iam_role.default_role.arn, data.aws_iam_role.automation_role.arn]
type = "AWS"
}
resources = ["*"]
}
}
resource "aws_kms_key" "s3_bucket" {
tags = {
"s3_bucket" = local.bucket_name
}
enable_key_rotation = true
policy = data.aws_iam_policy_document.voucher_s3_bucket_kms_key.json
}
resource "aws_s3_bucket_object_lock_configuration" "s3_bucket_object_lock_configuration" {
bucket = local.bucket_name
rule {
default_retention {
mode = "GOVERNANCE"
years = 10
}
}
}
data "aws_iam_policy_document" "voucher_s3_bucket" {
statement {
sid = "DenyNoKMSEncryption"
effect = "Deny"
actions = ["s3:PutObject"]
principals {
identifiers = ["*"]
type = "*"
}
resources = ["${module.voucher_s3_bucket.s3_bucket_arn}/*"]
condition {
test = "StringNotEqualsIfExists"
values = ["aws:kms"]
variable = "s3:x-amz-server-side-encryption"
}
condition {
test = "Null"
values = ["false"]
variable = "s3:x-amz-server-side-encryption"
}
}
statement {
sid = "DenyWrongKMSKey"
effect = "Deny"
actions = ["s3:PutObject"]
principals {
identifiers = ["*"]
type = "*"
}
resources = ["${module.s3_bucket.s3_bucket_arn}/*"]
condition {
test = "StringNotEquals"
values = [aws_kms_key.voucher_s3_bucket.arn]
variable = "s3:x-amz-server-side-encryption-aws-kms-key-id"
}
}
statement {
sid = "AllowAdminDefault"
effect = "Allow"
actions = ["s3:*"]
principals {
identifiers = [data.aws_iam_role.admin_role.arn, data.aws_iam_role.default_role.arn]
type = "AWS"
}
resources = [
"${module.voucher_s3_bucket.s3_bucket_arn}/*",
module.voucher_s3_bucket.s3_bucket_arn,
]
}
statement {
sid = "DenyDeleteActions"
effect = "Deny"
actions = ["s3:DeleteBucket", "s3:DeleteObject", "s3:DeleteObjectVersion", "s3:PutBucketObjectLockConfiguration"]
principals {
identifiers = ["*"]
type = "AWS"
}
resources = [
"${module.s3_bucket.s3_bucket_arn}/*",
module.s3_bucket.s3_bucket_arn,
]
}
}

connection string generated by atlas cluster using terraform not in correct format

So im using terraform to create an atlas cluster but the output im getting is incompleteate to do my request terraform is givim me this:
mongodb+srv://esc-app-dbcluster-devel.b59mwv7.mongodb.net
and what i need shoul be more like this:
mongodb+srv://admin:admin#esc-app-dbcluster-devel.b59mwv7.mongodb.net/development?retryWrites=true&w=majority
or atleast thats the format that works with what im testing.
this is my terraform code:
terraform {
required_providers {
mongodbatlas = {
source = "mongodb/mongodbatlas"
version = "1.4.6"
}
}
}
provider "mongodbatlas" {
public_key = var.atlas_public_key
private_key = var.atlas_private_key
}
resource "mongodbatlas_cluster" "db-cluster" {
project_id = var.atlas_project_id
name = var.db_cluster_name
# Provider Settings "block"
provider_name = "TENANT" //free tier
backing_provider_name = "AWS"
provider_region_name = "US_EAST_1" //free tier
provider_instance_size_name = "M0" //free tier
}
resource "mongodbatlas_database_user" "dbuser" {
username = var.db_user
password = var.db_password
project_id = var.atlas_project_id
auth_database_name = "admin"
roles {
role_name = "readWrite"
database_name = var.environment
}
}
resource "mongodbatlas_project_ip_access_list" "test" {
project_id = var.atlas_project_id
cidr_block = var.cidr
}
output "db_cn_string" {
value = mongodbatlas_cluster.db-cluster.connection_strings.0.standard_srv
}
code i use to connect
const environment = process.env.ENVIRONMENT;
const uridb = "mongodb+srv://admin:admin#esc-app-dbcluster-devel.b59mwv7.mongodb.net/development?retryWrites=true&w=majority" // working format
//dburi = "mongodb+srv://esc-app-dbcluster-devel.b59mwv7.mongodb.net" --- format from terraform
console.log('environment:::::', environment);
let ENVIRONMENT_VARIABLES = {
'process.env.ENVIRONMENT': JSON.stringify(environment),
'process.env.PORT': JSON.stringify('80'),
'process.env.MONGO_CONNECTION_STRING': JSON.stringify(uridb)
};
need a way to genarate the proper connection string

Dynamically AWS IAM policy document with principals

I am creating a dynamic AWS IAM policy document "FROM" static to "TO" dynamic but principals part gives "An argument named "principals" is not expected here"
If I delete "principals" from the aws_iam_policy_document it works. Any suggestion would be helpful.
FROM
data "aws_iam_policy_document" "bucket_policy" {
statement {
principals {
type = "AWS"
identifiers = [
"arn:aws:iam::sdfsdfsdeploy",
"arn:aws:iam::sdfsdfsdeploy/OrganizationAccountAccessRole"
]
}
actions = [
"s3:GetObject",
"s3:PutObject"
]
resources = formatlist("arn:aws:s3:::%s/*", var.bucket_name)
}
}
TO
this code in source = "../../modules/s3/main.tf"
data "aws_iam_policy_document" "bucket_policy" {
dynamic "statement" {
for_each = var.policies_list
iterator = role
content {
effect = lookup(role.value, "effect", null)
principals = lookup(role.value, "principals", null)
actions = lookup(role.value, "actions", null)
resources = lookup(role.value, "resources", null)
}
}
}
module "s3_test" {
source = "../../modules/s3"
region = var.region
policies_list = [
{
effect = "Allow"
principals = {
type = "AWS"
identifiers = [
"arn:aws:iam::3ssdfsdfy",
"arn:aws:iam::3ssdfsdfy:role/OrganizationAccountAccessRole"
]
}
actions = [
"s3:GetObject",
"s3:PutObject"
]
resources = formatlist("arn:aws:s3:::%s/*", "teskjkjsdkfkjskdjhkjfhkjhskjdf")
}
]
}
Found it.
variable "policies_list" {
description = "nested block: s3_aws_iam_policy_document"
type = set(object(
{
actions = list(string)
effect = string
principals = set(object(
{
type = string
identifiers = list(string)
}
))
resources = list(string)
}
))
default = []
}
data "aws_iam_policy_document" "bucket_policy" {
dynamic "statement" {
for_each = var. policies_list
iterator = role
content {
effect = lookup(role.value, "effect", null)
actions = lookup(role.value, "actions", null)
dynamic "principals" {
for_each = role.value.principals
content {
type = principals.value["type"]
identifiers = principals.value["identifiers"]
}
}
resources = lookup(role.value, "resources", null)
}
}
}
based on
https://github.com/niveklabs/tfwriter/blob/1ea629ed386bbe6a8f21617a430dae19ba536a98/google-beta/r/google_storage_bucket.md

Looping over map variable using for_each expression in terraform

I have variable that I want to iterate over using for_each in terraform to create multiple instances of submodule - node_groups, which is part of eks module. This is my variable:
variable "frame_platform_eks_node_groups" {
type = map
default = {
eks_kube_system = {
desired_capacity = 1,
max_capacity = 5,
min_capacity = 1,
instance_type = ["m5.large"],
k8s_label = "eks_kube_system",
additional_tags = "eks_kube_system_node"
},
eks_jenkins_build = {
desired_capacity = 1,
max_capacity = 10,
min_capacity = 1,
instance_type = ["m5.large"],
k8s_label = "eks_jenkins_build",
additional_tags = "eks_jenkins_build_node"
}
}
}
And this is my node_groups submodule, which is part of module eks.
module "eks" {
...
node_groups = {
for_each = var.frame_platform_eks_node_groups
each.key = {
desired_capacity = each.value.desired_capacity
max_capacity = each.value.max_capacity
min_capacity = each.value.min_capacity
instance_types = each.value.instance_type
k8s_labels = {
Name = each.value.k8s_label
}
additional_tags = {
ExtraTag = each.value.additional_tags
}
}
When I run terraform plan I am getting following error:
15: each.key = {
If this expression is intended to be a reference, wrap it in parentheses. If
it’s instead intended as a literal name containing periods, wrap it in quotes
to create a string literal.
My intention obviously is to get eks_kube_system and eks_jenkins_build values from the map variable with each.key reference. But something is wrong. Do you have advice what I am doing wrong?
Thank you!
Its not exactly clear what node_groups is as it is not defined in your question, but assuming that it is a list of maps, then the code should be:
module "eks" {
...
node_groups = [
for k,v in var.frame_platform_eks_node_groups:
{
desired_capacity = v.desired_capacity
max_capacity = v.max_capacity
min_capacity = v.min_capacity
instance_types = v.instance_type
k8s_labels = {
Name = v.k8s_label
}
additional_tags = {
ExtraTag = v.additional_tags
}
}
]

How to configure App Service to use Azure AD login from Terraform

It is easy to Configure a web App Service to use Azure AD login manually via the official document However, How can I achieve this from Terraform? I've searched a while didn't found any examples, if you happen to address one, would be nice to share with me.
The following code is how I created Resource group and provisioned the web application
terraform {
backend "azurerm" {}
}
terraform {
required_version = ">= 0.13"
}
resource "azurerm_resource_group" "tf_resource_group" {
name = "RG_${var.application_name}_${var.environment}"
location = var.location
tags = {
environment = var.environment
DeployedBy = "terraform"
}
}
resource "azurerm_app_service_plan" "tf_service_plan" {
name = "${var.application_name}-${var.environment}-asp"
location = azurerm_resource_group.tf_resource_group.location
resource_group_name = azurerm_resource_group.tf_resource_group.name
kind = "Linux"
reserved = true
sku {
tier = "Standard"
size = "S1"
}
tags = {
environment = var.environment
DeployedBy = "terraform"
}
}
resource "azurerm_app_service" "tf_app_service" {
name = var.application_name
location = azurerm_resource_group.tf_resource_group.location
resource_group_name = azurerm_resource_group.tf_resource_group.name
app_service_plan_id = azurerm_app_service_plan.tf_service_plan.id
site_config {
always_on = true
linux_fx_version = "DOCKER|${var.acr_name}.azurecr.io/${var.img_repo_name}:${var.tag}"
}
app_settings = {
DOCKER_REGISTRY_SERVER_URL = "$DRSRUL"
WEBSITES_ENABLE_APP_SERVICE_STORAGE = "false"
DOCKER_REGISTRY_SERVER_USERNAME = "$ACRNAME"
DOCKER_REGISTRY_SERVER_PASSWORD = "$PW"
}
identity {
type = "SystemAssigned"
}
}
I believe your "azurerm_app_service" resource block needs a auth_settings block with a active_directory block. Example:
auth_settings {
enabled = true
active_directory {
client_id = "${azuread_application.example.application_id}"
}
default_provider = "AzureActiveDirectory"
issuer = "https://sts.windows.net/xxxxxxx-xxxx-xxx-xxxx-xxxtenantID/"