From 0ca7d26b6f406887cee74837138c2264d616f40b Mon Sep 17 00:00:00 2001 From: Ben Doerr Date: Mon, 11 Dec 2023 03:02:19 -0500 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20(apply):=20Adds=20apply=20user=20an?= =?UTF-8?q?d=20role=20(#4)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .trivyignore | 2 + aws-iam-apply-policy.tf | 413 ++++++++++++++++++++++++++++ aws-iam-apply.tf | 85 ++++++ aws-iam-backend.tf | 15 +- examples/simple/ctx.tf | 2 + examples/simple/infracost-usage.yml | 42 --- examples/simple/main.tf | 30 +- outputs.tf | 32 ++- trivy.yaml | 2 - variables.tf | 39 ++- 10 files changed, 607 insertions(+), 55 deletions(-) create mode 100644 aws-iam-apply-policy.tf create mode 100644 aws-iam-apply.tf diff --git a/.trivyignore b/.trivyignore index ae9b640..0dbb37d 100644 --- a/.trivyignore +++ b/.trivyignore @@ -1 +1,3 @@ AVD-AWS-0089 +AVD-AWS-0342 +AVD-AWS-0057 diff --git a/aws-iam-apply-policy.tf b/aws-iam-apply-policy.tf new file mode 100644 index 0000000..77c2022 --- /dev/null +++ b/aws-iam-apply-policy.tf @@ -0,0 +1,413 @@ +data "aws_iam_policy_document" "apply_1" { + # budgets + dynamic "statement" { + for_each = var.apply_role.budgets ? range(1) : [] + content { + effect = "Allow" + actions = [ + "budgets:ModifyBudget", + "budgets:ViewBudget", + ] + resources = ["*"] + } + } + + # dynamodb + dynamic "statement" { + for_each = var.apply_role.dynamodb ? range(1) : [] + content { + effect = "Allow" + actions = [ + "dynamodb:CreateTable", + "dynamodb:DeleteTable", + "dynamodb:DescribeContinuousBackups", + "dynamodb:DescribeTable", + "dynamodb:DescribeTimeToLive", + "dynamodb:ListTagsOfResource", + "dynamodb:TagResource", + "dynamodb:UntagResource", + "dynamodb:UpdateTable", + "dynamodb:UpdateTimeToLive", + ] + resources = ["*"] + } + } + + # ec2_account + dynamic "statement" { + for_each = var.apply_role.ec2_account ? range(1) : [] + content { + effect = "Allow" + actions = [ + "ec2:DescribeAccountAttributes", + ] + resources = ["*"] + } + } + + # ec2_networking + dynamic "statement" { + for_each = var.apply_role.ec2_networking ? range(1) : [] + content { + effect = "Allow" + actions = [ + "ec2:AllocateAddress", + "ec2:AssociateAddress", + "ec2:AssociateDhcpOptions", + "ec2:AssociateRouteTable", + "ec2:AssociateVpcCidrBlock", + "ec2:AttachInternetGateway", + "ec2:AttachVpnGateway", + "ec2:AuthorizeSecurityGroupEgress", + "ec2:AuthorizeSecurityGroupIngress", + "ec2:CreateCustomerGateway", + "ec2:CreateDefaultVpc", + "ec2:CreateDhcpOptions", + "ec2:CreateEgressOnlyInternetGateway", + "ec2:CreateFlowLogs", + "ec2:CreateInternetGateway", + "ec2:CreateNatGateway", + "ec2:CreateNetworkAcl", + "ec2:CreateNetworkAclEntry", + "ec2:CreateRoute", + "ec2:CreateRouteTable", + "ec2:CreateSubnet", + "ec2:CreateVPC", + "ec2:CreateVpnGateway", + "ec2:DeleteCustomerGateway", + "ec2:DeleteDhcpOptions", + "ec2:DeleteEgressOnlyInternetGateway", + "ec2:DeleteFlowLogs", + "ec2:DeleteInternetGateway", + "ec2:DeleteNatGateway", + "ec2:DeleteNetworkAcl", + "ec2:DeleteNetworkAclEntry", + "ec2:DeleteRoute", + "ec2:DeleteRouteTable", + "ec2:DeleteSubnet", + "ec2:DeleteVPC", + "ec2:DeleteVpnGateway", + "ec2:DescribeAddresses", + "ec2:DescribeCustomerGateways", + "ec2:DescribeDhcpOptions", + "ec2:DescribeEgressOnlyInternetGateways", + "ec2:DescribeFlowLogs", + "ec2:DescribeInternetGateways", + "ec2:DescribeNatGateways", + "ec2:DescribeNetworkAcls", + "ec2:DescribeRouteTables", + "ec2:DescribeSecurityGroups", + "ec2:DescribeSubnets", + "ec2:DescribeVpcAttribute", + "ec2:DescribeVpcs", + "ec2:DescribeVpnGateways", + "ec2:DetachInternetGateway", + "ec2:DetachVpnGateway", + "ec2:DisableVgwRoutePropagation", + "ec2:DisassociateAddress", + "ec2:DisassociateRouteTable", + "ec2:DisassociateVpcCidrBlock", + "ec2:EnableVgwRoutePropagation", + "ec2:ReleaseAddress", + "ec2:RevokeSecurityGroupEgress", + "ec2:RevokeSecurityGroupIngress", + ] + resources = ["*"] + } + } + + # ec2_tags + dynamic "statement" { + for_each = var.apply_role.ec2_tags ? range(1) : [] + content { + effect = "Allow" + actions = [ + "ec2:CreateTags", + "ec2:DeleteTags", + ] + resources = ["*"] + } + } + + # ecs + dynamic "statement" { + for_each = var.apply_role.ecs ? range(1) : [] + content { + effect = "Allow" + actions = [ + "ecs:CreateCluster", + "ecs:CreateService", + "ecs:DeleteCluster", + "ecs:DeleteService", + "ecs:DeregisterTaskDefinition", + "ecs:DescribeClusters", + "ecs:DescribeServices", + "ecs:DescribeTaskDefinition", + "ecs:RegisterTaskDefinition", + "ecs:TagResource", + "ecs:UntagResource", + "ecs:UpdateCluster", + "ecs:UpdateService" + ] + resources = ["*"] + } + } + + # efs + dynamic "statement" { + for_each = var.apply_role.efs ? range(1) : [] + content { + effect = "Allow" + actions = [ + "elasticfilesystem:CreateAccessPoint", + "elasticfilesystem:CreateFileSystem", + "elasticfilesystem:DeleteAccessPoint", + "elasticfilesystem:DeleteFileSystem", + "elasticfilesystem:DescribeAccessPoints", + "elasticfilesystem:DescribeFileSystems", + "elasticfilesystem:DescribeLifecycleConfiguration", + "elasticfilesystem:DescribeMountTargets", + "elasticfilesystem:TagResource", + "elasticfilesystem:UntagResource" + ] + resources = ["*"] + } + } +} + +data "aws_iam_policy_document" "apply_2" { + # iam + dynamic "statement" { + for_each = var.apply_role.iam ? range(1) : [] + content { + effect = "Allow" + actions = [ + "iam:AddUserToGroup", + "iam:AttachGroupPolicy", + "iam:AttachRolePolicy", + "iam:CreateAccessKey", + "iam:CreateGroup", + "iam:CreateOpenIDConnectProvider", + "iam:CreatePolicy", + "iam:CreateRole", + "iam:CreateUser", + "iam:DeleteAccessKey", + "iam:DeleteGroup", + "iam:DeleteOpenIDConnectProvider", + "iam:DeletePolicy", + "iam:DeleteRole", + "iam:DeleteRolePermissionsBoundary", + "iam:DeleteUser", + "iam:DetachGroupPolicy", + "iam:DetachRolePolicy", + "iam:GetGroup", + "iam:GetOpenIDConnectProvider", + "iam:GetPolicy", + "iam:GetPolicyVersion", + "iam:GetRole", + "iam:GetUser", + "iam:ListAccessKeys", + "iam:ListAttachedGroupPolicies", + "iam:ListAttachedRolePolicies", + "iam:ListGroupsForUser", + "iam:ListInstanceProfilesForRole", + "iam:ListPolicyVersions", + "iam:ListRolePolicies", + "iam:PassRole", + "iam:PutRolePermissionsBoundary", + "iam:RemoveUserFromGroup", + "iam:TagOpenIDConnectProvider", + "iam:TagPolicy", + "iam:TagRole", + "iam:TagUser", + "iam:UnTagUser", + "iam:UntagOpenIDConnectProvider", + "iam:UntagPolicy", + "iam:UpdateAccessKey", + "iam:UpdateOpenIDConnectProviderThumbprint", + ] + resources = ["*"] + } + } + + # kms + dynamic "statement" { + for_each = var.apply_role.kms ? range(1) : [] + content { + effect = "Allow" + actions = [ + "kms:CreateGrant", + "kms:Decrypt", + "kms:DescribeKey", + "kms:Encrypt", + "kms:GenerateDataKey*", + "kms:ListAliases", + "kms:ReEncrypt*", + ] + resources = ["*"] + } + } + + # lambda + dynamic "statement" { + for_each = var.apply_role.lambda ? range(1) : [] + content { + effect = "Allow" + actions = [ + "lambda:AddPermission", + "lambda:CreateFunction", + "lambda:DeleteFunction", + "lambda:GetFunction", + "lambda:GetFunctionCodeSigningConfig", + "lambda:GetPolicy", + "lambda:ListVersionsByFunction", + "lambda:RemovePermission", + "lambda:TagResource", + "lambda:UntagResource" + ] + resources = ["*"] + } + } + + # logs + dynamic "statement" { + for_each = var.apply_role.logs ? range(1) : [] + content { + effect = "Allow" + actions = [ + "logs:AssociateKmsKey", + "logs:CreateLogGroup", + "logs:DeleteLogGroup", + "logs:DeleteResourcePolicy", + "logs:DeleteRetentionPolicy", + "logs:DeleteSubscriptionFilter", + "logs:DescribeLogGroups", + "logs:DescribeResourcePolicies", + "logs:DescribeSubscriptionFilters", + "logs:DisassociateKmsKey", + "logs:ListTagsLogGroup", + "logs:PutResourcePolicy", + "logs:PutRetentionPolicy", + "logs:PutSubscriptionFilter", + "logs:TagLogGroup", + "logs:UntagLogGroup", + ] + resources = ["*"] + } + } + + # route53 + dynamic "statement" { + for_each = var.apply_role.route53 ? range(1) : [] + content { + effect = "Allow" + actions = [ + "route53:ChangeResourceRecordSets", + "route53:ChangeTagsForResource", + "route53:CreateHostedZone", + "route53:CreateQueryLoggingConfig", + "route53:DeleteHostedZone", + "route53:DeleteQueryLoggingConfig", + "route53:GetChange", + "route53:GetHostedZone", + "route53:GetQueryLoggingConfig", + "route53:ListHostedZones", + "route53:ListResourceRecordSets", + "route53:ListTagsForResource", + ] + resources = ["*"] + } + } + + # s3 + dynamic "statement" { + for_each = var.apply_role.s3 ? range(1) : [] + content { + effect = "Allow" + actions = [ + "s3:CreateBucket", + "s3:DeleteBucket", + "s3:DeleteBucketWebsite", + "s3:GetAccelerateConfiguration", + "s3:GetBucketAcl", + "s3:GetBucketCORS", + "s3:GetBucketLocation", + "s3:GetBucketLogging", + "s3:GetBucketObjectLockConfiguration", + "s3:GetBucketPolicy", + "s3:GetBucketPublicAccessBlock", + "s3:GetBucketRequestPayment", + "s3:GetBucketTagging", + "s3:GetBucketVersioning", + "s3:GetBucketWebsite", + "s3:GetEncryptionConfiguration", + "s3:GetIntelligentTieringConfiguration", + "s3:GetInventoryConfiguration", + "s3:GetLifecycleConfiguration", + "s3:GetMetricsConfiguration", + "s3:GetObject", + "s3:GetObjectAcl", + "s3:GetReplicationConfiguration", + "s3:ListAllMyBuckets", + "s3:ListBucket", + "s3:PutAccelerateConfiguration", + "s3:PutBucketAcl", + "s3:PutBucketCORS", + "s3:PutBucketLogging", + "s3:PutBucketObjectLockConfiguration", + "s3:PutBucketPolicy", + "s3:PutBucketPublicAccessBlock", + "s3:PutBucketRequestPayment", + "s3:PutBucketVersioning", + "s3:PutBucketWebsite", + "s3:PutEncryptionConfiguration", + "s3:PutIntelligentTieringConfiguration", + "s3:PutInventoryConfiguration", + "s3:PutLifecycleConfiguration", + "s3:PutMetricsConfiguration", + "s3:PutObject", + "s3:PutObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutReplicationConfiguration" + ] + resources = ["*"] + } + } + + # sns + dynamic "statement" { + for_each = var.apply_role.sns ? range(1) : [] + content { + effect = "Allow" + actions = [ + "SNS:GetSubscriptionAttributes", + "sns:CreateTopic", + "sns:DeleteTopic", + "sns:GetTopicAttributes", + "sns:ListTagsForResource", + "sns:SetTopicAttributes", + "sns:TagResource", + "sns:UnTagResource", + ] + resources = ["*"] + } + } + + # ssm_params + dynamic "statement" { + for_each = var.apply_role.ssm_params ? range(1) : [] + content { + effect = "Allow" + actions = [ + "ssm:AddTagsToResource", + "ssm:DeleteParameter", + "ssm:DescribeParameters", + "ssm:GetParameter", + "ssm:GetParameters", + "ssm:ListTagsForResource", + "ssm:PutParameter" + ] + resources = ["*"] + } + } +} diff --git a/aws-iam-apply.tf b/aws-iam-apply.tf new file mode 100644 index 0000000..b3e8b8d --- /dev/null +++ b/aws-iam-apply.tf @@ -0,0 +1,85 @@ +module "label_apply" { + source = "bendoerr-terraform-modules/label/null" + version = "0.4.1" + context = var.context + name = "apply" +} + +resource "aws_iam_user" "apply" { + count = var.apply_user.create ? 1 : 0 + name = module.label_apply.id + tags = module.label_apply.tags + force_destroy = try(var.apply_user.force_destroy, null) +} + +resource "aws_iam_access_key" "apply" { + count = var.apply_user.create ? 1 : 0 + user = aws_iam_user.apply[0].name + pgp_key = var.apply_user.pgp_key +} + +data "aws_iam_user" "apply" { + count = var.apply_user.create ? 0 : 1 + user_name = var.apply_user.name +} + +data "aws_iam_policy_document" "apply_assume_role" { + count = var.apply_role.create ? 1 : 0 + + statement { + sid = replace("${module.label_apply.id}-0", "-", "") + actions = ["sts:AssumeRole"] + principals { + type = "AWS" + identifiers = [ + var.apply_user.create ? aws_iam_user.apply[0].arn : data.aws_iam_user.apply[0].arn + ] + } + } + + dynamic "statement" { + for_each = range(length(coalesce(var.apply_role.extra_principals, []))) + content { + sid = replace("${module.label_apply.id}-${statement.key + 1}", "-", "") + actions = ["sts:AssumeRole"] + principals { + type = var.apply_role.extra_principals[statement.key].type + identifiers = var.apply_role.extra_principals[statement.key].identifiers + } + } + } +} + +resource "aws_iam_role" "apply" { + count = var.apply_role.create ? 1 : 0 + name = module.label_apply.id + tags = module.label_apply.tags + assume_role_policy = data.aws_iam_policy_document.apply_assume_role[0].json +} + +data "aws_iam_role" "apply" { + count = var.apply_role.create ? 0 : 1 + name = var.apply_role.arn +} + +resource "aws_iam_policy" "apply_1" { + name = "${module.label_apply.id}-1" + tags = module.label_apply.tags + policy = data.aws_iam_policy_document.apply_1.json +} + +resource "aws_iam_role_policy_attachment" "apply_1" { + role = var.apply_role.create ? aws_iam_role.apply[0].id : data.aws_iam_role.apply[0].id + policy_arn = aws_iam_policy.apply_1.arn +} + +resource "aws_iam_policy" "apply_2" { + name = "${module.label_apply.id}-2" + tags = module.label_apply.tags + policy = data.aws_iam_policy_document.apply_2.json +} + +resource "aws_iam_role_policy_attachment" "apply_2" { + role = var.apply_role.create ? aws_iam_role.apply[0].id : data.aws_iam_role.apply[0].id + policy_arn = aws_iam_policy.apply_2.arn +} diff --git a/aws-iam-backend.tf b/aws-iam-backend.tf index c2fabe9..e701771 100644 --- a/aws-iam-backend.tf +++ b/aws-iam-backend.tf @@ -119,7 +119,7 @@ resource "aws_iam_policy" "backend_s3_rw" { policy = data.aws_iam_policy_document.backend_s3_rw[0].json } -data "aws_iam_policy_document" "assume_role" { +data "aws_iam_policy_document" "backend_assume_role" { count = var.backend_role.create ? 1 : 0 statement { @@ -150,17 +150,20 @@ resource "aws_iam_role" "backend" { count = var.backend_role.create ? 1 : 0 name = module.label_backend[0].id tags = module.label_backend[0].tags - assume_role_policy = data.aws_iam_policy_document.assume_role[0].json + assume_role_policy = data.aws_iam_policy_document.backend_assume_role[0].json +} + +data "aws_iam_role" "backend" { + count = var.backend_role.create ? 0 : 1 + name = var.backend_role.arn } resource "aws_iam_role_policy_attachment" "backend_dynamodb" { - count = var.backend_role.create ? 1 : 0 - role = aws_iam_role.backend[0].id + 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 = var.backend_role.create ? 1 : 0 - role = aws_iam_role.backend[0].id + 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 } diff --git a/examples/simple/ctx.tf b/examples/simple/ctx.tf index 34561c5..af33a74 100644 --- a/examples/simple/ctx.tf +++ b/examples/simple/ctx.tf @@ -26,6 +26,8 @@ module "context" { project = "simple" } +#tfsec:ignore:aws-s3-enable-bucket-encryption +#tfsec:ignore:aws-s3-encryption-customer-key module "tfstate" { source = "bendoerr-terraform-modules/tfstate/aws" version = "0.3.0" diff --git a/examples/simple/infracost-usage.yml b/examples/simple/infracost-usage.yml index 23ac156..d6ea368 100644 --- a/examples/simple/infracost-usage.yml +++ b/examples/simple/infracost-usage.yml @@ -3,45 +3,3 @@ # `infracost breakdown --usage-file infracost-usage.yml [other flags]` # See https://infracost.io/usage-file/ for docs version: 0.1 -resource_usage: - module.tfuser.module.store.aws_s3_bucket.this[0]: - standard: - storage_gb: - # Total storage in GB. - # Estimate 10 workspaces at 2MB each - 0.02 - - monthly_tier_1_requests: - # Monthly PUT, COPY, POST, LIST requests (Tier 1). - # 2 LIST & 1 PUT per apply - # Estimate: 5 applies a day for each workspace - # = 3 * 5 * 10 * 30 = 4500 - 4500 - - monthly_tier_2_requests: - # Monthly GET, SELECT, and all other requests (Tier 2). - # 2 GET per apply - # Estimate: 5 applies a day for each workspace - # = 2 * 5 * 10 * 30 = 3000 - 3000 - - monthly_select_data_scanned_gb: 0.0 # Monthly data scanned by S3 Select in GB. - monthly_select_data_returned_gb: 0.0 # Monthly data returned by S3 Select in GB. - - module.tfuser.aws_dynamodb_table.locks: - monthly_write_request_units: - # Monthly write request units in (used for on-demand DynamoDB). - # Estimate: 1 per apply 5 applies a day for each workspace - # = 5 * 10 * 30 - 1500 - - monthly_read_request_units: - # Monthly write request units in (used for on-demand DynamoDB). - 1500 - - storage_gb: 0.0000001 # Total storage for tables in GB. - - pitr_backup_storage_gb: 0 # Total storage for Point-In-Time Recovery (PITR) backups in GB. - on_demand_backup_storage_gb: 0 # Total storage for on-demand backups in GB. - monthly_data_restored_gb: 0 # Monthly size of restored data in GB. - monthly_streams_read_request_units: 0 # Monthly streams read request units. diff --git a/examples/simple/main.tf b/examples/simple/main.tf index d9e367d..e7c7ef8 100644 --- a/examples/simple/main.tf +++ b/examples/simple/main.tf @@ -3,19 +3,45 @@ module "tfuser" { context = module.context.shared backend_user = { - create : true, - pgp_key : "keybase:bendoerr" + create = true, + pgp_key = "keybase:bendoerr" } backend_role = { create = true + dynamodb_policy = { create = true table_arn = module.tfstate.lock_table_arn } + s3_policy = { create = true bucket_arn = module.tfstate.bucket_arn } } + + 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 + } } diff --git a/outputs.tf b/outputs.tf index 571d02d..e1e6525 100644 --- a/outputs.tf +++ b/outputs.tf @@ -19,11 +19,11 @@ output "backend_user_access_key_encrypted_secret" { } output "backend_role_arn" { - value = var.backend_role.create ? aws_iam_role.backend[0].arn : "" + value = var.backend_role.create ? aws_iam_role.backend[0].arn : data.aws_iam_role.backend[0].arn } output "backend_role_name" { - value = var.backend_role.create ? aws_iam_role.backend[0].name : "" + value = var.backend_role.create ? aws_iam_role.backend[0].name : data.aws_iam_role.backend[0].name } output "backend_dynamodb_rw_policy_arn" { @@ -33,3 +33,31 @@ output "backend_dynamodb_rw_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 } + +output "apply_user_arn" { + value = var.apply_user.create ? aws_iam_user.apply[0].arn : "" +} + +output "apply_user_name" { + value = var.apply_user.create ? aws_iam_user.apply[0].name : "" +} + +output "apply_user_unique_id" { + value = var.apply_user.create ? aws_iam_user.apply[0].unique_id : "" +} + +output "apply_user_access_key_id" { + value = var.apply_user.create ? aws_iam_access_key.apply[0].id : "" +} + +output "apply_user_access_key_encrypted_secret" { + value = var.apply_user.create ? aws_iam_access_key.apply[0].encrypted_secret : "" +} + +output "apply_role_arn" { + value = var.apply_role.create ? aws_iam_role.apply[0].arn : data.aws_iam_role.apply[0].arn +} + +output "apply_role_name" { + value = var.apply_role.create ? aws_iam_role.apply[0].name : data.aws_iam_role.apply[0].name +} diff --git a/trivy.yaml b/trivy.yaml index 33782a0..2fa7410 100644 --- a/trivy.yaml +++ b/trivy.yaml @@ -4,5 +4,3 @@ scan: - ./.infracost - ./examples/simple/.terraform - ./examples/simple/.infracost - - ./examples/with-backend-role/.terraform - - ./examples/with-backend-role/.infracost diff --git a/variables.tf b/variables.tf index 5b95000..8031702 100644 --- a/variables.tf +++ b/variables.tf @@ -31,7 +31,7 @@ variable "backend_user" { variable "backend_role" { type = object({ create = bool - arn = optional(string) # req, if create is false + arn = optional(string) # opt, if create is false extra_principals = optional(list(object({ type = string @@ -56,3 +56,40 @@ variable "backend_role" { # TODO Validation } + +variable "apply_user" { + type = object({ + create = bool + name = optional(string) # req, if create is false or invalid + force_destroy = optional(bool) # opt + pgp_key = optional(string) # req if create is true or invalid + }) +} + +variable "apply_role" { + type = object({ + create = bool + arn = optional(string) # req, if create is false + + extra_principals = optional(list(object({ + type = string + identifiers = list(string) + }))) + + budgets = optional(bool, false) + dynamodb = optional(bool, false) + ec2_account = optional(bool, false) + ec2_networking = optional(bool, false) + ec2_tags = optional(bool, false) + ecs = optional(bool, false) + efs = optional(bool, false) + iam = optional(bool, false) + kms = optional(bool, false) + lambda = optional(bool, false) + logs = optional(bool, false) + route53 = optional(bool, false) + s3 = optional(bool, false) + sns = optional(bool, false) + ssm_params = optional(bool, false) + }) +}