Skip to content

🚩 (backend): Allows omitting the backend user and role #7

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Dec 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 12 additions & 4 deletions aws-iam-apply.tf
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,21 @@ data "aws_iam_policy_document" "apply_assume_role" {
}

dynamic "statement" {
for_each = range(length(coalesce(var.apply_role.extra_principals, [])))
for_each = range(length(coalesce(var.apply_role.extra_assume_statements, [])))
content {
sid = replace("${module.label_apply.id}-${statement.key + 1}", "-", "")
actions = ["sts:AssumeRole"]
actions = var.apply_role.extra_assume_statements[statement.key].actions
principals {
type = var.apply_role.extra_principals[statement.key].type
identifiers = var.apply_role.extra_principals[statement.key].identifiers
type = var.apply_role.extra_assume_statements[statement.key].principals.type
identifiers = var.apply_role.extra_assume_statements[statement.key].principals.identifiers
}
dynamic "condition" {
for_each = range(length(coalesce(var.apply_role.extra_assume_statements[statement.key].conditions, [])))
content {
test = var.apply_role.extra_assume_statements[statement.key].conditions[condition.key].test
variable = var.apply_role.extra_assume_statements[statement.key].conditions[condition.key].variable
values = var.apply_role.extra_assume_statements[statement.key].conditions[condition.key].values
}
}
}
}
Expand Down
27 changes: 21 additions & 6 deletions aws-iam-backend.tf
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ resource "aws_iam_access_key" "backend" {
}

data "aws_iam_user" "backend" {
count = var.backend_user.create ? 0 : 1
count = var.backend_user.create ? 0 : (var.backend_user.name != null ? 1 : 0)
user_name = var.backend_user.name
}

Expand Down Expand Up @@ -134,13 +134,21 @@ data "aws_iam_policy_document" "backend_assume_role" {
}

dynamic "statement" {
for_each = range(length(coalesce(var.backend_role.extra_principals, [])))
for_each = range(length(coalesce(var.backend_role.extra_assume_statements, [])))
content {
sid = replace("${module.label_backend[0].id}-${statement.key + 1}", "-", "")
actions = ["sts:AssumeRole"]
actions = var.backend_role.extra_assume_statements[statement.key].actions
principals {
type = var.backend_role.extra_principals[statement.key].type
identifiers = var.backend_role.extra_principals[statement.key].identifiers
type = var.backend_role.extra_assume_statements[statement.key].principals.type
identifiers = var.backend_role.extra_assume_statements[statement.key].principals.identifiers
}
dynamic "condition" {
for_each = range(length(coalesce(var.backend_role.extra_assume_statements[statement.key].conditions, [])))
content {
test = var.backend_role.extra_assume_statements[statement.key].conditions[condition.key].test
variable = var.backend_role.extra_assume_statements[statement.key].conditions[condition.key].variable
values = var.backend_role.extra_assume_statements[statement.key].conditions[condition.key].values
}
}
}
}
Expand All @@ -153,17 +161,24 @@ resource "aws_iam_role" "backend" {
assume_role_policy = data.aws_iam_policy_document.backend_assume_role[0].json
}

locals {
attach_dyndb = var.backend_role.create && (var.backend_role.dynamodb_policy.create || var.backend_role.dynamodb_policy.policy_arn != null)
attach_s3 = var.backend_role.create && (var.backend_role.s3_policy.create || var.backend_role.s3_policy.policy_arn != null)
}

data "aws_iam_role" "backend" {
count = var.backend_role.create ? 0 : 1
count = var.backend_role.create ? 0 : (local.attach_dyndb || local.attach_s3 ? 1 : 0)
name = var.backend_role.arn
}

resource "aws_iam_role_policy_attachment" "backend_dynamodb" {
count = local.attach_dyndb ? 1 : 0
role = var.backend_role.create ? aws_iam_role.backend[0].id : data.aws_iam_role.backend[0].id
policy_arn = var.backend_role.dynamodb_policy.create ? aws_iam_policy.backend_dynamodb_rw[0].arn : var.backend_role.dynamodb_policy.policy_arn
}

resource "aws_iam_role_policy_attachment" "backend_s3" {
count = local.attach_s3 ? 1 : 0
role = var.backend_role.create ? aws_iam_role.backend[0].id : data.aws_iam_role.backend[0].id
policy_arn = var.backend_role.s3_policy.create ? aws_iam_policy.backend_s3_rw[0].arn : var.backend_role.s3_policy.policy_arn
}
27 changes: 27 additions & 0 deletions examples/only_apply/ctx.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
terraform {
required_version = ">= 0.13"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}

provider "aws" {
region = "us-east-1"
}

variable "namespace" {
type = string
}

module "context" {
source = "bendoerr-terraform-modules/context/null"
version = "0.4.1"
namespace = var.namespace
environment = "example"
role = "tfuser"
region = "us-east-1"
project = "simple"
}
5 changes: 5 additions & 0 deletions examples/only_apply/infracost-usage.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# You can use this file to define resource usage estimates for Infracost to use when calculating
# the cost of usage-based resource, such as AWS S3 or Lambda.
# `infracost breakdown --usage-file infracost-usage.yml [other flags]`
# See https://infracost.io/usage-file/ for docs
version: 0.1
28 changes: 28 additions & 0 deletions examples/only_apply/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
module "tfuser" {
source = "../.."
context = module.context.shared

apply_user = {
create = true,
pgp_key = "keybase:bendoerr"
}

apply_role = {
create = true
budgets = true
dynamodb = true
ec2_account = true
ec2_networking = true
ec2_tags = true
ecs = true
efs = true
iam = true
kms = true
lambda = true
logs = true
route53 = true
s3 = true
sns = true
ssm_params = true
}
}
32 changes: 16 additions & 16 deletions outputs.tf
Original file line number Diff line number Diff line change
@@ -1,63 +1,63 @@
output "backend_user_arn" {
value = var.backend_user.create ? aws_iam_user.backend[0].arn : ""
value = try(aws_iam_user.backend[0].arn, data.aws_iam_user.backend[0].arn, null)
}

output "backend_user_name" {
value = var.backend_user.create ? aws_iam_user.backend[0].name : ""
value = try(aws_iam_user.backend[0].id, data.aws_iam_user.backend[0].user_name, null)
}

output "backend_user_unique_id" {
value = var.backend_user.create ? aws_iam_user.backend[0].unique_id : ""
value = try(aws_iam_user.backend[0].unique_id, data.aws_iam_user.backend[0].id, null)
}

output "backend_user_access_key_id" {
value = var.backend_user.create ? aws_iam_access_key.backend[0].id : ""
value = var.backend_user.create ? aws_iam_access_key.backend[0].id : null
}

output "backend_user_access_key_encrypted_secret" {
value = var.backend_user.create ? aws_iam_access_key.backend[0].encrypted_secret : ""
value = var.backend_user.create ? aws_iam_access_key.backend[0].encrypted_secret : null
}

output "backend_role_arn" {
value = var.backend_role.create ? aws_iam_role.backend[0].arn : data.aws_iam_role.backend[0].arn
value = try(aws_iam_role.backend[0].arn, data.aws_iam_role.backend[0].arn, null)
}

output "backend_role_name" {
value = var.backend_role.create ? aws_iam_role.backend[0].name : data.aws_iam_role.backend[0].name
value = try(aws_iam_role.backend[0].name, data.aws_iam_role.backend[0].name, null)
}

output "backend_dynamodb_rw_policy_arn" {
value = var.backend_role.dynamodb_policy.create ? aws_iam_policy.backend_s3_rw[0].arn : var.backend_role.s3_policy.policy_arn
value = try(aws_iam_policy.backend_dynamodb_rw[0].arn, var.backend_role.dynamodb_policy.policy_arn)
}

output "backend_s3_rw_policy_arn" {
value = var.backend_role.s3_policy.create ? aws_iam_policy.backend_s3_rw[0].arn : var.backend_role.s3_policy.policy_arn
value = try(aws_iam_policy.backend_s3_rw[0].arn, var.backend_role.s3_policy.policy_arn)
}

output "apply_user_arn" {
value = var.apply_user.create ? aws_iam_user.apply[0].arn : ""
value = try(aws_iam_user.apply[0].arn, data.aws_iam_user.apply[0].arn, null)
}

output "apply_user_name" {
value = var.apply_user.create ? aws_iam_user.apply[0].name : ""
value = try(aws_iam_user.apply[0].name, data.aws_iam_user.apply[0].user_name, null)
}

output "apply_user_unique_id" {
value = var.apply_user.create ? aws_iam_user.apply[0].unique_id : ""
value = try(aws_iam_user.apply[0].unique_id, data.aws_iam_user.apply[0].id, null)
}

output "apply_user_access_key_id" {
value = var.apply_user.create ? aws_iam_access_key.apply[0].id : ""
value = var.apply_user.create ? aws_iam_access_key.apply[0].id : null
}

output "apply_user_access_key_encrypted_secret" {
value = var.apply_user.create ? aws_iam_access_key.apply[0].encrypted_secret : ""
value = var.apply_user.create ? aws_iam_access_key.apply[0].encrypted_secret : null
}

output "apply_role_arn" {
value = var.apply_role.create ? aws_iam_role.apply[0].arn : data.aws_iam_role.apply[0].arn
value = try(aws_iam_role.apply[0].arn, data.aws_iam_role.apply[0].arn, null)
}

output "apply_role_name" {
value = var.apply_role.create ? aws_iam_role.apply[0].name : data.aws_iam_role.apply[0].name
value = try(aws_iam_role.apply[0].name, data.aws_iam_role.apply[0].name, null)
}
47 changes: 47 additions & 0 deletions test/examples_only_apply_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package test_test

import (
"context"
"strings"
"testing"

"github.com/gruntwork-io/terratest/modules/random"

"github.com/aws/aws-sdk-go-v2/config"
"github.com/gruntwork-io/terratest/modules/terraform"
test_structure "github.com/gruntwork-io/terratest/modules/test-structure"
)

func TestOnlyApply(t *testing.T) {
rootFolder := "../"
terraformFolderRelativeToRoot := "examples/only_apply"

tempTestFolder := test_structure.CopyTerraformFolderToTemp(t, rootFolder, terraformFolderRelativeToRoot)

rndns := strings.ToLower(random.UniqueId())

terraformOptions := &terraform.Options{
// The path to where our Terraform code is located
TerraformDir: tempTestFolder,
Upgrade: true,
Vars: map[string]interface{}{
"namespace": rndns,
},
}

// At the end of the test, run `terraform destroy` to clean up any resources that were created
defer terraform.Destroy(t, terraformOptions)

// This will run `terraform init` and `terraform apply` and fail the test if there are any errors
terraform.InitAndApply(t, terraformOptions)

// AWS Session
_, err := config.LoadDefaultConfig(
context.TODO(),
config.WithRegion("us-east-1"),
)

if err != nil {
t.Fatal(err)
}
}
2 changes: 2 additions & 0 deletions trivy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@ scan:
- ./.infracost
- ./examples/simple/.terraform
- ./examples/simple/.infracost
- ./examples/only_apply/.terraform
- ./examples/only_apply/.infracost
45 changes: 34 additions & 11 deletions variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ variable "backend_user" {
force_destroy = optional(bool) # opt
pgp_key = optional(string) # req if create is true or invalid
})
default = {
create = false
}
nullable = false

# TODO Validation

Expand All @@ -33,26 +37,37 @@ variable "backend_role" {
create = bool
arn = optional(string) # opt, if create is false

extra_principals = optional(list(object({
type = string
identifiers = list(string)
extra_assume_statements = optional(list(object({
actions = list(string)
principals = object({
type = string
identifiers = list(string)
})
conditions = optional(list(object({
test = string
variable = string
values = list(string)
})))
})))

dynamodb_policy = object({
dynamodb_policy = optional(object({
create = bool
policy_arn = optional(string) # req, if create is false or invalid
table_arn = optional(string) # req, if create is true or invalid
kms_key = optional(string) # opt, if create is true or invalid
})
}), { create = false })

s3_policy = object({
s3_policy = optional(object({
create = bool
policy_arn = optional(string) # req, if create is false or invalid
bucket_arn = optional(string) # req, if create is true or invalid
kms_key = optional(string) # opt, if create is true or invalid
})

}), { create = false })
})
default = {
create = false
}
nullable = false

# TODO Validation
}
Expand All @@ -71,9 +86,17 @@ variable "apply_role" {
create = bool
arn = optional(string) # req, if create is false

extra_principals = optional(list(object({
type = string
identifiers = list(string)
extra_assume_statements = optional(list(object({
actions = list(string)
principals = object({
type = string
identifiers = list(string)
})
conditions = optional(list(object({
test = string
variable = string
values = list(string)
})))
})))

budgets = optional(bool, false)
Expand Down