1
0

initial commit

This commit is contained in:
xpk
2026-02-13 15:44:24 +08:00
parent 66be8224f4
commit 09ce4c881a
570 changed files with 61807 additions and 0 deletions
@@ -0,0 +1,81 @@
<!-- This readme file is generated with terraform-docs -->
## Requirements
No requirements.
## Providers
| Name | Version |
|------|---------|
| aws | n/a |
## Modules
No modules.
## Resources
| Name | Type |
|------|------|
| [aws_kms_alias.allpurpose](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/kms_alias) | resource |
| [aws_kms_alias.backup](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/kms_alias) | resource |
| [aws_kms_alias.database](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/kms_alias) | resource |
| [aws_kms_alias.log](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/kms_alias) | resource |
| [aws_kms_alias.notify](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/kms_alias) | resource |
| [aws_kms_alias.secret](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/kms_alias) | resource |
| [aws_kms_alias.storage](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/kms_alias) | resource |
| [aws_kms_key.allpurpose](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/kms_key) | resource |
| [aws_kms_key.backup](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/kms_key) | resource |
| [aws_kms_key.database](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/kms_key) | resource |
| [aws_kms_key.eks_ebs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/kms_key) | resource |
| [aws_kms_key.log](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/kms_key) | resource |
| [aws_kms_key.notify](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/kms_key) | resource |
| [aws_kms_key.secret](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/kms_key) | resource |
| [aws_kms_key.storage](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/kms_key) | resource |
| [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source |
| [aws_iam_policy_document.UseOfKeyByAll](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
| [aws_iam_policy_document.base](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
| [aws_iam_policy_document.eksebs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
| [aws_iam_policy_document.log](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
| [aws_iam_policy_document.notify](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
| [aws_iam_policy_document.rds](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
| [aws_iam_policy_document.storage](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
| [aws_iam_role.asg-service-linked-role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_role) | data source |
| [aws_region.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/region) | data source |
## Inputs
| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| bypass\_policy\_lockout\_safety\_check | A flag to indicate whether to bypass the key policy lockout safety check. Setting this value to true increases the risk that the KMS key becomes unmanageable | `bool` | `false` | no |
| create-allpurpose-key | Create a CMK for general use | `bool` | n/a | yes |
| create-backup-key | Create a CMK for use with AWS backup | `bool` | n/a | yes |
| create-database-key | Create a CMK for use with databases such as RDS, DynamoDB, Redis | `bool` | n/a | yes |
| create-eksebs-key | Create a CMK for use with ENS volumes on EKS nodes | `bool` | n/a | yes |
| create-log-key | Create a CMK for use with logging such as CloudwatchLogs and Cloudtrail | `bool` | n/a | yes |
| create-notify-key | Create a CMK for use with notification and events | `bool` | n/a | yes |
| create-secret-key | Create a CMK for use with secretsmanager | `bool` | n/a | yes |
| create-storage-key | Create a CMK for use with storage such as EBS, S3, EFS | `bool` | n/a | yes |
| customer\_master\_key\_spec | Specifies whether the key contains a symmetric key or an asymmetric key pair and the encryption algorithms or signing algorithms that the key supports. Valid values: `SYMMETRIC_DEFAULT`, `RSA_2048`, `RSA_3072`, `RSA_4096`, `HMAC_256`, `ECC_NIST_P256`, `ECC_NIST_P384`, `ECC_NIST_P521`, or `ECC_SECG_P256K1`. Defaults to `SYMMETRIC_DEFAULT` | `string` | `"SYMMETRIC_DEFAULT"` | no |
| deletion\_window\_in\_days | The waiting period, specified in number of days. After the waiting period ends, AWS KMS deletes the KMS key. If you specify a value, it must be between `7` and `30`, inclusive. If you do not specify a value, it defaults to `30` | `number` | `30` | no |
| description | The description of the key as viewed in AWS console | `string` | `null` | no |
| enable\_default\_policy | Specifies whether to enable the default key policy. Defaults to `true` | `bool` | `true` | no |
| enable\_key\_rotation | Specifies whether key rotation is enabled. Defaults to `true` | `bool` | `true` | no |
| grants | A map of grant definitions to create | `any` | `{}` | no |
| is\_enabled | Specifies whether the key is enabled. Defaults to `true` | `bool` | `true` | no |
| key\_administrator\_arn | IAM user/group/role with highest permissions. If none is specified, access will be granted to this account | `string` | `null` | no |
| key\_usage | Specifies the intended use of the key. Valid values: `ENCRYPT_DECRYPT` or `SIGN_VERIFY`. Defaults to `ENCRYPT_DECRYPT` | `string` | `"ENCRYPT_DECRYPT"` | no |
| multi\_region | Indicates whether the KMS key is a multi-Region (`true`) or regional (`false`) key. Defaults to `false` | `bool` | `false` | no |
| name-prefix | Assign a name prefix for key alias | `string` | `null` | no |
| policy | A valid policy JSON document. Although this is a key policy, not an IAM policy, an `aws_iam_policy_document`, in the form that designates a principal, can be used | `string` | `null` | no |
| rotation\_period\_in\_days | rotation period in days | `number` | `365` | no |
## Outputs
| Name | Description |
|------|-------------|
| cmks | Customer managed KMS key arns |
---
## Authorship
This module was developed by xpk.
@@ -0,0 +1,21 @@
data "aws_caller_identity" "this" {}
module "example-keys" {
source = "../"
name-prefix = "xpk"
deletion_window_in_days = 7
create-allpurpose-key = true
create-backup-key = true
create-database-key = true
create-log-key = true
create-notify-key = true
create-secret-key = true
create-storage-key = true
create-eksebs-key = true
key_administrator_arn = data.aws_caller_identity.this.arn
}
output "cmks" {
value = module.example-keys.cmks.*
}
@@ -0,0 +1,27 @@
provider "aws" {
region = "ap-east-1"
default_tags {
tags = {
Environment = "lab"
Project = "iac"
Application = "terraform"
Owner = "ken2026"
TerraformDir = "${reverse(split("/", path.cwd))[1]}/${reverse(split("/", path.cwd))[0]}"
}
}
}
output "last-updated" {
value = timestamp()
}
terraform {
required_version = ">= 1.13.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.100.0"
}
}
}
@@ -0,0 +1,629 @@
/*
Module to create the following CMKs:
- allpurpose
- storage
- database
- secrets
- backup
- log
- notify
*/
data "aws_region" "this" {}
data "aws_caller_identity" "current" {}
# Keys
resource "aws_kms_key" "allpurpose" {
count = var.create-allpurpose-key ? 1 : 0
description = "All purpose customer-managed KMS key"
enable_key_rotation = var.enable_key_rotation
rotation_period_in_days = var.rotation_period_in_days
is_enabled = var.is_enabled
policy = data.aws_iam_policy_document.UseOfKeyByAll.json
deletion_window_in_days = var.deletion_window_in_days
customer_master_key_spec = "SYMMETRIC_DEFAULT"
key_usage = "ENCRYPT_DECRYPT"
multi_region = var.multi_region
bypass_policy_lockout_safety_check = var.bypass_policy_lockout_safety_check
}
resource "aws_kms_key" "storage" {
count = var.create-storage-key ? 1 : 0
description = "Customer-managed KMS key for encrypting cloud storage such as EBS and S3"
enable_key_rotation = var.enable_key_rotation
rotation_period_in_days = var.rotation_period_in_days
is_enabled = var.is_enabled
policy = data.aws_iam_policy_document.storage.json
deletion_window_in_days = var.deletion_window_in_days
customer_master_key_spec = "SYMMETRIC_DEFAULT"
key_usage = "ENCRYPT_DECRYPT"
multi_region = var.multi_region
bypass_policy_lockout_safety_check = var.bypass_policy_lockout_safety_check
}
# Key use for EBS volumes on EKS nodes
resource "aws_kms_key" "eks_ebs" {
count = var.create-eksebs-key ? 1 : 0
description = "CMK for use with ENS volumes on EKS nodes"
enable_key_rotation = var.enable_key_rotation
rotation_period_in_days = var.rotation_period_in_days
is_enabled = var.is_enabled
policy = data.aws_iam_policy_document.eksebs[0].json
deletion_window_in_days = var.deletion_window_in_days
customer_master_key_spec = "SYMMETRIC_DEFAULT"
key_usage = "ENCRYPT_DECRYPT"
multi_region = var.multi_region
bypass_policy_lockout_safety_check = var.bypass_policy_lockout_safety_check
}
resource "aws_kms_key" "database" {
count = var.create-database-key ? 1 : 0
description = "Customer-managed KMS key for encrypting cloud databases such as RDS and Elasticache"
enable_key_rotation = var.enable_key_rotation
rotation_period_in_days = var.rotation_period_in_days
is_enabled = var.is_enabled
policy = data.aws_iam_policy_document.rds.json
deletion_window_in_days = var.deletion_window_in_days
customer_master_key_spec = "SYMMETRIC_DEFAULT"
key_usage = "ENCRYPT_DECRYPT"
multi_region = var.multi_region
bypass_policy_lockout_safety_check = var.bypass_policy_lockout_safety_check
}
resource "aws_kms_key" "secret" {
count = var.create-secret-key ? 1 : 0
description = "Customer-managed KMS key for encrypting secrets"
enable_key_rotation = var.enable_key_rotation
rotation_period_in_days = var.rotation_period_in_days
is_enabled = var.is_enabled
policy = data.aws_iam_policy_document.UseOfKeyByAll.json
deletion_window_in_days = var.deletion_window_in_days
customer_master_key_spec = "SYMMETRIC_DEFAULT"
key_usage = "ENCRYPT_DECRYPT"
multi_region = var.multi_region
bypass_policy_lockout_safety_check = var.bypass_policy_lockout_safety_check
}
resource "aws_kms_key" "backup" {
count = var.create-backup-key ? 1 : 0
description = "Customer-managed KMS key for encrypting backup data"
enable_key_rotation = var.enable_key_rotation
rotation_period_in_days = var.rotation_period_in_days
is_enabled = var.is_enabled
policy = data.aws_iam_policy_document.UseOfKeyByAll.json
deletion_window_in_days = var.deletion_window_in_days
customer_master_key_spec = "SYMMETRIC_DEFAULT"
key_usage = "ENCRYPT_DECRYPT"
multi_region = var.multi_region
bypass_policy_lockout_safety_check = var.bypass_policy_lockout_safety_check
}
resource "aws_kms_key" "log" {
count = var.create-log-key ? 1 : 0
description = "Customer-managed KMS key for cloudwatch logs and cloudtrail"
enable_key_rotation = var.enable_key_rotation
rotation_period_in_days = var.rotation_period_in_days
is_enabled = var.is_enabled
policy = data.aws_iam_policy_document.log.json
deletion_window_in_days = var.deletion_window_in_days
customer_master_key_spec = "SYMMETRIC_DEFAULT"
key_usage = "ENCRYPT_DECRYPT"
multi_region = var.multi_region
bypass_policy_lockout_safety_check = var.bypass_policy_lockout_safety_check
}
resource "aws_kms_key" "notify" {
count = var.create-notify-key ? 1 : 0
description = "Customer-managed KMS key for encrypting notifications"
enable_key_rotation = var.enable_key_rotation
rotation_period_in_days = var.rotation_period_in_days
is_enabled = var.is_enabled
policy = data.aws_iam_policy_document.notify.json
deletion_window_in_days = var.deletion_window_in_days
customer_master_key_spec = "SYMMETRIC_DEFAULT"
key_usage = "ENCRYPT_DECRYPT"
multi_region = var.multi_region
bypass_policy_lockout_safety_check = var.bypass_policy_lockout_safety_check
}
locals {
prefix = var.name-prefix == null ? "" : "${var.name-prefix}-"
}
# Key aliases
resource "aws_kms_alias" "allpurpose" {
count = var.create-allpurpose-key ? 1 : 0
name = "alias/${local.prefix}allpurpose"
target_key_id = aws_kms_key.allpurpose[0].id
}
resource "aws_kms_alias" "storage" {
count = var.create-storage-key ? 1 : 0
name = "alias/${local.prefix}storage"
target_key_id = aws_kms_key.storage[0].id
}
resource "aws_kms_alias" "database" {
count = var.create-database-key ? 1 : 0
name = "alias/${local.prefix}database"
target_key_id = aws_kms_key.database[0].id
}
resource "aws_kms_alias" "backup" {
count = var.create-backup-key ? 1 : 0
name = "alias/${local.prefix}backup"
target_key_id = aws_kms_key.backup[0].id
}
resource "aws_kms_alias" "secret" {
count = var.create-secret-key ? 1 : 0
name = "alias/${local.prefix}secret"
target_key_id = aws_kms_key.secret[0].id
}
resource "aws_kms_alias" "log" {
count = var.create-log-key ? 1 : 0
name = "alias/${local.prefix}log"
target_key_id = aws_kms_key.log[0].id
}
resource "aws_kms_alias" "notify" {
count = var.create-notify-key ? 1 : 0
name = "alias/${local.prefix}notify"
target_key_id = aws_kms_key.notify[0].id
}
# Policies
data "aws_iam_policy_document" "storage" {
source_policy_documents = [data.aws_iam_policy_document.base.json]
statement {
sid = "Allow use by AWS services"
effect = "Allow"
principals {
identifiers = [
"delivery.logs.amazonaws.com" # vpc flow log
]
type = "Service"
}
actions = [
"kms:Encrypt",
"kms:Decrypt",
"kms:ReEncrypt*",
"kms:GenerateDataKey*",
"kms:Describe*"
]
resources = ["*"]
}
statement {
sid = "Allow use of key by s3 and ec2"
effect = "Allow"
principals {
identifiers = [data.aws_caller_identity.current.account_id]
type = "AWS"
}
actions = [
"kms:Encrypt",
"kms:Decrypt",
"kms:ReEncrypt*",
"kms:GenerateDataKey*",
"kms:Describe*"
]
resources = ["*"]
condition {
test = "StringEquals"
values = [
"ec2.${data.aws_region.this.name}.amazonaws.com",
"s3.${data.aws_region.this.name}.amazonaws.com"
]
variable = "kms:ViaService"
}
}
# this needs to be explicitly allowed for users and roles to be able to encrypt and decrypt data
statement {
sid = "Allow use of key by users and roles in same account"
effect = "Allow"
principals {
identifiers = [data.aws_caller_identity.current.account_id]
type = "AWS"
}
actions = [
"kms:Encrypt",
"kms:Decrypt",
"kms:ReEncrypt*",
"kms:GenerateDataKey*",
"kms:Describe*"
]
resources = ["*"]
}
}
data "aws_iam_policy_document" "rds" {
source_policy_documents = [data.aws_iam_policy_document.base.json]
statement {
sid = "Allow use by AWS services"
effect = "Allow"
principals {
identifiers = ["arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"]
type = "AWS"
}
actions = [
"kms:Encrypt",
"kms:Decrypt",
"kms:ReEncrypt*",
"kms:GenerateDataKey*",
"kms:Describe*"
]
resources = ["*"]
condition {
test = "StringEquals"
values = [
"rds.${data.aws_region.this.id}.amazonaws.com",
"elasticache.${data.aws_region.this.id}.amazonaws.com",
"dax.${data.aws_region.this.id}.amazonaws.com"
]
variable = "kms:ViaService"
}
}
}
data "aws_iam_role" "asg-service-linked-role" {
count = var.create-eksebs-key ? 1 : 0
name = "AWSServiceRoleForAutoScaling"
}
data "aws_iam_policy_document" "eksebs" {
count = var.create-eksebs-key ? 1 : 0
source_policy_documents = [data.aws_iam_policy_document.base.json]
statement {
sid = "Allow use by EKS"
effect = "Allow"
principals {
identifiers = [
data.aws_iam_role.asg-service-linked-role[0].arn
]
type = "AWS"
}
actions = [
"kms:Encrypt",
"kms:Decrypt",
"kms:ReEncrypt*",
"kms:GenerateDataKey*",
"kms:Describe*"
]
resources = ["*"]
}
statement {
sid = "Allow grants by EKS"
effect = "Allow"
principals {
identifiers = [
data.aws_iam_role.asg-service-linked-role[0].arn
]
type = "AWS"
}
actions = [
"kms:RevokeGrant",
"kms:ListGrants",
"kms:CreateGrant"
]
resources = ["*"]
condition {
test = "Bool"
values = ["true"]
variable = "kms:GrantIsForAWSResource"
}
}
}
data "aws_iam_policy_document" "notify" {
source_policy_documents = [data.aws_iam_policy_document.base.json]
statement {
sid = "Allow use by AWS services"
effect = "Allow"
principals {
identifiers = [
"logs.${data.aws_region.this.id}.amazonaws.com", # cloudwatch log groups
"backup.amazonaws.com", # Notifications
"events.amazonaws.com" # Notifications
]
type = "Service"
}
actions = [
"kms:Encrypt",
"kms:Decrypt",
"kms:ReEncrypt*",
"kms:GenerateDataKey*",
"kms:Describe*"
]
resources = ["*"]
}
}
data "aws_iam_policy_document" "log" {
source_policy_documents = [data.aws_iam_policy_document.base.json]
statement {
sid = "Allow use by AWS services"
effect = "Allow"
principals {
identifiers = [
"logs.${data.aws_region.this.id}.amazonaws.com", # cloudwatch log groups
"apigateway.${data.aws_region.this.id}.amazonaws.com", # api gateway
"delivery.logs.amazonaws.com", # vpc flow log
"cloudtrail.amazonaws.com" # cloudtrail
]
type = "Service"
}
actions = [
"kms:Encrypt",
"kms:Decrypt",
"kms:ReEncrypt*",
"kms:GenerateDataKey*",
"kms:Describe*"
]
resources = ["*"]
}
}
# allow all entities in this account to perform encryption and decryption
data "aws_iam_policy_document" "UseOfKeyByAll" {
source_policy_documents = [data.aws_iam_policy_document.base.json]
statement {
sid = "AllowUseOfKey"
effect = "Allow"
principals {
identifiers = ["arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"]
type = "AWS"
}
actions = [
"kms:Encrypt",
"kms:Decrypt",
"kms:ReEncrypt*",
"kms:GenerateDataKey*",
"kms:DescribeKey"
]
resources = ["*"]
condition {
test = "StringEquals"
values = [data.aws_caller_identity.current.account_id]
variable = "aws:PrincipalAccount"
}
}
statement {
sid = "AllowAttachmentOfPersistentResources"
effect = "Allow"
principals {
identifiers = ["arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"]
type = "AWS"
}
actions = [
"kms:CreateGrant",
"kms:ListGrants",
"kms:RevokeGrant"
]
resources = ["*"]
condition {
test = "Bool"
values = ["true"]
variable = "kms:GrantIsForAWSResource"
}
}
}
# base policies allowing full access to key admin and read access to all
data "aws_iam_policy_document" "base" {
source_policy_documents = [jsonencode(
{
"Id" : "CmkBasePolicy",
"Version" : "2012-10-17",
"Statement" : [
{
"Sid" : "ReadPermissonForAll",
"Effect" : "Allow",
"Principal" : {
"AWS" : "arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"
},
"Action" : [
"kms:DescribeKey",
"kms:GetKeyPolicy",
"kms:ListAliases",
"kms:ListKeyPolicies",
"kms:ListKeys",
"kms:ListResourceTags"
],
"Resource" : "*"
},
{
"Sid" : "KeyAdministratorsAccess",
"Effect" : "Allow",
"Principal" : {
"AWS" : var.key_administrator_arn != null ? var.key_administrator_arn : "arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"
},
"Action" : [
"kms:Create*",
"kms:Describe*",
"kms:Enable*",
"kms:List*",
"kms:Put*",
"kms:Update*",
"kms:Revoke*",
"kms:Disable*",
"kms:Get*",
"kms:Delete*",
"kms:TagResource",
"kms:UntagResource",
"kms:ScheduleKeyDeletion",
"kms:CancelKeyDeletion",
"kms:RotateKeyOnDemand"
],
"Resource" : "*"
}
]
}
)]
}
# data "aws_iam_policy_document" "this" {
# source_policy_documents = var.source_policy_documents
# override_policy_documents = var.override_policy_documents
#
# # Default policy - account wide access to all key operations
# dynamic "statement" {
# for_each = var.enable_default_policy ? [1] : []
#
# content {
# sid = "Default"
# actions = ["kms:*"]
# resources = ["*"]
#
# principals {
# type = "AWS"
# identifiers = ["arn:${data.aws_partition.current.partition}:iam::${data.aws_caller_identity.current.account_id}:root"]
# }
# }
# }
#
# # Key owner - all key operations
# dynamic "statement" {
# for_each = var.enable_default_policy ? [1] : []
#
# content {
# sid = "KeyOwner"
# actions = ["kms:*"]
# resources = ["*"]
#
# principals {
# type = "AWS"
# identifiers = var.key_owners
# }
# }
# }
#
# # Key administrators - https://docs.aws.amazon.com/kms/latest/developerguide/key-policy-default.html#key-policy-default-allow-administrators
# dynamic "statement" {
# for_each = length(var.key_administrators) > 0 ? [1] : []
#
# content {
# sid = "KeyAdministration"
# actions = [
# "kms:Create*",
# "kms:Describe*",
# "kms:Enable*",
# "kms:List*",
# "kms:Put*",
# "kms:Update*",
# "kms:Revoke*",
# "kms:Disable*",
# "kms:Get*",
# "kms:Delete*",
# "kms:TagResource",
# "kms:UntagResource",
# "kms:ScheduleKeyDeletion",
# "kms:CancelKeyDeletion",
# ]
# resources = ["*"]
#
# principals {
# type = "AWS"
# identifiers = var.key_administrators
# }
# }
# }
#
# # Key users - https://docs.aws.amazon.com/kms/latest/developerguide/key-policy-default.html#key-policy-default-allow-users
# dynamic "statement" {
# for_each = length(var.key_users) > 0 ? [1] : []
#
# content {
# sid = "KeyUsage"
# actions = [
# "kms:Encrypt",
# "kms:Decrypt",
# "kms:ReEncrypt*",
# "kms:GenerateDataKey*",
# "kms:DescribeKey",
# ]
# resources = ["*"]
#
# principals {
# type = "AWS"
# identifiers = var.key_users
# }
# }
# }
#
# # Key service users - https://docs.aws.amazon.com/kms/latest/developerguide/key-policy-default.html#key-policy-service-integration
# dynamic "statement" {
# for_each = length(var.key_service_users) > 0 ? [1] : []
#
# content {
# sid = "KeyServiceUsage"
# actions = [
# "kms:CreateGrant",
# "kms:ListGrants",
# "kms:RevokeGrant",
# ]
# resources = ["*"]
#
# principals {
# type = "AWS"
# identifiers = var.key_service_users
# }
#
# condition {
# test = "Bool"
# variable = "kms:GrantIsForAWSResource"
# values = [true]
# }
# }
# }
#
# # Key cryptographic operations - https://docs.aws.amazon.com/kms/latest/developerguide/key-policy-default.html#key-policy-users-crypto
# dynamic "statement" {
# for_each = length(var.key_symmetric_encryption_users) > 0 ? [1] : []
#
# content {
# sid = "KeySymmetricEncryption"
# actions = [
# "kms:Decrypt",
# "kms:DescribeKey",
# "kms:Encrypt",
# "kms:GenerateDataKey*",
# "kms:ReEncrypt*",
# ]
# resources = ["*"]
#
# principals {
# type = "AWS"
# identifiers = var.key_symmetric_encryption_users
# }
# }
# }
# }
# ################################################################################
# # Grant
# ################################################################################
#
# resource "aws_kms_grant" "this" {
# for_each = { for k, v in var.grants : k => v if var.create }
#
# name = try(each.value.name, each.key)
# key_id = aws_kms_key.this[0].key_id
# grantee_principal = each.value.grantee_principal
# operations = each.value.operations
#
# dynamic "constraints" {
# for_each = length(lookup(each.value, "constraints", {})) == 0 ? [] : [each.value.constraints]
#
# content {
# encryption_context_equals = try(constraints.value.encryption_context_equals, null)
# encryption_context_subset = try(constraints.value.encryption_context_subset, null)
# }
# }
#
# retiring_principal = try(each.value.retiring_principal, null)
# grant_creation_tokens = try(each.value.grant_creation_tokens, null)
# retire_on_delete = try(each.value.retire_on_delete, null)
# }
@@ -0,0 +1,33 @@
output "cmks" {
description = "Customer managed KMS key arns"
value = {
backup = {
alias = aws_kms_alias.backup.*.name
arn = aws_kms_key.backup.*.arn
},
database = {
alias = aws_kms_alias.database.*.name
arn = aws_kms_key.database.*.arn
},
allpurpose = {
alias = aws_kms_alias.allpurpose.*.name
arn = aws_kms_key.allpurpose.*.arn
},
secret = {
alias = aws_kms_alias.secret.*.name
arn = aws_kms_key.secret.*.arn
},
log = {
alias = aws_kms_alias.log.*.name
arn = aws_kms_key.log.*.arn
},
notify = {
alias = aws_kms_alias.notify.*.name
arn = aws_kms_key.notify.*.arn
},
storage = {
alias = aws_kms_alias.storage.*.name
arn = aws_kms_key.storage.*.arn
}
}
}
@@ -0,0 +1,128 @@
variable "create-allpurpose-key" {
description = "Create a CMK for general use"
type = bool
}
variable "create-storage-key" {
description = "Create a CMK for use with storage such as EBS, S3, EFS"
type = bool
}
variable "create-eksebs-key" {
description = "Create a CMK for use with ENS volumes on EKS nodes"
type = bool
}
variable "create-database-key" {
description = "Create a CMK for use with databases such as RDS, DynamoDB, Redis"
type = bool
}
variable "create-backup-key" {
description = "Create a CMK for use with AWS backup"
type = bool
}
variable "create-secret-key" {
description = "Create a CMK for use with secretsmanager"
type = bool
}
variable "create-log-key" {
description = "Create a CMK for use with logging such as CloudwatchLogs and Cloudtrail"
type = bool
}
variable "create-notify-key" {
description = "Create a CMK for use with notification and events"
type = bool
}
variable "name-prefix" {
description = "Assign a name prefix for key alias"
type = string
default = null
}
variable "bypass_policy_lockout_safety_check" {
description = "A flag to indicate whether to bypass the key policy lockout safety check. Setting this value to true increases the risk that the KMS key becomes unmanageable"
type = bool
default = false
}
variable "customer_master_key_spec" {
description = "Specifies whether the key contains a symmetric key or an asymmetric key pair and the encryption algorithms or signing algorithms that the key supports. Valid values: `SYMMETRIC_DEFAULT`, `RSA_2048`, `RSA_3072`, `RSA_4096`, `HMAC_256`, `ECC_NIST_P256`, `ECC_NIST_P384`, `ECC_NIST_P521`, or `ECC_SECG_P256K1`. Defaults to `SYMMETRIC_DEFAULT`"
type = string
default = "SYMMETRIC_DEFAULT"
}
variable "deletion_window_in_days" {
description = "The waiting period, specified in number of days. After the waiting period ends, AWS KMS deletes the KMS key. If you specify a value, it must be between `7` and `30`, inclusive. If you do not specify a value, it defaults to `30`"
type = number
default = 30
}
variable "description" {
description = "The description of the key as viewed in AWS console"
type = string
default = null
}
variable "enable_key_rotation" {
description = "Specifies whether key rotation is enabled. Defaults to `true`"
type = bool
default = true
}
variable "is_enabled" {
description = "Specifies whether the key is enabled. Defaults to `true`"
type = bool
default = true
}
variable "key_usage" {
description = "Specifies the intended use of the key. Valid values: `ENCRYPT_DECRYPT` or `SIGN_VERIFY`. Defaults to `ENCRYPT_DECRYPT`"
type = string
default = "ENCRYPT_DECRYPT"
}
variable "multi_region" {
description = "Indicates whether the KMS key is a multi-Region (`true`) or regional (`false`) key. Defaults to `false`"
type = bool
default = false
}
variable "policy" {
description = "A valid policy JSON document. Although this is a key policy, not an IAM policy, an `aws_iam_policy_document`, in the form that designates a principal, can be used"
type = string
default = null
}
variable "enable_default_policy" {
description = "Specifies whether to enable the default key policy. Defaults to `true`"
type = bool
default = true
}
variable "key_administrator_arn" {
description = "IAM user/group/role with highest permissions. If none is specified, access will be granted to this account"
type = string
default = null
}
################################################################################
# Grant
################################################################################
variable "grants" {
description = "A map of grant definitions to create"
type = any
default = {}
}
variable "rotation_period_in_days" {
description = "rotation period in days"
type = number
default = 365
}
@@ -0,0 +1,61 @@
<!-- This readme file is generated with terraform-docs -->
# SecretRotationReminder
Deploy lambda function which takes secret rotation event from secretsmanager
and send reminders to users using SNS.
This function can be used by any number of secrets
Secret ARN is obtained from the secretsmanager event
This function overrides the blueprint function from AWS. Instead of rotating the secret value,
it sends a reminder to user who will manually rotate the secret.
## Requirements
No requirements.
## Providers
| Name | Version |
|------|---------|
| archive | n/a |
| aws | n/a |
## Modules
No modules.
## Resources
| Name | Type |
|------|------|
| [aws_cloudwatch_log_group.rotation-reminder](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group) | resource |
| [aws_iam_policy.lambda](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource |
| [aws_iam_role.lambda](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
| [aws_iam_role_policy_attachment.lambda](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource |
| [aws_lambda_function.rotation-reminder](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lambda_function) | resource |
| [aws_lambda_permission.rotation-reminder](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lambda_permission) | resource |
| [aws_security_group.rotation-reminder](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource |
| [aws_sns_topic.reminder](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sns_topic) | resource |
| [aws_sns_topic_subscription.reminder](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sns_topic_subscription) | resource |
| [archive_file.payload](https://registry.terraform.io/providers/hashicorp/archive/latest/docs/data-sources/file) | data source |
| [aws_iam_policy_document.assume_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
| [aws_subnet.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/subnet) | data source |
## Inputs
| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| lambda-subnet-ids | List of subnets to place lambda function | `list(string)` | n/a | yes |
| logs-cmk-arn | ARN of cloudwatch logs encryption CMK | `string` | n/a | yes |
| prefix | Resource prefix. e.g. whk1-bea-icc-mbk | `string` | n/a | yes |
| rotation-reminder-recipients | SNS recipients for secret rotation reminders | `list(string)` | n/a | yes |
| sns-cmk-arn | ARN of SNS encryption CMK | `string` | n/a | yes |
## Outputs
| Name | Description |
|------|-------------|
| function-arn | n/a |
---
## Authorship
This module was developed by Rackspace.
@@ -0,0 +1,17 @@
module "secret-rotation-reminder" {
source = "../"
sns-cmk-arn = "arn:aws:kms:ap-east-1:111122223333:key/e13912c7-54d3-4d77-9a52-c482bcaf3209"
logs-cmk-arn = "arn:aws:kms:ap-east-1:111122223333:key/143d0178-8ad2-458b-90b3-0fa6b3e62fc4"
rotation-reminder-recipients = ["foo@bar.local"]
prefix = "prod-project1"
lambda-subnet-ids = ["subnet-001", "subnet-002"]
}
resource "aws_secretsmanager_secret_rotation" "secret-rotation" {
secret_id = "your-secret-id"
rotation_lambda_arn = module.secret-rotation-reminder.function-arn
rotate_immediately = false
rotation_rules {
automatically_after_days = 365
}
}
@@ -0,0 +1,143 @@
/**
* # SecretRotationReminder
* Deploy lambda function which takes secret rotation event from secretsmanager
* and send reminders to users using SNS.
* This function can be used by any number of secrets
* Secret ARN is obtained from the secretsmanager event
*
* This function overrides the blueprint function from AWS. Instead of rotating the secret value,
* it sends a reminder to user who will manually rotate the secret.
*/
resource "aws_sns_topic" "reminder" {
name = "${var.prefix}-SecretRotationReminder"
kms_master_key_id = var.sns-cmk-arn
}
resource "aws_sns_topic_subscription" "reminder" {
for_each = toset(var.rotation-reminder-recipients)
topic_arn = aws_sns_topic.reminder.arn
protocol = "email"
endpoint = each.value
}
data "archive_file" "payload" {
type = "zip"
source_file = "${path.module}/rotation_reminder.py"
output_path = "payload.zip"
}
data "aws_subnet" "this" {
id = var.lambda-subnet-ids[0]
}
resource "aws_security_group" "rotation-reminder" {
name = "${var.prefix}-SecretRotationReminder"
description = "Allow access to VPC endpoint"
vpc_id = data.aws_subnet.this.vpc_id
egress {
description = "Access to VPC endpoints"
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
}
resource "aws_lambda_function" "rotation-reminder" {
function_name = "${var.prefix}-SecretRotationReminder"
description = "Sends secret rotation reminder"
role = aws_iam_role.lambda.arn
handler = "rotation_reminder.lambda_handler"
filename = data.archive_file.payload.output_path
source_code_hash = data.archive_file.payload.output_base64sha256
runtime = "python3.13"
timeout = 180
vpc_config {
subnet_ids = var.lambda-subnet-ids
security_group_ids = [aws_security_group.rotation-reminder.id]
}
environment {
variables = {
SNS_TOPIC_ARN = aws_sns_topic.reminder.arn
}
}
}
resource "aws_lambda_permission" "rotation-reminder" {
statement_id = "SecretRotationReminderPermission"
action = "lambda:InvokeFunction"
function_name = aws_lambda_function.rotation-reminder.function_name
principal = "secretsmanager.amazonaws.com"
# this function should be allowed to send reminders for all secrets # source_arn = module.mock-secret.secret_arn
}
resource "aws_cloudwatch_log_group" "rotation-reminder" {
name = "/aws/lambda/whk1-bea-icc-obk-SecretRotationReminder"
retention_in_days = 400 # intentionally set to longer than 1 year as rotation may happen yearly
kms_key_id = var.logs-cmk-arn
}
resource "aws_iam_role" "lambda" {
name = "${var.prefix}-SecretRotationReminderFunctionRole"
assume_role_policy = data.aws_iam_policy_document.assume_role.json
}
resource "aws_iam_role_policy_attachment" "lambda" {
for_each = { for k, v in [
"arn:aws:iam::aws:policy/AWSLambdaExecute",
aws_iam_policy.lambda.arn
] : k => v }
role = aws_iam_role.lambda.name
policy_arn = each.value
}
resource "aws_iam_policy" "lambda" {
name_prefix = "SecretRotationPolicy"
policy = jsonencode(
{
"Version" : "2012-10-17",
"Statement" : [
{
"Sid" : "AllowAccessToSecretSnsVpc",
"Effect" : "Allow",
"Action" : [
"SNS:Publish",
"secretsmanager:DescribeSecret",
"secretsmanager:ListSecretVersionIds",
"secretsmanager:UpdateSecretVersionStage",
"secretsmanager:GetSecretValue",
"secretsmanager:PutSecretValue",
"ec2:CreateNetworkInterface",
"ec2:DescribeNetworkInterfaces",
"ec2:DescribeSubnets",
"ec2:DeleteNetworkInterface",
"ec2:AssignPrivateIpAddresses",
"ec2:UnassignPrivateIpAddresses",
"ec2:DescribeSecurityGroups",
"ec2:DescribeSubnets",
"ec2:DescribeVpcs",
"ec2:GetSecurityGroupsForVpc"
],
"Resource" : "*"
}
]
}
)
}
data "aws_iam_policy_document" "assume_role" {
statement {
effect = "Allow"
principals {
type = "Service"
identifiers = ["lambda.amazonaws.com"]
}
actions = ["sts:AssumeRole"]
}
}
@@ -0,0 +1,3 @@
output function-arn {
value = aws_lambda_function.rotation-reminder.arn
}
@@ -0,0 +1,112 @@
"""
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
This function is designed to send a reminder through SNS
when secretsmanager automatic rotation event arrives. It
does not rotate any secret.
Secretsmanager initiate rotation in 4 steps [1], and some of
these steps must be implemented even if the purpose of this
function is not to rotate any secret.
[1] https://docs.aws.amazon.com/secretsmanager/latest/userguide/rotate-secrets_lambda-functions.html
"""
import boto3
import os
# Read sns topic from lambda environment variable
SNS_TOPIC_ARN = os.environ['SNS_TOPIC_ARN']
# lambda handler
def lambda_handler(event, context):
# debug use
# print(f"DEBUG: Event received by Lambda: {event}")
secret_id = event['SecretId']
token = event['ClientRequestToken']
step = event['Step']
# Secretsmanager sends 4 rotation events, but we will only use the createSecret event
# and the finishSecret event
if step == "createSecret":
# send notification and create a new secret from existing secret
send_notification(secret_id, token)
elif step == "finishSecret":
# set new secret with version AWSCURRENT
swap_current_version(secret_id, token)
else:
print(f"Steps other than createSecret and finishSecret will be ignored: {step}")
def send_notification(secret_id, token):
print(f"Clone secret and send notification for {secret_id}")
sm_client = boto3.client('secretsmanager')
"""
A new secret version is required by rotation workflow
We will simply copy existing version to a new version
label it AWSPENDING
"""
orig_secret = sm_client.get_secret_value(
SecretId=secret_id,
VersionStage='AWSCURRENT'
)['SecretString']
response = sm_client.put_secret_value(
SecretId=secret_id,
ClientRequestToken=token,
SecretString=orig_secret,
VersionStages=['AWSPENDING']
)
print(f"Retrieved existing secret and saved it as AWSPENDING. Version id is {response['VersionId']}")
# Send out reminder about secret rotation
sns_client = boto3.client('sns')
sns_client.publish(
TopicArn=SNS_TOPIC_ARN,
Message=f"""Hello Cloud Operation team,
The following secret is due for update. Please perform the following steps to rotate the secret:
{secret_id}
1. Open the secret on secretsmanager.
2. Click the Retrieve secret value botton to reveal the Edit button.
3. Click on Edit and change the secret string to a new one. Click save to commit your change.
4. Update the password for the underlying resource (e.g. redis or rds)
5. Optionally update your application configuration with the new credential if it does not fetch from secretsmanager automatically
""",
Subject='Secret rotation reminder for ' + secret_id.split(":")[6]
)
print(f"Notification sent to {SNS_TOPIC_ARN}")
def swap_current_version(secret_id, token):
sm_client = boto3.client('secretsmanager')
metadata = sm_client.describe_secret(SecretId=secret_id)
pending_version_id = None
current_version_id = None
for version in metadata["VersionIdsToStages"]:
if "AWSCURRENT" in metadata["VersionIdsToStages"][version]:
current_version_id = version
elif "AWSPENDING" in metadata["VersionIdsToStages"][version]:
pending_version_id = version
print(f"Remove {current_version_id} from AWSCURRENT and point AWSCURRENT to {pending_version_id}")
sm_client.update_secret_version_stage(
SecretId=secret_id,
VersionStage="AWSCURRENT",
MoveToVersionId=pending_version_id,
RemoveFromVersionId=current_version_id
)
print(f"Remove AWSPENDING from {pending_version_id}")
sm_client.update_secret_version_stage(
SecretId=secret_id,
VersionStage='AWSPENDING',
RemoveFromVersionId=pending_version_id
)
@@ -0,0 +1,24 @@
variable "rotation-reminder-recipients" {
type = list(string)
description = "SNS recipients for secret rotation reminders"
}
variable "sns-cmk-arn" {
type = string
description = "ARN of SNS encryption CMK"
}
variable "prefix" {
type = string
description = "Resource prefix. e.g. whk1-bea-icc-mbk"
}
variable "lambda-subnet-ids" {
type = list(string)
description = "List of subnets to place lambda function"
}
variable "logs-cmk-arn" {
type = string
description = "ARN of cloudwatch logs encryption CMK"
}
@@ -0,0 +1,601 @@
##################################################################################
#
# Conformance Pack:
# Operational Best Practices for CIS AWS Foundations Benchmark Level 1
#
# This conformance pack helps verify compliance with CIS AWS Foundations Benchmark Level 1 requirements.
#
# See Parameters section for names and descriptions of required parameters.
#
##################################################################################
Parameters:
AccessKeysRotatedParamMaxAccessKeyAge:
Default: '90'
Type: String
IamPasswordPolicyParamMaxPasswordAge:
Default: '90'
Type: String
IamPasswordPolicyParamMinimumPasswordLength:
Default: '14'
Type: String
IamPasswordPolicyParamPasswordReusePrevention:
Default: '24'
Type: String
IamPasswordPolicyParamRequireLowercaseCharacters:
Default: 'true'
Type: String
IamPasswordPolicyParamRequireNumbers:
Default: 'true'
Type: String
IamPasswordPolicyParamRequireSymbols:
Default: 'true'
Type: String
IamPasswordPolicyParamRequireUppercaseCharacters:
Default: 'true'
Type: String
IamPolicyInUseParamPolicyARN:
Default: arn:aws:iam::aws:policy/AWSSupportAccess
Type: String
IamUserUnusedCredentialsCheckParamMaxCredentialUsageAge:
Default: '45'
Type: String
RestrictedIncomingTrafficParamBlockedPort3:
Default: '3389'
Type: String
S3AccountLevelPublicAccessBlocksPeriodicParamBlockPublicAcls:
Default: 'True'
Type: String
S3AccountLevelPublicAccessBlocksPeriodicParamBlockPublicPolicy:
Default: 'True'
Type: String
S3AccountLevelPublicAccessBlocksPeriodicParamIgnorePublicAcls:
Default: 'True'
Type: String
S3AccountLevelPublicAccessBlocksPeriodicParamRestrictPublicBuckets:
Default: 'True'
Type: String
S3BucketVersioningEnabledParamIsMfaDeleteEnabled:
Default: 'TRUE'
Type: String
Resources:
AccessKeysRotated:
Properties:
ConfigRuleName: access-keys-rotated
InputParameters:
maxAccessKeyAge:
Fn::If:
- accessKeysRotatedParamMaxAccessKeyAge
- Ref: AccessKeysRotatedParamMaxAccessKeyAge
- Ref: AWS::NoValue
Source:
Owner: AWS
SourceIdentifier: ACCESS_KEYS_ROTATED
Type: AWS::Config::ConfigRule
CloudTrailCloudWatchLogsEnabled:
Properties:
ConfigRuleName: cloud-trail-cloud-watch-logs-enabled
Source:
Owner: AWS
SourceIdentifier: CLOUD_TRAIL_CLOUD_WATCH_LOGS_ENABLED
Type: AWS::Config::ConfigRule
Ec2EbsEncryptionByDefault:
Properties:
ConfigRuleName: ec2-ebs-encryption-by-default
Source:
Owner: AWS
SourceIdentifier: EC2_EBS_ENCRYPTION_BY_DEFAULT
Type: AWS::Config::ConfigRule
EncryptedVolumes:
Properties:
ConfigRuleName: encrypted-volumes
Scope:
ComplianceResourceTypes:
- AWS::EC2::Volume
Source:
Owner: AWS
SourceIdentifier: ENCRYPTED_VOLUMES
Type: AWS::Config::ConfigRule
IamNoInlinePolicyCheck:
Properties:
ConfigRuleName: iam-no-inline-policy-check
Scope:
ComplianceResourceTypes:
- AWS::IAM::User
- AWS::IAM::Role
- AWS::IAM::Group
Source:
Owner: AWS
SourceIdentifier: IAM_NO_INLINE_POLICY_CHECK
Type: AWS::Config::ConfigRule
IamPasswordPolicy:
Properties:
ConfigRuleName: iam-password-policy
InputParameters:
MaxPasswordAge:
Fn::If:
- iamPasswordPolicyParamMaxPasswordAge
- Ref: IamPasswordPolicyParamMaxPasswordAge
- Ref: AWS::NoValue
MinimumPasswordLength:
Fn::If:
- iamPasswordPolicyParamMinimumPasswordLength
- Ref: IamPasswordPolicyParamMinimumPasswordLength
- Ref: AWS::NoValue
PasswordReusePrevention:
Fn::If:
- iamPasswordPolicyParamPasswordReusePrevention
- Ref: IamPasswordPolicyParamPasswordReusePrevention
- Ref: AWS::NoValue
RequireLowercaseCharacters:
Fn::If:
- iamPasswordPolicyParamRequireLowercaseCharacters
- Ref: IamPasswordPolicyParamRequireLowercaseCharacters
- Ref: AWS::NoValue
RequireNumbers:
Fn::If:
- iamPasswordPolicyParamRequireNumbers
- Ref: IamPasswordPolicyParamRequireNumbers
- Ref: AWS::NoValue
RequireSymbols:
Fn::If:
- iamPasswordPolicyParamRequireSymbols
- Ref: IamPasswordPolicyParamRequireSymbols
- Ref: AWS::NoValue
RequireUppercaseCharacters:
Fn::If:
- iamPasswordPolicyParamRequireUppercaseCharacters
- Ref: IamPasswordPolicyParamRequireUppercaseCharacters
- Ref: AWS::NoValue
Source:
Owner: AWS
SourceIdentifier: IAM_PASSWORD_POLICY
Type: AWS::Config::ConfigRule
IamPolicyInUse:
Properties:
ConfigRuleName: iam-policy-in-use
InputParameters:
policyARN:
Fn::If:
- iamPolicyInUseParamPolicyARN
- Ref: IamPolicyInUseParamPolicyARN
- Ref: AWS::NoValue
Source:
Owner: AWS
SourceIdentifier: IAM_POLICY_IN_USE
Type: AWS::Config::ConfigRule
IamPolicyNoStatementsWithAdminAccess:
Properties:
ConfigRuleName: iam-policy-no-statements-with-admin-access
Scope:
ComplianceResourceTypes:
- AWS::IAM::Policy
Source:
Owner: AWS
SourceIdentifier: IAM_POLICY_NO_STATEMENTS_WITH_ADMIN_ACCESS
Type: AWS::Config::ConfigRule
IamRootAccessKeyCheck:
Properties:
ConfigRuleName: iam-root-access-key-check
Source:
Owner: AWS
SourceIdentifier: IAM_ROOT_ACCESS_KEY_CHECK
Type: AWS::Config::ConfigRule
IamUserGroupMembershipCheck:
Properties:
ConfigRuleName: iam-user-group-membership-check
Scope:
ComplianceResourceTypes:
- AWS::IAM::User
Source:
Owner: AWS
SourceIdentifier: IAM_USER_GROUP_MEMBERSHIP_CHECK
Type: AWS::Config::ConfigRule
IamUserNoPoliciesCheck:
Properties:
ConfigRuleName: iam-user-no-policies-check
Scope:
ComplianceResourceTypes:
- AWS::IAM::User
Source:
Owner: AWS
SourceIdentifier: IAM_USER_NO_POLICIES_CHECK
Type: AWS::Config::ConfigRule
IamUserUnusedCredentialsCheck:
Properties:
ConfigRuleName: iam-user-unused-credentials-check
InputParameters:
maxCredentialUsageAge:
Fn::If:
- iamUserUnusedCredentialsCheckParamMaxCredentialUsageAge
- Ref: IamUserUnusedCredentialsCheckParamMaxCredentialUsageAge
- Ref: AWS::NoValue
Source:
Owner: AWS
SourceIdentifier: IAM_USER_UNUSED_CREDENTIALS_CHECK
Type: AWS::Config::ConfigRule
IncomingSshDisabled:
Properties:
ConfigRuleName: restricted-ssh
Scope:
ComplianceResourceTypes:
- AWS::EC2::SecurityGroup
Source:
Owner: AWS
SourceIdentifier: INCOMING_SSH_DISABLED
Type: AWS::Config::ConfigRule
MfaEnabledForIamConsoleAccess:
Properties:
ConfigRuleName: mfa-enabled-for-iam-console-access
Source:
Owner: AWS
SourceIdentifier: MFA_ENABLED_FOR_IAM_CONSOLE_ACCESS
Type: AWS::Config::ConfigRule
MultiRegionCloudTrailEnabled:
Properties:
ConfigRuleName: multi-region-cloudtrail-enabled
Source:
Owner: AWS
SourceIdentifier: MULTI_REGION_CLOUD_TRAIL_ENABLED
Type: AWS::Config::ConfigRule
RdsSnapshotEncrypted:
Properties:
ConfigRuleName: rds-snapshot-encrypted
Scope:
ComplianceResourceTypes:
- AWS::RDS::DBSnapshot
- AWS::RDS::DBClusterSnapshot
Source:
Owner: AWS
SourceIdentifier: RDS_SNAPSHOT_ENCRYPTED
Type: AWS::Config::ConfigRule
RdsStorageEncrypted:
Properties:
ConfigRuleName: rds-storage-encrypted
Scope:
ComplianceResourceTypes:
- AWS::RDS::DBInstance
Source:
Owner: AWS
SourceIdentifier: RDS_STORAGE_ENCRYPTED
Type: AWS::Config::ConfigRule
RestrictedIncomingTraffic:
Properties:
ConfigRuleName: restricted-common-ports
InputParameters:
blockedPort3:
Fn::If:
- restrictedIncomingTrafficParamBlockedPort3
- Ref: RestrictedIncomingTrafficParamBlockedPort3
- Ref: AWS::NoValue
Scope:
ComplianceResourceTypes:
- AWS::EC2::SecurityGroup
Source:
Owner: AWS
SourceIdentifier: RESTRICTED_INCOMING_TRAFFIC
Type: AWS::Config::ConfigRule
RootAccountMfaEnabled:
Properties:
ConfigRuleName: root-account-mfa-enabled
Source:
Owner: AWS
SourceIdentifier: ROOT_ACCOUNT_MFA_ENABLED
Type: AWS::Config::ConfigRule
S3AccountLevelPublicAccessBlocksPeriodic:
Properties:
ConfigRuleName: s3-account-level-public-access-blocks-periodic
InputParameters:
BlockPublicAcls:
Fn::If:
- s3AccountLevelPublicAccessBlocksPeriodicParamBlockPublicAcls
- Ref: S3AccountLevelPublicAccessBlocksPeriodicParamBlockPublicAcls
- Ref: AWS::NoValue
BlockPublicPolicy:
Fn::If:
- s3AccountLevelPublicAccessBlocksPeriodicParamBlockPublicPolicy
- Ref: S3AccountLevelPublicAccessBlocksPeriodicParamBlockPublicPolicy
- Ref: AWS::NoValue
IgnorePublicAcls:
Fn::If:
- s3AccountLevelPublicAccessBlocksPeriodicParamIgnorePublicAcls
- Ref: S3AccountLevelPublicAccessBlocksPeriodicParamIgnorePublicAcls
- Ref: AWS::NoValue
RestrictPublicBuckets:
Fn::If:
- s3AccountLevelPublicAccessBlocksPeriodicParamRestrictPublicBuckets
- Ref: S3AccountLevelPublicAccessBlocksPeriodicParamRestrictPublicBuckets
- Ref: AWS::NoValue
Source:
Owner: AWS
SourceIdentifier: S3_ACCOUNT_LEVEL_PUBLIC_ACCESS_BLOCKS_PERIODIC
Type: AWS::Config::ConfigRule
S3BucketLevelPublicAccessProhibited:
Properties:
ConfigRuleName: s3-bucket-level-public-access-prohibited
Scope:
ComplianceResourceTypes:
- AWS::S3::Bucket
Source:
Owner: AWS
SourceIdentifier: S3_BUCKET_LEVEL_PUBLIC_ACCESS_PROHIBITED
Type: AWS::Config::ConfigRule
S3BucketLoggingEnabled:
Properties:
ConfigRuleName: s3-bucket-logging-enabled
Scope:
ComplianceResourceTypes:
- AWS::S3::Bucket
Source:
Owner: AWS
SourceIdentifier: S3_BUCKET_LOGGING_ENABLED
Type: AWS::Config::ConfigRule
S3BucketPublicReadProhibited:
Properties:
ConfigRuleName: s3-bucket-public-read-prohibited
Scope:
ComplianceResourceTypes:
- AWS::S3::Bucket
Source:
Owner: AWS
SourceIdentifier: S3_BUCKET_PUBLIC_READ_PROHIBITED
Type: AWS::Config::ConfigRule
S3BucketPublicWriteProhibited:
Properties:
ConfigRuleName: s3-bucket-public-write-prohibited
Scope:
ComplianceResourceTypes:
- AWS::S3::Bucket
Source:
Owner: AWS
SourceIdentifier: S3_BUCKET_PUBLIC_WRITE_PROHIBITED
Type: AWS::Config::ConfigRule
S3BucketVersioningEnabled:
Properties:
ConfigRuleName: s3-bucket-versioning-enabled
InputParameters:
isMfaDeleteEnabled:
Fn::If:
- s3BucketVersioningEnabledParamIsMfaDeleteEnabled
- Ref: S3BucketVersioningEnabledParamIsMfaDeleteEnabled
- Ref: AWS::NoValue
Scope:
ComplianceResourceTypes:
- AWS::S3::Bucket
Source:
Owner: AWS
SourceIdentifier: S3_BUCKET_VERSIONING_ENABLED
Type: AWS::Config::ConfigRule
AccountContactDetailsConfigured:
Properties:
ConfigRuleName: account-contact-details-configured
Description: Ensure the contact email and telephone number for AWS accounts are current and map to more than one individual in your organization. Within the My Account section of the console ensure correct information is specified in the Contact Information section.
Source:
Owner: AWS
SourceIdentifier: AWS_CONFIG_PROCESS_CHECK
Type: AWS::Config::ConfigRule
AccountSecurityContactConfigured:
Properties:
ConfigRuleName: account-security-contact-configured
Description: Ensure the contact email and telephone number for the your organizations security team are current. Within the My Account section of the AWS Management Console ensure the correct information is specified in the Security section.
Source:
Owner: AWS
SourceIdentifier: AWS_CONFIG_PROCESS_CHECK
Type: AWS::Config::ConfigRule
AccountSecurityQuestionsConfigured:
Properties:
ConfigRuleName: account-security-questions-configured
Description: Ensure the security questions that can be used to authenticate individuals calling AWS customer service for support are configured. Within the My Account section of the AWS Management Console ensure three security challenge questions are configured.
Source:
Owner: AWS
SourceIdentifier: AWS_CONFIG_PROCESS_CHECK
Type: AWS::Config::ConfigRule
RootAccountRegularUse:
Properties:
ConfigRuleName: root-account-regular-use
Description: Ensure the use of the root account is avoided for everyday tasks. Within IAM, run a credential report to examine when the root user was last used.
Source:
Owner: AWS
SourceIdentifier: AWS_CONFIG_PROCESS_CHECK
Type: AWS::Config::ConfigRule
IAMUserConsoleAndAPIAccessAtCreation:
Properties:
ConfigRuleName: iam-user-console-and-api-access-at-creation
Description: Ensure access keys are not setup during the initial user setup for all IAM users that have a console password. For all IAM users with console access, compare the user 'Creation time` to the Access Key `Created` date.
Source:
Owner: AWS
SourceIdentifier: AWS_CONFIG_PROCESS_CHECK
Type: AWS::Config::ConfigRule
IAMUserSingleAccessKey:
Properties:
ConfigRuleName: iam-user-single-access-key
Description: Ensure there is only one active access key available for any single IAM user. For all IAM users check that there is only one active key used within the Security Credentials tab for each user within IAM.
Source:
Owner: AWS
SourceIdentifier: AWS_CONFIG_PROCESS_CHECK
Type: AWS::Config::ConfigRule
IAMExpiredCertificates:
Properties:
ConfigRuleName: iam-expired-certificates
Description: Ensure that all the expired SSL/TLS certificates stored in IAM are removed. From the command line with the installed AWS CLI run the 'aws iam list-server-certificates' command and determine if there are any expired server certificates.
Source:
Owner: AWS
SourceIdentifier: AWS_CONFIG_PROCESS_CHECK
Type: AWS::Config::ConfigRule
IAMAccessAnalyzerEnabled:
Properties:
ConfigRuleName: iam-access-analyzer-enabled
Description: Ensure that IAM Access analyzer is enabled. Within the IAM section of the console, select Access analyzer and ensure that the STATUS is set to Active.
Source:
Owner: AWS
SourceIdentifier: AWS_CONFIG_PROCESS_CHECK
Type: AWS::Config::ConfigRule
AlarmUnauthorizedAPIcalls:
Properties:
ConfigRuleName: alarm-unauthorized-api-calls
Description: Ensure a log metric filter and an alarm exists for unauthorized API calls.
Source:
Owner: AWS
SourceIdentifier: AWS_CONFIG_PROCESS_CHECK
Type: AWS::Config::ConfigRule
AlarmSignInWithoutMFA:
Properties:
ConfigRuleName: alarm-sign-in-without-mfa
Description: Ensure a log metric filter and an alarm exists for AWS Management Console sign-in without Multi-Factor Authentication (MFA).
Source:
Owner: AWS
SourceIdentifier: AWS_CONFIG_PROCESS_CHECK
Type: AWS::Config::ConfigRule
AlarmRootAccountUse:
Properties:
ConfigRuleName: alarm-root-account-use
Description: Ensure a log metric filter and an alarm exists for usage of the root account.
Source:
Owner: AWS
SourceIdentifier: AWS_CONFIG_PROCESS_CHECK
Type: AWS::Config::ConfigRule
AlarmIAMpolicyChange:
Properties:
ConfigRuleName: alarm-iam-policy-change
Description: Ensure a log metric filter and an alarm exists for IAM policy changes.
Source:
Owner: AWS
SourceIdentifier: AWS_CONFIG_PROCESS_CHECK
Type: AWS::Config::ConfigRule
AlarmCloudtrailConfigChange:
Properties:
ConfigRuleName: alarm-cloudtrail-config-change
Description: Ensure a log metric filter and an alarm exists for AWS CloudTrail configuration changes.
Source:
Owner: AWS
SourceIdentifier: AWS_CONFIG_PROCESS_CHECK
Type: AWS::Config::ConfigRule
AlarmS3BucketPolicyChange:
Properties:
ConfigRuleName: alarm-s3-bucket-policy-change
Description: Ensure a log metric filter and an alarm exists for Amazon S3 bucket policy changes.
Source:
Owner: AWS
SourceIdentifier: AWS_CONFIG_PROCESS_CHECK
Type: AWS::Config::ConfigRule
AlarmVPCNetworkGatewayChange:
Properties:
ConfigRuleName: alarm-vpc-network-gateway-change
Description: Ensure a log metric filter and an alarm exists for changes to network gateways.
Source:
Owner: AWS
SourceIdentifier: AWS_CONFIG_PROCESS_CHECK
Type: AWS::Config::ConfigRule
AlarmVPCroutetableChange:
Properties:
ConfigRuleName: alarm-vpc-route-table-change
Description: Ensure a log metric filter and an alarm exists for route table changes.
Source:
Owner: AWS
SourceIdentifier: AWS_CONFIG_PROCESS_CHECK
Type: AWS::Config::ConfigRule
AlarmVPCChange:
Properties:
ConfigRuleName: alarm-vpc-change
Description: Ensure a log metric filter and an alarm exists for Amazon Virtual Private Cloud (VPC) changes.
Source:
Owner: AWS
SourceIdentifier: AWS_CONFIG_PROCESS_CHECK
Type: AWS::Config::ConfigRule
AlarmOrganizationsChange:
Properties:
ConfigRuleName: alarm-organizations-change
Description: Ensure a log metric filter and an alarm exists for AWS Organizations changes.
Source:
Owner: AWS
SourceIdentifier: AWS_CONFIG_PROCESS_CHECK
Type: AWS::Config::ConfigRule
VPCNetworkACLOpenAdminPorts:
Properties:
ConfigRuleName: vpc-networkacl-open-admin-ports
Description: Ensure no network ACLs allow public ingress to the remote server administration ports. Within the VPC section of the console, ensure there are network ACLs with a source of '0.0.0.0/0' with allowing ports or port ranges including remote server admin ports.
Source:
Owner: AWS
SourceIdentifier: AWS_CONFIG_PROCESS_CHECK
Type: AWS::Config::ConfigRule
Conditions:
accessKeysRotatedParamMaxAccessKeyAge:
Fn::Not:
- Fn::Equals:
- ''
- Ref: AccessKeysRotatedParamMaxAccessKeyAge
iamPasswordPolicyParamMaxPasswordAge:
Fn::Not:
- Fn::Equals:
- ''
- Ref: IamPasswordPolicyParamMaxPasswordAge
iamPasswordPolicyParamMinimumPasswordLength:
Fn::Not:
- Fn::Equals:
- ''
- Ref: IamPasswordPolicyParamMinimumPasswordLength
iamPasswordPolicyParamPasswordReusePrevention:
Fn::Not:
- Fn::Equals:
- ''
- Ref: IamPasswordPolicyParamPasswordReusePrevention
iamPasswordPolicyParamRequireLowercaseCharacters:
Fn::Not:
- Fn::Equals:
- ''
- Ref: IamPasswordPolicyParamRequireLowercaseCharacters
iamPasswordPolicyParamRequireNumbers:
Fn::Not:
- Fn::Equals:
- ''
- Ref: IamPasswordPolicyParamRequireNumbers
iamPasswordPolicyParamRequireSymbols:
Fn::Not:
- Fn::Equals:
- ''
- Ref: IamPasswordPolicyParamRequireSymbols
iamPasswordPolicyParamRequireUppercaseCharacters:
Fn::Not:
- Fn::Equals:
- ''
- Ref: IamPasswordPolicyParamRequireUppercaseCharacters
iamPolicyInUseParamPolicyARN:
Fn::Not:
- Fn::Equals:
- ''
- Ref: IamPolicyInUseParamPolicyARN
iamUserUnusedCredentialsCheckParamMaxCredentialUsageAge:
Fn::Not:
- Fn::Equals:
- ''
- Ref: IamUserUnusedCredentialsCheckParamMaxCredentialUsageAge
restrictedIncomingTrafficParamBlockedPort3:
Fn::Not:
- Fn::Equals:
- ''
- Ref: RestrictedIncomingTrafficParamBlockedPort3
s3AccountLevelPublicAccessBlocksPeriodicParamBlockPublicAcls:
Fn::Not:
- Fn::Equals:
- ''
- Ref: S3AccountLevelPublicAccessBlocksPeriodicParamBlockPublicAcls
s3AccountLevelPublicAccessBlocksPeriodicParamBlockPublicPolicy:
Fn::Not:
- Fn::Equals:
- ''
- Ref: S3AccountLevelPublicAccessBlocksPeriodicParamBlockPublicPolicy
s3AccountLevelPublicAccessBlocksPeriodicParamIgnorePublicAcls:
Fn::Not:
- Fn::Equals:
- ''
- Ref: S3AccountLevelPublicAccessBlocksPeriodicParamIgnorePublicAcls
s3AccountLevelPublicAccessBlocksPeriodicParamRestrictPublicBuckets:
Fn::Not:
- Fn::Equals:
- ''
- Ref: S3AccountLevelPublicAccessBlocksPeriodicParamRestrictPublicBuckets
s3BucketVersioningEnabledParamIsMfaDeleteEnabled:
Fn::Not:
- Fn::Equals:
- ''
- Ref: S3BucketVersioningEnabledParamIsMfaDeleteEnabled
@@ -0,0 +1,25 @@
# Overview
This module performs the following tasks:
- Enable AWS config in all regions
- Deploy [CIS1.4 level 1 conformance pack](https://docs.aws.amazon.com/config/latest/developerguide/operational-best-practices-for-cis_aws_benchmark_level_1.html). Rules file Cis14Level1.yaml is downloaded from https://raw.githubusercontent.com/awslabs/aws-config-rules/master/aws-config-conformance-packs/Operational-Best-Practices-for-CIS-AWS-v1.4-Level1.yaml
- Set Config retention period
- Setup Config aggregator, aggregate Config in all regions into primary region
- Create s3 bucket for config use
## Inputs:
| Name | Description | Type | Default | Required |
|--------------------|-------------------------------------------------------------|------|---------|:-----:|
| application | name of application | string | none | yes |
| environment | capacity of environment (prd/dev/lab) | string | none | yes |
| customer-name | owner of aws resources | string | none | yes |
| project | name of project | string | none | yes |
| default-tags | tags to be added to resources | list | none | yes |
| aws-region-short | short name of aws region (e.g. apne1) | string | none | yes |
| primary-aws-region | name of primary region where global events will be recorded | string | none | yes |
# Notes
- It takes a while for AWS to process Config changes.
- [AWS managed config rules](https://docs.aws.amazon.com/config/latest/developerguide/managed-rules-by-aws-config.html) are automatically applied. Those rule may duplicate with Cis1.4.
@@ -0,0 +1,122 @@
// Config rules from asecure.cloud https://asecure.cloud/p/monitoring_cis_benchmark/
// Conformance pack has not been made available to terraform https://github.com/hashicorp/terraform-provider-aws/issues/11098
resource "aws_config_config_rule" "ConfigRule1" {
name = "mfa-enabled-for-iam-console-access"
description = "A Config rule that checks whether AWS Multi-Factor Authentication (MFA) is enabled for all AWS Identity and Access Management (IAM) users that use a console password. The rule is COMPLIANT if MFA is enabled."
source {
owner = "AWS"
source_identifier = "MFA_ENABLED_FOR_IAM_CONSOLE_ACCESS"
}
scope {
compliance_resource_types = []
}
}
resource "aws_config_config_rule" "ConfigRule2" {
name = "iam-user-unused-credentials-check"
description = "A config rule that checks whether your AWS Identity and Access Management (IAM) users have passwords or active access keys that have not been used within the specified number of days you provided. Re-evaluating this rule within 4 hours of the first eva..."
input_parameters = "{\"maxCredentialUsageAge\":\"90\"}"
source {
owner = "AWS"
source_identifier = "IAM_USER_UNUSED_CREDENTIALS_CHECK"
}
scope {
compliance_resource_types = []
}
}
resource "aws_config_config_rule" "ConfigRule3" {
name = "access-keys-rotated"
description = "A config rule that checks whether the active access keys are rotated within the number of days specified in maxAccessKeyAge. The rule is NON_COMPLIANT if the access keys have not been rotated for more than maxAccessKeyAge number of days."
input_parameters = "{\"maxAccessKeyAge\":\"90\"}"
source {
owner = "AWS"
source_identifier = "ACCESS_KEYS_ROTATED"
}
scope {
compliance_resource_types = []
}
}
resource "aws_config_config_rule" "ConfigRule4" {
name = "iam-password-policy"
description = "A Config rule that checks whether the account password policy for IAM users meets the specified requirements."
input_parameters = "{\"RequireUppercaseCharacters\":\"true\",\"RequireLowercaseCharacters\":\"true\",\"RequireSymbols\":\"true\",\"RequireNumbers\":\"true\",\"MinimumPasswordLength\":\"14\",\"PasswordReusePrevention\":\"24\",\"MaxPasswordAge\":\"90\"}"
source {
owner = "AWS"
source_identifier = "IAM_PASSWORD_POLICY"
}
scope {
compliance_resource_types = []
}
}
resource "aws_config_config_rule" "ConfigRule5" {
name = "iam-root-access-key-check"
description = "A config rule that checks whether the root user access key is available. The rule is COMPLIANT if the user access key does not exist."
source {
owner = "AWS"
source_identifier = "IAM_ROOT_ACCESS_KEY_CHECK"
}
scope {
compliance_resource_types = []
}
}
resource "aws_config_config_rule" "ConfigRule6" {
name = "root-account-mfa-enabled"
description = "A Config rule that checks whether users of your AWS account require a multi-factor authentication (MFA) device to sign in with root credentials."
source {
owner = "AWS"
source_identifier = "ROOT_ACCOUNT_MFA_ENABLED"
}
scope {
compliance_resource_types = []
}
}
resource "aws_config_config_rule" "ConfigRule7" {
name = "root-account-hardware-mfa-enabled"
description = "A config rule that checks whether your AWS account is enabled to use multi-factor authentication (MFA) hardware device to sign in with root credentials. The rule is NON_COMPLIANT if any virtual MFA devices are permitted for signing in with root credent..."
source {
owner = "AWS"
source_identifier = "ROOT_ACCOUNT_HARDWARE_MFA_ENABLED"
}
scope {
compliance_resource_types = []
}
}
resource "aws_config_config_rule" "ConfigRule8" {
name = "iam-user-no-policies-check"
description = "A Config rule that checks that none of your IAM users have policies attached. IAM users must inherit permissions from IAM groups or roles."
source {
owner = "AWS"
source_identifier = "IAM_USER_NO_POLICIES_CHECK"
}
scope {
compliance_resource_types = ["AWS::IAM::User"]
}
}
resource "aws_config_config_rule" "ConfigRule11" {
name = "iam-policy-no-statements-with-admin-access"
description = "A config rule that checks whether the default version of AWS Identity and Access Management (IAM) policies do not have administrator access. If any statement has 'Effect': 'Allow' with 'Action': '*' over 'Resource': '*', the rule is NON_COMPLIANT."
source {
owner = "AWS"
source_identifier = "IAM_POLICY_NO_STATEMENTS_WITH_ADMIN_ACCESS"
}
scope {
compliance_resource_types = ["AWS::IAM::Policy"]
}
}
@@ -0,0 +1,154 @@
/*
AWS Config Service
If config is already enabled, import it with
terraform import aws_config_configuration_recorder.config-recorder default
*/
data aws_caller_identity this {}
data aws_regions all-regions {}
resource "aws_iam_service_linked_role" "config" {
aws_service_name = "config.amazonaws.com"
}
resource null_resource cli-resource-awsconfig {
for_each = data.aws_regions.all-regions.names
provisioner "local-exec" {
when = create
command = <<-EOD
aws configservice --region ${each.value} put-configuration-recorder --configuration-recorder name=default,roleARN="${aws_iam_service_linked_role.config.arn}" --recording-group allSupported=true,includeGlobalResourceTypes=false
aws configservice --region ${each.value} put-delivery-channel --delivery-channel name=default,s3BucketName=${module.config-bucket.bucket-name},configSnapshotDeliveryProperties={deliveryFrequency=Twelve_Hours}
aws configservice --region ${each.value} put-retention-configuration --retention-period-in-days ${var.config-retention-days}
aws configservice --region ${each.value} put-conformance-pack --conformance-pack-name Cis14Level1 --template-body file://Cis14Level1.yaml
aws configservice --region ${each.value} start-configuration-recorder --configuration-recorder-name default
if [ \"${each.value}\" == \"${var.primary-aws-region}\" ]; then
aws configservice --region ${each.value} put-configuration-recorder --configuration-recorder name=default,roleARN="${aws_iam_service_linked_role.config.arn}" --recording-group allSupported=true,includeGlobalResourceTypes=true
fi
EOD
}
// Destroy provisioner does not accept variables. Workaround is to delete recorder in all regions.
provisioner "local-exec" {
when = destroy
on_failure = continue
command = <<-EOD
aws ec2 describe-regions | jq -cr .Regions[].RegionName | while read r; do
aws configservice --region $r describe-configuration-recorders --output text | while read dummy; do
aws configservice --region $r stop-configuration-recorder --configuration-recorder-name default
aws configservice --region $r delete-delivery-channel --delivery-channel-name default
aws configservice --region $r delete-configuration-recorder --configuration-recorder-name default
done
done
EOD
}
}
resource "aws_config_configuration_aggregator" "config-aggregator" {
depends_on = [null_resource.cli-resource-awsconfig]
name = "ConfigAggregator"
account_aggregation_source {
account_ids = [data.aws_caller_identity.this.id]
all_regions = true
}
}
/*
resource "aws_config_configuration_recorder" "config-recorder" {
name = "${local.resource-prefix}-awsconfig"
role_arn = aws_iam_service_linked_role.config.arn
recording_group {
all_supported = true
include_global_resource_types = true
}
}
resource "aws_config_delivery_channel" "config-delivery-channel" {
name = "${local.resource-prefix}-configdeliverychannel"
s3_bucket_name = module.config-bucket.bucket-name
depends_on = [aws_config_configuration_recorder.config-recorder]
}
resource "aws_config_configuration_recorder_status" "main" {
name = aws_config_configuration_recorder.config-recorder.name
is_enabled = true
depends_on = [aws_config_delivery_channel.config-delivery-channel]
}
*/
######## Config Bucket - Policy ########
module config-bucket {
source = "../../storage/infra-s3-bucket"
bucket-name = "${var.resource-prefix}-configbucket-${data.aws_caller_identity.this.account_id}"
add-random-suffix = false
bucket-policy-json = data.aws_iam_policy_document.config_bucket_policy.json
default-tags = var.default-tags
}
data "aws_iam_policy_document" "config_bucket_policy" {
statement {
sid = "AWSConfigBucketPermissionsCheck"
principals {
type = "Service"
identifiers = ["config.amazonaws.com"]
}
actions = [
"s3:GetBucketAcl",
]
resources = [
"arn:aws:s3:::${var.resource-prefix}-configbucket-${data.aws_caller_identity.this.account_id}",
]
}
statement {
sid = "AWSConfigBucketExistenceCheck"
principals {
type = "Service"
identifiers = ["config.amazonaws.com"]
}
actions = [
"s3:ListBucket",
]
resources = [
"arn:aws:s3:::${var.resource-prefix}-configbucket-${data.aws_caller_identity.this.account_id}",
]
}
statement {
sid = "AWSConfigBucketDelivery"
principals {
type = "Service"
identifiers = ["config.amazonaws.com"]
}
actions = [
"s3:PutObject",
]
resources = [
"arn:aws:s3:::${var.resource-prefix}-configbucket-${data.aws_caller_identity.this.account_id}/*",
]
condition {
test = "StringEquals"
variable = "s3:x-amz-acl"
values = [
"bucket-owner-full-control",
]
}
}
}
@@ -0,0 +1,9 @@
terraform {
required_version = "~> 1.2.5"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 3.75.2"
}
}
}
@@ -0,0 +1,9 @@
variable "default-tags" {}
variable resource-prefix {}
variable config-retention-days {
type = number
default = 365
}
variable primary-aws-region {}
@@ -0,0 +1,21 @@
# Overview
This module performs the following tasks:
- Create KMS key for cloudtrail and CWL encryption
- Create s3 bucket for cloudtrail use
- Create cloudtrail
- Create cloudwatch log group for cloudtrail
- Create cloudwatch metric filter for CIS1.1
- Create cloudwatch alarm for CIS1.1
## Inputs:
| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:-----:|
| application | name of application | string | none | yes |
| environment | capacity of environment (prd/dev/lab) | string | none | yes |
| customer-name | owner of aws resources | string | none | yes |
| project | name of project | string | none | yes |
| default-tags | tags to be added to resources | list | none | yes |
| cloudtrail-retain-days | Days before cloudtrail logs are expired on s3 | number | 90 | yes |
| aws-region-short | short name of aws region (e.g. apne1) | string | none | yes |
@@ -0,0 +1,80 @@
resource "aws_iam_role" "iam_cloudtrial_cloudwatch_role" {
name = "${var.resource-prefix}-cwl-role"
assume_role_policy = data.aws_iam_policy_document.ct-role-assumerole-policy.json
description = "Enables AWS CloudTrail to deliver log to CloudWatch log"
tags = var.default-tags
}
resource "aws_iam_role_policy" "iam_cloudtrial_cloudwatach_role_policy" {
name = "${var.resource-prefix}-cwl-role-policy"
role = aws_iam_role.iam_cloudtrial_cloudwatch_role.id
policy = data.aws_iam_policy_document.ct-role-pdoc.json
}
data "aws_iam_policy_document" "ct-role-assumerole-policy" {
statement {
effect = "Allow"
actions = ["sts:AssumeRole"]
principals {
type = "Service"
identifiers = ["cloudtrail.amazonaws.com"]
}
}
}
data "aws_iam_policy_document" "ct-role-pdoc" {
statement {
effect = "Allow"
actions = ["logs:CreateLogStream"]
resources = [
"${aws_cloudwatch_log_group.ct-cwl.arn}:log-stream:*",
]
}
statement {
effect = "Allow"
actions = ["logs:PutLogEvents"]
resources = [
"${aws_cloudwatch_log_group.ct-cwl.arn}:log-stream:*",
]
}
}
resource "aws_cloudtrail" "default" {
name = "${var.resource-prefix}-trail-001"
enable_logging = true
s3_bucket_name = local.ct-bucket-name
enable_log_file_validation = true
is_multi_region_trail = true
include_global_service_events = true
cloud_watch_logs_role_arn = aws_iam_role.iam_cloudtrial_cloudwatch_role.arn
cloud_watch_logs_group_arn = "${aws_cloudwatch_log_group.ct-cwl.arn}:*"
tags = var.default-tags
kms_key_id = aws_kms_key.ctbucket-key.arn
is_organization_trail = false
event_selector {
read_write_type = "All"
include_management_events = true
data_resource {
type = "AWS::S3::Object"
values = ["arn:aws:s3:::"]
}
data_resource {
type = "AWS::Lambda::Function"
values = ["arn:aws:lambda"]
}
}
#insight_selector {
# insight_type = "ApiCallRateInsight"
#}
}
@@ -0,0 +1,69 @@
resource "aws_kms_key" "ctbucket-key" {
deletion_window_in_days = 7
tags = var.default-tags
policy = data.aws_iam_policy_document.key-policy.json
enable_key_rotation = true
}
resource "aws_kms_alias" ctbucket-key-aliaas {
name = "alias/${var.resource-prefix}-kmskey-default"
target_key_id = aws_kms_key.ctbucket-key.key_id
}
# https://gist.github.com/shortjared/4c1e3fe52bdfa47522cfe5b41e5d6f22
data "aws_iam_policy_document" "key-policy" {
statement {
sid = "Key usage by aws services"
principals {
identifiers = [
"autoscaling.amazonaws.com",
"cloudtrail.amazonaws.com",
"eks.amazonaws.com",
"eks-nodegroup.amazonaws.com",
"guardduty.amazonaws.com",
"delivery.logs.amazonaws.com",
"sns.amazonaws.com",
"sqs.amazonaws.com",
"lambda.amazonaws.com",
"backup.amazonaws.com",
"events.amazonaws.com",
"cloudwatch.amazonaws.com",
"s3.amazonaws.com",
"logs.amazonaws.com"
]
type = "Service"
}
actions = [
"kms:Encrypt",
"kms:Decrypt",
"kms:ReEncrypt*",
"kms:GenerateDataKey*",
"kms:DescribeKey"
]
resources = [
"*"
]
effect = "Allow"
}
statement {
sid = "Key administrator"
actions = [
"kms:*"
]
resources = [
"*"
]
principals {
type = "AWS"
identifiers = [data.aws_caller_identity.this.account_id]
}
effect = "Allow"
}
}
@@ -0,0 +1,64 @@
data "aws_iam_policy_document" "cloudtrail_bucket_policy" {
statement {
sid = "AWSCloudTrailAclCheck"
principals {
type = "Service"
identifiers = ["cloudtrail.amazonaws.com"]
}
actions = [
"s3:GetBucketAcl",
]
resources = [
"arn:aws:s3:::${local.ct-bucket-name}",
]
}
statement {
sid = "AWSCloudTrailWrite"
principals {
type = "Service"
identifiers = ["config.amazonaws.com", "cloudtrail.amazonaws.com"]
}
actions = [
"s3:PutObject"
]
resources = [
"arn:aws:s3:::${local.ct-bucket-name}/*"
]
}
statement {
sid = "ReadAccessForAccountOwner"
principals {
type = "AWS"
identifiers = [data.aws_caller_identity.this.account_id]
}
actions = [
"s3:Get*"
]
resources = [
"arn:aws:s3:::${local.ct-bucket-name}",
"arn:aws:s3:::${local.ct-bucket-name}/*"
]
}
}
module ct-bucket {
source = "../../storage/infra-s3-bucket"
bucket-name = local.ct-bucket-name
bucket-policy-json = data.aws_iam_policy_document.cloudtrail_bucket_policy.json
default-tags = var.default-tags
}
@@ -0,0 +1,377 @@
resource "aws_cloudwatch_log_group" "ct-cwl" {
name_prefix = "cloudtrail/"
retention_in_days = var.cloudtrail-retain-days
kms_key_id = aws_kms_key.ctbucket-key.arn
tags = var.default-tags
}
resource "aws_cloudwatch_log_metric_filter" "cwl-metric-filter-cis11" {
name = "cis11-rootaccess-filter"
pattern = <<EOT
{$.userIdentity.type="Root" && $.userIdentity.invokedBy NOT EXISTS && $.eventType !="AwsServiceEvent"}
EOT
log_group_name = aws_cloudwatch_log_group.ct-cwl.name
metric_transformation {
name = "cis11-rootaccess-metric"
namespace = "LogMetrics"
value = "1"
}
}
resource "aws_cloudwatch_metric_alarm" "cis11-rootaccess-alarm" {
alarm_name = "cis11-rootaccess-alarm"
comparison_operator = "GreaterThanOrEqualToThreshold"
evaluation_periods = "1"
metric_name = "cis11-rootaccess-metric"
namespace = "LogMetrics"
period = "300"
statistic = "Average"
threshold = "1"
alarm_description = "Root access is detected from cloudtrail"
treat_missing_data = "notBreaching"
// alarm_actions = []
}
// CIS 3.x benchmark from asecure.cloud https://asecure.cloud/p/monitoring_cis_benchmark/
resource "aws_cloudwatch_metric_alarm" "CwAlarm2" {
alarm_name = "cis-unauthorized_api_calls"
alarm_description = "A CloudWatch Alarm that triggers if Multiple unauthorized actions or logins attempted."
metric_name = "UnauthorizedAttemptCount"
namespace = "CloudTrailMetrics"
statistic = "Sum"
period = "60"
threshold = "1"
evaluation_periods = "1"
comparison_operator = "GreaterThanOrEqualToThreshold"
// alarm_actions = [""]
treat_missing_data = "notBreaching"
}
resource "aws_cloudwatch_log_metric_filter" "MetricFilter2" {
log_group_name = aws_cloudwatch_log_group.ct-cwl.name
pattern = "{ ($.errorCode = \"*UnauthorizedOperation\") || ($.errorCode = \"AccessDenied*\") }"
name = "UnauthorizedAttemptCount"
metric_transformation {
name = "UnauthorizedAttemptCount"
value = "1"
namespace = "CloudTrailMetrics"
}
}
resource "aws_cloudwatch_metric_alarm" "CwAlarm3" {
alarm_name = "cis-no_mfa_console_logins"
alarm_description = "A CloudWatch Alarm that triggers if there is a Management Console sign-in without MFA."
metric_name = "ConsoleSigninWithoutMFA"
namespace = "CloudTrailMetrics"
statistic = "Sum"
period = "60"
threshold = "1"
evaluation_periods = "1"
comparison_operator = "GreaterThanOrEqualToThreshold"
// alarm_actions = [""]
treat_missing_data = "notBreaching"
}
resource "aws_cloudwatch_log_metric_filter" "MetricFilter3" {
log_group_name = aws_cloudwatch_log_group.ct-cwl.name
pattern = "{($.eventName = \"ConsoleLogin\") && ($.additionalEventData.MFAUsed != \"Yes\") && ($.responseElements.ConsoleLogin != \"Failure\") && ($.additionalEventData.SamlProviderArn NOT EXISTS) }"
name = "ConsoleSigninWithoutMFA"
metric_transformation {
name = "ConsoleSigninWithoutMFA"
value = "1"
namespace = "CloudTrailMetrics"
}
}
resource "aws_cloudwatch_metric_alarm" "CwAlarm4" {
alarm_name = "cis-iam_policy_changes"
alarm_description = "A CloudWatch Alarm that triggers when changes are made to IAM policies. Events include IAM policy creation/deletion/update operations as well as attaching/detaching policies from IAM users, roles or groups."
metric_name = "IAMPolicyEventCount"
namespace = "CloudTrailMetrics"
statistic = "Sum"
period = "300"
threshold = "1"
evaluation_periods = "1"
comparison_operator = "GreaterThanOrEqualToThreshold"
// alarm_actions = [""]
treat_missing_data = "notBreaching"
}
resource "aws_cloudwatch_log_metric_filter" "MetricFilter4" {
log_group_name = aws_cloudwatch_log_group.ct-cwl.name
pattern = "{($.eventName=DeleteGroupPolicy)||($.eventName=DeleteRolePolicy)||($.eventName=DeleteUserPolicy)||($.eventName=PutGroupPolicy)||($.eventName=PutRolePolicy)||($.eventName=PutUserPolicy)||($.eventName=CreatePolicy)||($.eventName=DeletePolicy)||($.eventName=CreatePolicyVersion)||($.eventName=DeletePolicyVersion)||($.eventName=AttachRolePolicy)||($.eventName=DetachRolePolicy)||($.eventName=AttachUserPolicy)||($.eventName=DetachUserPolicy)||($.eventName=AttachGroupPolicy)||($.eventName=DetachGroupPolicy)}"
name = "IAMPolicyEventCount"
metric_transformation {
name = "IAMPolicyEventCount"
value = "1"
namespace = "CloudTrailMetrics"
}
}
resource "aws_cloudwatch_metric_alarm" "CwAlarm5" {
alarm_name = "cis-cloudtrail_changes"
alarm_description = "A CloudWatch Alarm that triggers when changes are made to CloudTrail."
metric_name = "CloudTrailEventCount"
namespace = "CloudTrailMetrics"
statistic = "Sum"
period = "300"
threshold = "1"
evaluation_periods = "1"
comparison_operator = "GreaterThanOrEqualToThreshold"
// alarm_actions = [""]
treat_missing_data = "notBreaching"
}
resource "aws_cloudwatch_log_metric_filter" "MetricFilter5" {
log_group_name = aws_cloudwatch_log_group.ct-cwl.name
pattern = "{ ($.eventName = CreateTrail) || ($.eventName = UpdateTrail) || ($.eventName = DeleteTrail) || ($.eventName = StartLogging) || ($.eventName = StopLogging) }"
name = "CloudTrailEventCount"
metric_transformation {
name = "CloudTrailEventCount"
value = "1"
namespace = "CloudTrailMetrics"
}
}
resource "aws_cloudwatch_metric_alarm" "CwAlarm6" {
alarm_name = "cis-failed_console_logins"
alarm_description = "A CloudWatch Alarm that triggers if there are AWS Management Console authentication failures."
metric_name = "ConsoleLoginFailures"
namespace = "CloudTrailMetrics"
statistic = "Sum"
period = "300"
threshold = "1"
evaluation_periods = "1"
comparison_operator = "GreaterThanOrEqualToThreshold"
// alarm_actions = [""]
treat_missing_data = "notBreaching"
}
resource "aws_cloudwatch_log_metric_filter" "MetricFilter6" {
log_group_name = aws_cloudwatch_log_group.ct-cwl.name
pattern = "{ ($.eventName = ConsoleLogin) && ($.errorMessage = \"Failed authentication\") }"
name = "ConsoleLoginFailures"
metric_transformation {
name = "ConsoleLoginFailures"
value = "1"
namespace = "CloudTrailMetrics"
}
}
resource "aws_cloudwatch_metric_alarm" "CwAlarm7" {
alarm_name = "cis-disabled_deleted_cmks"
alarm_description = "A CloudWatch Alarm that triggers if customer created CMKs get disabled or scheduled for deletion."
metric_name = "KMSCustomerKeyDeletion"
namespace = "CloudTrailMetrics"
statistic = "Sum"
period = "60"
threshold = "1"
evaluation_periods = "1"
comparison_operator = "GreaterThanOrEqualToThreshold"
// alarm_actions = [""]
treat_missing_data = "notBreaching"
}
resource "aws_cloudwatch_log_metric_filter" "MetricFilter7" {
log_group_name = aws_cloudwatch_log_group.ct-cwl.name
pattern = "{ ($.eventSource = kms.amazonaws.com) && (($.eventName=DisableKey) || ($.eventName=ScheduleKeyDeletion)) }"
name = "KMSCustomerKeyDeletion"
metric_transformation {
name = "KMSCustomerKeyDeletion"
value = "1"
namespace = "CloudTrailMetrics"
}
}
resource "aws_cloudwatch_metric_alarm" "CwAlarm8" {
alarm_name = "cis-s3_changes"
alarm_description = "A CloudWatch Alarm that triggers when changes are made to an S3 Bucket."
metric_name = "S3BucketActivityEventCount"
namespace = "CloudTrailMetrics"
statistic = "Sum"
period = "300"
threshold = "1"
evaluation_periods = "1"
comparison_operator = "GreaterThanOrEqualToThreshold"
// alarm_actions = [""]
treat_missing_data = "notBreaching"
}
resource "aws_cloudwatch_log_metric_filter" "MetricFilter8" {
log_group_name = aws_cloudwatch_log_group.ct-cwl.name
pattern = "{ ($.eventSource = s3.amazonaws.com) && (($.eventName = PutBucketAcl) || ($.eventName = PutBucketPolicy) || ($.eventName = PutBucketCors) || ($.eventName = PutBucketLifecycle) || ($.eventName = PutBucketReplication) || ($.eventName = DeleteBucketPolicy) || ($.eventName = DeleteBucketCors) || ($.eventName = DeleteBucketLifecycle) || ($.eventName = DeleteBucketReplication)) }"
name = "S3BucketActivityEventCount"
metric_transformation {
name = "S3BucketActivityEventCount"
value = "1"
namespace = "CloudTrailMetrics"
}
}
resource "aws_cloudwatch_metric_alarm" "CwAlarm9" {
alarm_name = "cis-config_changes"
alarm_description = "A CloudWatch Alarm that triggers when changes are made to AWS Config."
metric_name = "CloudTrailEventCount"
namespace = "CloudTrailMetrics"
statistic = "Sum"
period = "300"
threshold = "1"
evaluation_periods = "1"
comparison_operator = "GreaterThanOrEqualToThreshold"
// alarm_actions = [""]
treat_missing_data = "notBreaching"
}
resource "aws_cloudwatch_log_metric_filter" "MetricFilter9" {
log_group_name = aws_cloudwatch_log_group.ct-cwl.name
pattern = "{ ($.eventName = PutConfigurationRecorder) || ($.eventName = StopConfigurationRecorder) || ($.eventName = DeleteDeliveryChannel) || ($.eventName = PutDeliveryChannel) }"
name = "CloudTrailEventCount"
metric_transformation {
name = "CloudTrailEventCount"
value = "1"
namespace = "CloudTrailMetrics"
}
}
resource "aws_cloudwatch_metric_alarm" "CwAlarm10" {
alarm_name = "cis-securitygroup_changes"
alarm_description = "A CloudWatch Alarm that triggers when changes are made to Security Groups."
metric_name = "SecurityGroupEventCount"
namespace = "CloudTrailMetrics"
statistic = "Sum"
period = "300"
threshold = "1"
evaluation_periods = "1"
comparison_operator = "GreaterThanOrEqualToThreshold"
// alarm_actions = [""]
treat_missing_data = "notBreaching"
}
resource "aws_cloudwatch_log_metric_filter" "MetricFilter10" {
log_group_name = aws_cloudwatch_log_group.ct-cwl.name
pattern = "{ ($.eventName = AuthorizeSecurityGroupIngress) || ($.eventName = AuthorizeSecurityGroupEgress) || ($.eventName = RevokeSecurityGroupIngress) || ($.eventName = RevokeSecurityGroupEgress) || ($.eventName = CreateSecurityGroup) || ($.eventName = DeleteSecurityGroup) }"
name = "SecurityGroupEventCount"
metric_transformation {
name = "SecurityGroupEventCount"
value = "1"
namespace = "CloudTrailMetrics"
}
}
resource "aws_cloudwatch_metric_alarm" "CwAlarm11" {
alarm_name = "cis-nacl_changes"
alarm_description = "A CloudWatch Alarm that triggers when changes are made to Network ACLs."
metric_name = "NetworkAclEventCount"
namespace = "CloudTrailMetrics"
statistic = "Sum"
period = "300"
threshold = "1"
evaluation_periods = "1"
comparison_operator = "GreaterThanOrEqualToThreshold"
// alarm_actions = [""]
treat_missing_data = "notBreaching"
}
resource "aws_cloudwatch_log_metric_filter" "MetricFilter11" {
log_group_name = aws_cloudwatch_log_group.ct-cwl.name
pattern = "{ ($.eventName = CreateNetworkAcl) || ($.eventName = CreateNetworkAclEntry) || ($.eventName = DeleteNetworkAcl) || ($.eventName = DeleteNetworkAclEntry) || ($.eventName = ReplaceNetworkAclEntry) || ($.eventName = ReplaceNetworkAclAssociation) }"
name = "NetworkAclEventCount"
metric_transformation {
name = "NetworkAclEventCount"
value = "1"
namespace = "CloudTrailMetrics"
}
}
resource "aws_cloudwatch_metric_alarm" "CwAlarm12" {
alarm_name = "cis-igw_changes"
alarm_description = "A CloudWatch Alarm that triggers when changes are made to an Internet Gateway in a VPC."
metric_name = "GatewayEventCount"
namespace = "CloudTrailMetrics"
statistic = "Sum"
period = "300"
threshold = "1"
evaluation_periods = "1"
comparison_operator = "GreaterThanOrEqualToThreshold"
// alarm_actions = [""]
treat_missing_data = "notBreaching"
}
resource "aws_cloudwatch_log_metric_filter" "MetricFilter12" {
log_group_name = aws_cloudwatch_log_group.ct-cwl.name
pattern = "{ ($.eventName = CreateCustomerGateway) || ($.eventName = DeleteCustomerGateway) || ($.eventName = AttachInternetGateway) || ($.eventName = CreateInternetGateway) || ($.eventName = DeleteInternetGateway) || ($.eventName = DetachInternetGateway) }"
name = "GatewayEventCount"
metric_transformation {
name = "GatewayEventCount"
value = "1"
namespace = "CloudTrailMetrics"
}
}
resource "aws_cloudwatch_metric_alarm" "CwAlarm13" {
alarm_name = "cis-vpc_routetable_changes"
alarm_description = "A CloudWatch Alarm that triggers when changes are made to a VPC's Route Table."
metric_name = "VpcRouteTableEventCount"
namespace = "CloudTrailMetrics"
statistic = "Sum"
period = "300"
threshold = "1"
evaluation_periods = "1"
comparison_operator = "GreaterThanOrEqualToThreshold"
// alarm_actions = [""]
treat_missing_data = "notBreaching"
}
resource "aws_cloudwatch_log_metric_filter" "MetricFilter13" {
log_group_name = aws_cloudwatch_log_group.ct-cwl.name
pattern = "{ ($.eventName = AssociateRouteTable) || ($.eventName = CreateRoute) || ($.eventName = CreateRouteTable) || ($.eventName = DeleteRoute) || ($.eventName = DeleteRouteTable) || ($.eventName = ReplaceRoute) || ($.eventName = ReplaceRouteTableAssociation) || ($.eventName = DisassociateRouteTable) }"
name = "VpcRouteTableEventCount"
metric_transformation {
name = "VpcRouteTableEventCount"
value = "1"
namespace = "CloudTrailMetrics"
}
}
resource "aws_cloudwatch_metric_alarm" "CwAlarm14" {
alarm_name = "cis-vpc_changes"
alarm_description = "A CloudWatch Alarm that triggers when changes are made to a VPC."
metric_name = "VpcEventCount"
namespace = "CloudTrailMetrics"
statistic = "Sum"
period = "300"
threshold = "1"
evaluation_periods = "1"
comparison_operator = "GreaterThanOrEqualToThreshold"
// alarm_actions = [""]
treat_missing_data = "notBreaching"
}
resource "aws_cloudwatch_log_metric_filter" "MetricFilter14" {
log_group_name = aws_cloudwatch_log_group.ct-cwl.name
pattern = "{ ($.eventName = CreateVpc) || ($.eventName = DeleteVpc) || ($.eventName = ModifyVpcAttribute) || ($.eventName = AcceptVpcPeeringConnection) || ($.eventName = CreateVpcPeeringConnection) || ($.eventName = DeleteVpcPeeringConnection) || ($.eventName = RejectVpcPeeringConnection) || ($.eventName = AttachClassicLinkVpc) || ($.eventName = DetachClassicLinkVpc) || ($.eventName = DisableVpcClassicLink) || ($.eventName = EnableVpcClassicLink) }"
name = "VpcEventCount"
metric_transformation {
name = "VpcEventCount"
value = "1"
namespace = "CloudTrailMetrics"
}
}
@@ -0,0 +1 @@
data "aws_caller_identity" "this" {}
@@ -0,0 +1,19 @@
/*
variable "customer-name" {}
variable "environment" {}
variable "project" {}
variable "application" {}
*/
variable resource-prefix {}
variable "default-tags" {}
variable "cloudtrail-retain-days" {
type = number
default = 90
}
data aws_region this-region {}
locals {
ct-bucket-name = "${var.resource-prefix}-ctbucket-${data.aws_caller_identity.this.account_id}"
}
@@ -0,0 +1,17 @@
resource "aws_directory_service_directory" "connector" {
name = var.adc-domainname
enable_sso = false # enabling this results in error when terraform is ran in member accounts
password = var.adc-service-account-password
size = var.adc-size
type = "ADConnector"
description = "ADConnector"
tags = var.default-tags
connect_settings {
customer_dns_ips = var.adc-dns-ips
customer_username = var.adc-service-account-username
subnet_ids = var.adc-subnet-ids
vpc_id = var.adc-vpc-id
}
}
@@ -0,0 +1,11 @@
output directory-id {
value = aws_directory_service_directory.connector.id
}
output security-group-id {
value = aws_directory_service_directory.connector.security_group_id
}
output customer-dns-ip {
value = flatten(aws_directory_service_directory.connector.connect_settings[*].customer_dns_ips)
}
@@ -0,0 +1,8 @@
variable "adc-domainname" {}
variable "adc-service-account-password" {}
variable "adc-size" {}
variable "adc-dns-ips" {}
variable "adc-service-account-username" {}
variable "adc-subnet-ids" {}
variable "adc-vpc-id" {}
variable "default-tags" {}
@@ -0,0 +1,13 @@
# five-deployer-roles
This module create IAM roles for use with IAC execution. 5 roles are created and each role has permissions to perform
different tasks. The 5 roles are:
* NetworkDeployer: Role with access to manage network related resources
* SecurityDeployer: Role with access to manage IAM related resources
* DatabaseDeployer: Role with access to manage database related resources
* StorageDeployer: Role with access to manage storage related resources
* CommonDeployer: Role with access to manage all resources, excluding access granted to the 4 other roles
# Changelog
* 20230313: Initial release
* 20230929: Added iam:PassRole to NetworkDeployer for creating vpc flowlogs
@@ -0,0 +1 @@
data "aws_caller_identity" "this" {}
@@ -0,0 +1,639 @@
data "aws_default_tags" "this" {
lifecycle {
postcondition {
condition = length(self.tags) >= 1
error_message = "Validation failed: Provider default_tags not set."
}
}
}
data "aws_iam_policy_document" "assume-role-policy" {
statement {
actions = ["sts:AssumeRole"]
principals {
type = "AWS"
identifiers = [var.role-trusted-entity-arn]
}
}
}
resource "aws_iam_role" "SecurityDeployer" {
name = "SecurityDeployer"
description = "Admin access to IAM, KMS, SecretsManager, ec2 Key Pair"
max_session_duration = var.max_session_duration
assume_role_policy = data.aws_iam_policy_document.assume-role-policy.json
}
resource "aws_iam_role_policy" "SecurityDeployerPolicy" {
name = "SecurityDeployerPolicy"
policy = jsonencode({
"Version" : "2012-10-17",
"Statement" : [
{
"Effect" : "Allow",
"Action" : [
"iam:*",
"secretsmanager:*",
"ec2:ImportKeyPair",
"kms:*",
"ec2:CreateKeyPair",
"ec2:DescribeKeyPairs",
"ec2:DeleteKeyPair",
"acm:*",
"config:*",
"guardduty:*",
"inspector2:*",
"securityhub:*",
"shield:*",
"sso:*",
"organizations:*"
],
"Resource" : "*"
}
]
}
)
role = aws_iam_role.SecurityDeployer.id
}
resource "aws_iam_role" "NetworkDeployer" {
name = "NetworkDeployer"
description = "Admin access to VPC, SecurityGroup, Route53"
max_session_duration = var.max_session_duration
assume_role_policy = data.aws_iam_policy_document.assume-role-policy.json
}
# iam:PassRole required to create flowlogs
resource "aws_iam_role_policy" "NetworkDeployerPolicy" {
name = "NetworkDeployerPolicy"
policy = jsonencode({
"Version" : "2012-10-17",
"Statement" : [
{
"Effect" : "Allow",
"Action" : [
"iam:PassRole",
"ec2:AcceptVpcEndpointConnections",
"ec2:AllocateAddress",
"ec2:AssignIpv6Addresses",
"ec2:AssignPrivateIpAddresses",
"ec2:AssociateAddress",
"ec2:AssociateDhcpOptions",
"ec2:AssociateRouteTable",
"ec2:AssociateSubnetCidrBlock",
"ec2:AssociateVpcCidrBlock",
"ec2:AttachInternetGateway",
"ec2:AttachNetworkInterface",
"ec2:AttachVpnGateway",
"ec2:CreateCarrierGateway",
"ec2:CreateCustomerGateway",
"ec2:CreateDefaultSubnet",
"ec2:CreateDefaultVpc",
"ec2:CreateDhcpOptions",
"ec2:CreateEgressOnlyInternetGateway",
"ec2:CreateFlowLogs",
"ec2:CreateInternetGateway",
"ec2:CreateNatGateway",
"ec2:CreateNetworkAcl",
"ec2:CreateNetworkAclEntry",
"ec2:CreateNetworkInterface",
"ec2:CreateNetworkInterfacePermission",
"ec2:CreatePlacementGroup",
"ec2:CreateRoute",
"ec2:CreateRouteTable",
"ec2:CreateSecurityGroup",
"ec2:CreateSubnet",
"ec2:CreateTags",
"ec2:CreateVpc",
"ec2:CreateVpcEndpoint",
"ec2:CreateVpcEndpointConnectionNotification",
"ec2:CreateVpcEndpointServiceConfiguration",
"ec2:CreateVpnConnection",
"ec2:CreateVpnConnectionRoute",
"ec2:CreateVpnGateway",
"ec2:DeleteCarrierGateway",
"ec2:DeleteEgressOnlyInternetGateway",
"ec2:DeleteFlowLogs",
"ec2:DeleteNatGateway",
"ec2:DeleteNetworkInterface",
"ec2:DeleteNetworkInterfacePermission",
"ec2:DeletePlacementGroup",
"ec2:DeleteSubnet",
"ec2:DeleteTags",
"ec2:DeleteVpc",
"ec2:DeleteVpcEndpointConnectionNotifications",
"ec2:DeleteVpcEndpointServiceConfigurations",
"ec2:DeleteVpcEndpoints",
"ec2:DeleteVpnConnection",
"ec2:DeleteVpnConnectionRoute",
"ec2:DeleteVpnGateway",
"ec2:DescribeAccountAttributes",
"ec2:DescribeAddresses",
"ec2:DescribeAvailabilityZones",
"ec2:DescribeCarrierGateways",
"ec2:DescribeClassicLinkInstances",
"ec2:DescribeCustomerGateways",
"ec2:DescribeDhcpOptions",
"ec2:DescribeEgressOnlyInternetGateways",
"ec2:DescribeFlowLogs",
"ec2:DescribeInstances",
"ec2:DescribeInternetGateways",
"ec2:DescribeKeyPairs",
"ec2:DescribeMovingAddresses",
"ec2:DescribeNatGateways",
"ec2:DescribeNetworkAcls",
"ec2:DescribeNetworkInterfaceAttribute",
"ec2:DescribeNetworkInterfacePermissions",
"ec2:DescribeNetworkInterfaces",
"ec2:DescribePlacementGroups",
"ec2:DescribePrefixLists",
"ec2:DescribeRouteTables",
"ec2:DescribeSecurityGroupReferences",
"ec2:DescribeSecurityGroupRules",
"ec2:DescribeSecurityGroups",
"ec2:DescribeStaleSecurityGroups",
"ec2:DescribeSubnets",
"ec2:DescribeTags",
"ec2:DescribeVpcAttribute",
"ec2:DescribeVpcClassicLink",
"ec2:DescribeVpcClassicLinkDnsSupport",
"ec2:DescribeVpcEndpointConnectionNotifications",
"ec2:DescribeVpcEndpointConnections",
"ec2:DescribeVpcEndpointServiceConfigurations",
"ec2:DescribeVpcEndpointServicePermissions",
"ec2:DescribeVpcEndpointServices",
"ec2:DescribeVpcEndpoints",
"ec2:DescribeVpcPeeringConnections",
"ec2:DescribeVpcs",
"ec2:DescribeVpnConnections",
"ec2:DescribeVpnGateways",
"ec2:DescribePublicIpv4Pools",
"ec2:DescribeIpv6Pools",
"ec2:DetachInternetGateway",
"ec2:DetachNetworkInterface",
"ec2:DetachVpnGateway",
"ec2:DisableVgwRoutePropagation",
"ec2:DisableVpcClassicLinkDnsSupport",
"ec2:DisassociateAddress",
"ec2:DisassociateRouteTable",
"ec2:DisassociateSubnetCidrBlock",
"ec2:DisassociateVpcCidrBlock",
"ec2:EnableVgwRoutePropagation",
"ec2:EnableVpcClassicLinkDnsSupport",
"ec2:ModifyNetworkInterfaceAttribute",
"ec2:ModifySecurityGroupRules",
"ec2:ModifySubnetAttribute",
"ec2:ModifyVpcAttribute",
"ec2:ModifyVpcEndpoint",
"ec2:ModifyVpcEndpointConnectionNotification",
"ec2:ModifyVpcEndpointServiceConfiguration",
"ec2:ModifyVpcEndpointServicePermissions",
"ec2:ModifyVpcPeeringConnectionOptions",
"ec2:ModifyVpcTenancy",
"ec2:MoveAddressToVpc",
"ec2:RejectVpcEndpointConnections",
"ec2:ReleaseAddress",
"ec2:ReplaceNetworkAclAssociation",
"ec2:ReplaceNetworkAclEntry",
"ec2:ReplaceRoute",
"ec2:ReplaceRouteTableAssociation",
"ec2:ResetNetworkInterfaceAttribute",
"ec2:RestoreAddressToClassic",
"ec2:UnassignIpv6Addresses",
"ec2:UnassignPrivateIpAddresses",
"ec2:UpdateSecurityGroupRuleDescriptionsEgress",
"ec2:UpdateSecurityGroupRuleDescriptionsIngress",
"ec2:AcceptVpcPeeringConnection",
"ec2:AttachClassicLinkVpc",
"ec2:AuthorizeSecurityGroupEgress",
"ec2:AuthorizeSecurityGroupIngress",
"ec2:CreateVpcPeeringConnection",
"ec2:DeleteCustomerGateway",
"ec2:DeleteDhcpOptions",
"ec2:DeleteInternetGateway",
"ec2:DeleteNetworkAcl",
"ec2:DeleteNetworkAclEntry",
"ec2:DeleteRoute",
"ec2:DeleteRouteTable",
"ec2:DeleteSecurityGroup",
"ec2:DeleteVolume",
"ec2:DeleteVpcPeeringConnection",
"ec2:DetachClassicLinkVpc",
"ec2:DisableVpcClassicLink",
"ec2:EnableVpcClassicLink",
"ec2:GetConsoleScreenshot",
"ec2:RejectVpcPeeringConnection",
"ec2:RevokeSecurityGroupEgress",
"ec2:RevokeSecurityGroupIngress",
"ec2:CreateLocalGatewayRoute",
"ec2:CreateLocalGatewayRouteTableVpcAssociation",
"ec2:DeleteLocalGatewayRoute",
"ec2:DeleteLocalGatewayRouteTableVpcAssociation",
"ec2:DescribeLocalGatewayRouteTableVirtualInterfaceGroupAssociations",
"ec2:DescribeLocalGatewayRouteTableVpcAssociations",
"ec2:DescribeLocalGatewayRouteTables",
"ec2:DescribeLocalGatewayVirtualInterfaceGroups",
"ec2:DescribeLocalGatewayVirtualInterfaces",
"ec2:DescribeLocalGateways",
"ec2:SearchLocalGatewayRoutes",
"ec2:AcceptTransitGatewayVpcAttachment",
"ec2:AssociateTransitGatewayRouteTable",
"ec2:CreateTransitGateway",
"ec2:CreateTransitGatewayRoute",
"ec2:CreateTransitGatewayRouteTable",
"ec2:CreateTransitGatewayVpcAttachment",
"ec2:DeleteTransitGateway",
"ec2:DeleteTransitGatewayRoute",
"ec2:DeleteTransitGatewayRouteTable",
"ec2:DeleteTransitGatewayVpcAttachment",
"ec2:DescribeTransitGatewayAttachments",
"ec2:DescribeTransitGatewayRouteTables",
"ec2:DescribeTransitGatewayVpcAttachments",
"ec2:DescribeTransitGateways",
"ec2:DisableTransitGatewayRouteTablePropagation",
"ec2:DisassociateTransitGatewayRouteTable",
"ec2:EnableTransitGatewayRouteTablePropagation",
"ec2:ExportTransitGatewayRoutes",
"ec2:GetTransitGatewayAttachmentPropagations",
"ec2:GetTransitGatewayRouteTableAssociations",
"ec2:GetTransitGatewayRouteTablePropagations",
"ec2:ModifyTransitGateway",
"ec2:ModifyTransitGatewayVpcAttachment",
"ec2:RejectTransitGatewayVpcAttachment",
"ec2:ReplaceTransitGatewayRoute",
"ec2:SearchTransitGatewayRoutes",
"route53domains:*",
"route53resolver:*",
"route53:*",
"directconnect:*"
],
"Resource" : "*"
}
]
}
)
role = aws_iam_role.NetworkDeployer.id
}
resource "aws_iam_role" "DatabaseDeployer" {
name = "DatabaseDeployer"
description = "Admin access to databases"
max_session_duration = var.max_session_duration
assume_role_policy = data.aws_iam_policy_document.assume-role-policy.json
}
resource "aws_iam_role_policy" "DatabaseDeployerPolicy" {
name = "DatabaseDeployerPolicy"
policy = jsonencode(
{
"Version" : "2012-10-17",
"Statement" : [
{
"Effect" : "Allow",
"Action" : [
"rds:*",
"redshift:*",
"elasticache:*",
"kms:Get*",
"kms:List*",
"kms:Describe*"
],
"Resource" : "*"
}
]
}
)
role = aws_iam_role.DatabaseDeployer.id
}
resource "aws_iam_role" "StorageDeployer" {
name = "StorageDeployer"
description = "Admin access to S3, RDS, ElastiCache, ECR"
max_session_duration = var.max_session_duration
assume_role_policy = data.aws_iam_policy_document.assume-role-policy.json
}
resource "aws_iam_role_policy" "StorageDeployerPolicy" {
name = "StorageDeployerPolicy"
policy = jsonencode(
{
"Version" : "2012-10-17",
"Statement" : [
{
"Effect" : "Allow",
"Action" : [
"s3:*",
"ecr:*",
"elasticfilesystem:*",
"fsx:*",
"kms:Get*",
"kms:List*",
"kms:Describe*"
],
"Resource" : "*"
}
]
}
)
role = aws_iam_role.StorageDeployer.id
}
resource "aws_iam_role" "CommonDeployer" {
name = "CommonDeployer"
description = "Admin access to all services except those allowed in other deployer roles"
max_session_duration = var.max_session_duration
assume_role_policy = data.aws_iam_policy_document.assume-role-policy.json
}
resource "aws_iam_role_policy" "CommonDeployerPolicy" {
name = "CommonDeployerPolicy"
policy = jsonencode(
{
"Version" : "2012-10-17",
"Statement" : [
{
"Sid" : "NegateSecurityDeployerPermissions",
"Effect" : "Allow",
"NotAction" : [
"iam:*",
"secretsmanager:*",
"ec2:ImportKeyPair",
"kms:EnableKey",
"kms:ImportKeyMaterial",
"kms:Decrypt",
"kms:GenerateRandom",
"kms:PutKeyPolicy",
"kms:GenerateDataKeyWithoutPlaintext",
"kms:Verify",
"kms:CancelKeyDeletion",
"kms:ReplicateKey",
"kms:GenerateDataKeyPair",
"kms:SynchronizeMultiRegionKey",
"kms:DeleteCustomKeyStore",
"kms:GenerateMac",
"kms:UpdatePrimaryRegion",
"kms:UpdateCustomKeyStore",
"kms:Encrypt",
"kms:ScheduleKeyDeletion",
"kms:ReEncryptTo",
"kms:CreateKey",
"kms:ConnectCustomKeyStore",
"kms:Sign",
"kms:CreateGrant",
"kms:EnableKeyRotation",
"kms:UpdateKeyDescription",
"kms:DeleteImportedKeyMaterial",
"kms:GenerateDataKeyPairWithoutPlaintext",
"kms:DisableKey",
"kms:ReEncryptFrom",
"kms:DisableKeyRotation",
"kms:RetireGrant",
"kms:VerifyMac",
"kms:UpdateAlias",
"kms:CreateCustomKeyStore",
"kms:RevokeGrant",
"kms:GenerateDataKey",
"kms:CreateAlias",
"kms:DisconnectCustomKeyStore",
"kms:DeleteAlias",
"ec2:CreateKeyPair",
"ec2:DescribeKeyPairs",
"ec2:DeleteKeyPair",
"acm:*",
"config:*",
"guardduty:*",
"inspector2:*",
"securityhub:*",
"shield:*",
"sso:*",
"organizations:*"
],
"Resource" : "*"
},
{
"Sid" : "NegateNetworkDeployerPermissions",
"Effect" : "Allow",
"NotAction" : [
"ec2:AcceptVpcEndpointConnections",
"ec2:AllocateAddress",
"ec2:AssignIpv6Addresses",
"ec2:AssignPrivateIpAddresses",
"ec2:AssociateAddress",
"ec2:AssociateDhcpOptions",
"ec2:AssociateRouteTable",
"ec2:AssociateSubnetCidrBlock",
"ec2:AssociateVpcCidrBlock",
"ec2:AttachInternetGateway",
"ec2:AttachNetworkInterface",
"ec2:AttachVpnGateway",
"ec2:CreateCarrierGateway",
"ec2:CreateCustomerGateway",
"ec2:CreateDefaultSubnet",
"ec2:CreateDefaultVpc",
"ec2:CreateDhcpOptions",
"ec2:CreateEgressOnlyInternetGateway",
"ec2:CreateFlowLogs",
"ec2:CreateInternetGateway",
"ec2:CreateNatGateway",
"ec2:CreateNetworkAcl",
"ec2:CreateNetworkAclEntry",
"ec2:CreateNetworkInterface",
"ec2:CreateNetworkInterfacePermission",
"ec2:CreatePlacementGroup",
"ec2:CreateRoute",
"ec2:CreateRouteTable",
"ec2:CreateSecurityGroup",
"ec2:CreateSubnet",
"ec2:CreateTags",
"ec2:CreateVpc",
"ec2:CreateVpcEndpoint",
"ec2:CreateVpcEndpointConnectionNotification",
"ec2:CreateVpcEndpointServiceConfiguration",
"ec2:CreateVpnConnection",
"ec2:CreateVpnConnectionRoute",
"ec2:CreateVpnGateway",
"ec2:DeleteCarrierGateway",
"ec2:DeleteEgressOnlyInternetGateway",
"ec2:DeleteFlowLogs",
"ec2:DeleteNatGateway",
"ec2:DeleteNetworkInterface",
"ec2:DeleteNetworkInterfacePermission",
"ec2:DeletePlacementGroup",
"ec2:DeleteSubnet",
"ec2:DeleteTags",
"ec2:DeleteVpc",
"ec2:DeleteVpcEndpointConnectionNotifications",
"ec2:DeleteVpcEndpointServiceConfigurations",
"ec2:DeleteVpcEndpoints",
"ec2:DeleteVpnConnection",
"ec2:DeleteVpnConnectionRoute",
"ec2:DeleteVpnGateway",
"ec2:DescribeAccountAttributes",
"ec2:DescribeAddresses",
"ec2:DescribeAvailabilityZones",
"ec2:DescribeCarrierGateways",
"ec2:DescribeClassicLinkInstances",
"ec2:DescribeCustomerGateways",
"ec2:DescribeDhcpOptions",
"ec2:DescribeEgressOnlyInternetGateways",
"ec2:DescribeFlowLogs",
"ec2:DescribeInstances",
"ec2:DescribeInternetGateways",
"ec2:DescribeKeyPairs",
"ec2:DescribeMovingAddresses",
"ec2:DescribeNatGateways",
"ec2:DescribeNetworkAcls",
"ec2:DescribeNetworkInterfaceAttribute",
"ec2:DescribeNetworkInterfacePermissions",
"ec2:DescribeNetworkInterfaces",
"ec2:DescribePlacementGroups",
"ec2:DescribePrefixLists",
"ec2:DescribeRouteTables",
"ec2:DescribeSecurityGroupReferences",
"ec2:DescribeSecurityGroupRules",
"ec2:DescribeSecurityGroups",
"ec2:DescribeStaleSecurityGroups",
"ec2:DescribeSubnets",
"ec2:DescribeTags",
"ec2:DescribeVpcAttribute",
"ec2:DescribeVpcClassicLink",
"ec2:DescribeVpcClassicLinkDnsSupport",
"ec2:DescribeVpcEndpointConnectionNotifications",
"ec2:DescribeVpcEndpointConnections",
"ec2:DescribeVpcEndpointServiceConfigurations",
"ec2:DescribeVpcEndpointServicePermissions",
"ec2:DescribeVpcEndpointServices",
"ec2:DescribeVpcEndpoints",
"ec2:DescribeVpcPeeringConnections",
"ec2:DescribeVpcs",
"ec2:DescribeVpnConnections",
"ec2:DescribeVpnGateways",
"ec2:DescribePublicIpv4Pools",
"ec2:DescribeIpv6Pools",
"ec2:DetachInternetGateway",
"ec2:DetachNetworkInterface",
"ec2:DetachVpnGateway",
"ec2:DisableVgwRoutePropagation",
"ec2:DisableVpcClassicLinkDnsSupport",
"ec2:DisassociateAddress",
"ec2:DisassociateRouteTable",
"ec2:DisassociateSubnetCidrBlock",
"ec2:DisassociateVpcCidrBlock",
"ec2:EnableVgwRoutePropagation",
"ec2:EnableVpcClassicLinkDnsSupport",
"ec2:ModifyNetworkInterfaceAttribute",
"ec2:ModifySecurityGroupRules",
"ec2:ModifySubnetAttribute",
"ec2:ModifyVpcAttribute",
"ec2:ModifyVpcEndpoint",
"ec2:ModifyVpcEndpointConnectionNotification",
"ec2:ModifyVpcEndpointServiceConfiguration",
"ec2:ModifyVpcEndpointServicePermissions",
"ec2:ModifyVpcPeeringConnectionOptions",
"ec2:ModifyVpcTenancy",
"ec2:MoveAddressToVpc",
"ec2:RejectVpcEndpointConnections",
"ec2:ReleaseAddress",
"ec2:ReplaceNetworkAclAssociation",
"ec2:ReplaceNetworkAclEntry",
"ec2:ReplaceRoute",
"ec2:ReplaceRouteTableAssociation",
"ec2:ResetNetworkInterfaceAttribute",
"ec2:RestoreAddressToClassic",
"ec2:UnassignIpv6Addresses",
"ec2:UnassignPrivateIpAddresses",
"ec2:UpdateSecurityGroupRuleDescriptionsEgress",
"ec2:UpdateSecurityGroupRuleDescriptionsIngress",
"ec2:AcceptVpcPeeringConnection",
"ec2:AttachClassicLinkVpc",
"ec2:AuthorizeSecurityGroupEgress",
"ec2:AuthorizeSecurityGroupIngress",
"ec2:CreateVpcPeeringConnection",
"ec2:DeleteCustomerGateway",
"ec2:DeleteDhcpOptions",
"ec2:DeleteInternetGateway",
"ec2:DeleteNetworkAcl",
"ec2:DeleteNetworkAclEntry",
"ec2:DeleteRoute",
"ec2:DeleteRouteTable",
"ec2:DeleteSecurityGroup",
"ec2:DeleteVolume",
"ec2:DeleteVpcPeeringConnection",
"ec2:DetachClassicLinkVpc",
"ec2:DisableVpcClassicLink",
"ec2:EnableVpcClassicLink",
"ec2:GetConsoleScreenshot",
"ec2:RejectVpcPeeringConnection",
"ec2:RevokeSecurityGroupEgress",
"ec2:RevokeSecurityGroupIngress",
"ec2:CreateLocalGatewayRoute",
"ec2:CreateLocalGatewayRouteTableVpcAssociation",
"ec2:DeleteLocalGatewayRoute",
"ec2:DeleteLocalGatewayRouteTableVpcAssociation",
"ec2:DescribeLocalGatewayRouteTableVirtualInterfaceGroupAssociations",
"ec2:DescribeLocalGatewayRouteTableVpcAssociations",
"ec2:DescribeLocalGatewayRouteTables",
"ec2:DescribeLocalGatewayVirtualInterfaceGroups",
"ec2:DescribeLocalGatewayVirtualInterfaces",
"ec2:DescribeLocalGateways",
"ec2:SearchLocalGatewayRoutes",
"ec2:AcceptTransitGatewayVpcAttachment",
"ec2:AssociateTransitGatewayRouteTable",
"ec2:CreateTransitGateway",
"ec2:CreateTransitGatewayRoute",
"ec2:CreateTransitGatewayRouteTable",
"ec2:CreateTransitGatewayVpcAttachment",
"ec2:DeleteTransitGateway",
"ec2:DeleteTransitGatewayRoute",
"ec2:DeleteTransitGatewayRouteTable",
"ec2:DeleteTransitGatewayVpcAttachment",
"ec2:DescribeTransitGatewayAttachments",
"ec2:DescribeTransitGatewayRouteTables",
"ec2:DescribeTransitGatewayVpcAttachments",
"ec2:DescribeTransitGateways",
"ec2:DisableTransitGatewayRouteTablePropagation",
"ec2:DisassociateTransitGatewayRouteTable",
"ec2:EnableTransitGatewayRouteTablePropagation",
"ec2:ExportTransitGatewayRoutes",
"ec2:GetTransitGatewayAttachmentPropagations",
"ec2:GetTransitGatewayRouteTableAssociations",
"ec2:GetTransitGatewayRouteTablePropagations",
"ec2:ModifyTransitGateway",
"ec2:ModifyTransitGatewayVpcAttachment",
"ec2:RejectTransitGatewayVpcAttachment",
"ec2:ReplaceTransitGatewayRoute",
"ec2:SearchTransitGatewayRoutes",
"route53domains:*",
"route53resolver:*",
"route53:*",
"directconnect:*"
],
"Resource" : "*"
},
{
"Sid" : "NegateDatabaseDeployerPermissions",
"Effect" : "Allow",
"NotAction" : [
"rds:*",
"redshift:*",
"elasticache:*"
],
"Resource" : "*"
},
{
"Sid" : "NegateStorageDeployerPermissions",
"Effect" : "Allow",
"NotAction" : [
"s3:*",
"ecr:*",
"elasticfilesystem:*",
"fsx:*"
],
"Resource" : "*"
}
]
}
)
role = aws_iam_role.CommonDeployer.id
}
@@ -0,0 +1,9 @@
output "devsecops-roles" {
value = [
aws_iam_role.CommonDeployer.arn,
aws_iam_role.DatabaseDeployer.arn,
aws_iam_role.NetworkDeployer.arn,
aws_iam_role.DatabaseDeployer.arn,
aws_iam_role.StorageDeployer.arn
]
}
@@ -0,0 +1,9 @@
terraform {
required_version = ">= 1.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 3.25"
}
}
}
@@ -0,0 +1,12 @@
/* variable "aws-region" {}
variable "aws-region-short" {}
variable "customer-name" {}
variable "environment" {}
variable "project" {}
variable "application" {}
*/
variable max_session_duration {
type = number
default = 14400
}
variable role-trusted-entity-arn {}
@@ -0,0 +1,17 @@
# Overview
This module performs the following tasks:
- Enable AWS config
- Create AWS config files for CIS benchmark
- Create s3 bucket for config use
## Inputs:
| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:-----:|
| application | name of application | string | none | yes |
| environment | capacity of environment (prd/dev/lab) | string | none | yes |
| customer-name | owner of aws resources | string | none | yes |
| project | name of project | string | none | yes |
| default-tags | tags to be added to resources | list | none | yes |
| aws-region-short | short name of aws region (e.g. apne1) | string | none | yes |
@@ -0,0 +1,8 @@
data aws_caller_identity this {}
resource aws_guardduty_detector gd {
enable = true
finding_publishing_frequency = "ONE_HOUR"
tags = var.default-tags
}
@@ -0,0 +1,3 @@
output guardduty-arn {
value = aws_guardduty_detector.gd.arn
}
@@ -0,0 +1 @@
variable "default-tags" {}
@@ -0,0 +1,24 @@
# iam-user module
Module for creating IAM user. Credentials, if any, will be stored in secretsmanager
## Example
```terraform
module iam-user {
source = "../../modules/security_identity_compliance/iam-user"
default-tags = local.default-tags
iam-user-name = var.iam-user-name
iam-user-policy = ""
iam-user-policy-name = "SelfServicePermissions"
create-access-key = false
create-password = false
managed-policy-arns = ["arn:aws:iam::aws:policy/job-function/ViewOnlyAccess"]
create-group = true
add-to-groups = []
iam-group-name = var.iam-group-name
}
output iam-user-arn {
value = module.iam-user.iam-user-arn
}
```
@@ -0,0 +1,17 @@
resource "aws_iam_group" "iam-group" {
name = var.iam-group-name
}
resource "aws_iam_group_policy" "iam-group-policy-new-group" {
count = var.iam-group-policy != "" ? 1 : 0
name = var.iam-group-policy-name
group = aws_iam_group.iam-group.name
policy = var.iam-group-policy
}
resource "aws_iam_group_policy_attachment" "iam-group-managed-policies" {
count = length(var.managed-policy-arns) > 0 ? 1 : 0
group = aws_iam_group.iam-group.name
policy_arn = var.managed-policy-arns[count.index]
}
@@ -0,0 +1,7 @@
output iam-group-name {
value = aws_iam_group.iam-group.name
}
output iam-group-arn {
value = aws_iam_group.iam-group.arn
}
@@ -0,0 +1,4 @@
variable managed-policy-arns {}
variable iam-group-name {}
variable iam-group-policy {}
variable iam-group-policy-name {}
@@ -0,0 +1,9 @@
terraform {
required_version = ">= 1.3.9"
required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 5.0"
}
}
}
@@ -0,0 +1,12 @@
BSD Zero Clause License
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
@@ -0,0 +1,52 @@
<!-- This readme file is generated with terraform-docs -->
## Requirements
| Name | Version |
|------|---------|
| terraform | >= 1.3.0 |
| aws | ~> 5.0 |
## Providers
| Name | Version |
|------|---------|
| aws | ~> 5.0 |
## Modules
No modules.
## Resources
| Name | Type |
|------|------|
| [aws_iam_instance_profile.ip](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_instance_profile) | resource |
| [aws_iam_policy.p](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource |
| [aws_iam_role.r](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
| [aws_iam_role_policy_attachment.pa](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource |
## Inputs
| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| create-instance-profile | Determines whether instance profile will be created | `bool` | `false` | no |
| description | Description of IAM role | `string` | n/a | yes |
| max-session-duration | Max session duration in seconds | `number` | `3600` | no |
| path | Path of IAM role. Defaults to /Customer/ | `string` | `"/Customer/"` | no |
| policies | Map of policies to be created and attached | <pre>map(<br> object(<br> {<br> description = string<br> policy = string<br> }<br> )<br> )</pre> | `{}` | no |
| role-name | Name of IAM role | `string` | n/a | yes |
| tags | Tags additional to default tags | `map(string)` | `{}` | no |
| trusted-entity | AWS service allowed to assume this role or a full assume role policy | `string` | n/a | yes |
## Outputs
| Name | Description |
|------|-------------|
| instance-profile-arn | ARN of IAM instance profile |
| name | Name of IAM role |
| profile-name | Name of IAM instance profile |
| role-arn | IAM role ARN |
---
## Authorship
This module was developed by xpk.
@@ -0,0 +1,50 @@
# Assume role policy can be provided as-is, or built using the trusted-entity variable
locals {
assume-role-policy = endswith(var.trusted-entity, ".com") ? jsonencode(
{
"Version" : "2012-10-17",
"Statement" : [
{
"Effect" : "Allow",
"Principal" : {
"Service" : [
var.trusted-entity
]
},
"Action" : "sts:AssumeRole"
}
]
}
) : var.trusted-entity
}
resource "aws_iam_instance_profile" "ip" {
count = var.create-instance-profile ? 1 : 0
name = "${var.role-name}-profile"
role = aws_iam_role.r.name
path = var.path
}
resource "aws_iam_role" "r" {
name = var.role-name
description = var.description
assume_role_policy = local.assume-role-policy
force_detach_policies = true
path = var.path
max_session_duration = var.max-session-duration
tags = var.tags
}
resource "aws_iam_policy" "p" {
for_each = var.policies
description = each.value.description
name = each.key
policy = each.value.policy
tags = var.tags
}
resource "aws_iam_role_policy_attachment" "pa" {
for_each = aws_iam_policy.p
role = aws_iam_role.r.name
policy_arn = each.value.arn
}
@@ -0,0 +1,19 @@
output "profile-name" {
description = "Name of IAM instance profile"
value = aws_iam_instance_profile.ip[*].name
}
output "role-arn" {
description = "IAM role ARN"
value = aws_iam_role.r.arn
}
output "name" {
description = "Name of IAM role"
value = aws_iam_role.r.name
}
output "instance-profile-arn" {
description = "ARN of IAM instance profile"
value = aws_iam_instance_profile.ip.*.arn
}
@@ -0,0 +1,51 @@
variable "create-instance-profile" {
description = "Determines whether instance profile will be created"
type = bool
default = false
}
variable "description" {
description = "Description of IAM role"
type = string
}
variable "policies" {
description = "Map of policies to be created and attached"
type = map(
object(
{
description = string
policy = string
}
)
)
default = {}
}
variable "role-name" {
description = "Name of IAM role"
type = string
}
variable "path" {
description = "Path of IAM role. Defaults to /Customer/"
type = string
default = "/Customer/"
}
variable "trusted-entity" {
description = "AWS service allowed to assume this role or a full assume role policy"
type = string
}
variable "max-session-duration" {
description = "Max session duration in seconds"
type = number
default = 3600
}
variable "tags" {
description = "Tags additional to default tags"
type = map(string)
default = {}
}
@@ -0,0 +1,12 @@
BSD Zero Clause License
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
@@ -0,0 +1,51 @@
<!-- This readme file is generated with terraform-docs -->
Inline policy for IAM role is not supported by this module. Use managed policies instead.
## Requirements
| Name | Version |
|------|---------|
| terraform | >= 1.3.0 |
| aws | >= 5.4.0 |
## Providers
| Name | Version |
|------|---------|
| aws | >= 5.4.0 |
## Modules
No modules.
## Resources
| Name | Type |
|------|------|
| [aws_iam_instance_profile.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_instance_profile) | resource |
| [aws_iam_role.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
## Inputs
| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| assume-role-policy | The actual assume role policy if trusted-entity is not provided. | `string` | `null` | no |
| create-instance-profile | Determines whether instance profile will be created | `bool` | `false` | no |
| description | Description of IAM role | `string` | n/a | yes |
| managed-policy-arns | List of managed policies to be attached to role | `list(string)` | `null` | no |
| path | Path of IAM role. Defaults to /Customer/ | `string` | `"/Customer/"` | no |
| role-name | Name of IAM role | `string` | n/a | yes |
| trusted-entity | AWS service allowed to assume this role. Either this or assume-role-policy must be provided. | `string` | n/a | yes |
## Outputs
| Name | Description |
|------|-------------|
| instance-profile-arn | ARN of IAM instance profile |
| name | Name of IAM role |
| profile-name | Name of IAM instance profile |
| role-arn | IAM role ARN |
---
## Authorship
This module was developed by xpk.
@@ -0,0 +1,40 @@
# Assume role policy can be provided as-is, or built using the trusted-entity variable
locals {
assume-role-policy = var.assume-role-policy != null ? var.assume-role-policy : jsonencode(
{
"Version" : "2012-10-17",
"Statement" : [
{
"Effect" : "Allow",
"Principal" : {
"Service" : [
var.trusted-entity
]
},
"Action" : "sts:AssumeRole"
}
]
}
)
}
resource "aws_iam_instance_profile" "this" {
count = var.create-instance-profile ? 1 : 0
name = "${var.role-name}-profile"
role = aws_iam_role.this.name
path = var.path
}
resource "aws_iam_role" "this" {
name = var.role-name
description = var.description
assume_role_policy = local.assume-role-policy
managed_policy_arns = var.managed-policy-arns
force_detach_policies = true
path = var.path
# disable use of inline policy
# inline_policy {
# name = var.inline-policy-name
# policy = var.inline-policy
# }
}
@@ -0,0 +1,19 @@
output "profile-name" {
description = "Name of IAM instance profile"
value = aws_iam_instance_profile.this[*].name
}
output "role-arn" {
description = "IAM role ARN"
value = aws_iam_role.this.arn
}
output "name" {
description = "Name of IAM role"
value = aws_iam_role.this.name
}
output "instance-profile-arn" {
description = "ARN of IAM instance profile"
value = aws_iam_instance_profile.this.*.arn
}
@@ -0,0 +1,9 @@
terraform {
required_version = ">= 1.3.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 5.4.0"
}
}
}
@@ -0,0 +1,38 @@
variable "create-instance-profile" {
description = "Determines whether instance profile will be created"
type = bool
default = false
}
variable "description" {
description = "Description of IAM role"
type = string
}
variable "managed-policy-arns" {
description = "List of managed policies to be attached to role"
type = list(string)
default = null
}
variable "role-name" {
description = "Name of IAM role"
type = string
}
variable "path" {
description = "Path of IAM role. Defaults to /Customer/"
type = string
default = "/Customer/"
}
variable "trusted-entity" {
description = "AWS service allowed to assume this role. Set this to null if assume-role-policy is to be provided."
type = string
}
variable "assume-role-policy" {
description = "The actual assume role policy if trusted-entity is not provided."
type = string
default = null
}
@@ -0,0 +1,56 @@
<!-- This readme file is generated with terraform-docs -->
## Requirements
No requirements.
## Providers
| Name | Version |
|------|---------|
| aws | n/a |
| random | n/a |
## Modules
No modules.
## Resources
| Name | Type |
|------|------|
| [aws_iam_access_key.iam-user-access-key](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_access_key) | resource |
| [aws_iam_group_membership.group-membership](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_group_membership) | resource |
| [aws_iam_user.iam-user](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_user) | resource |
| [aws_iam_user_login_profile.iam-user-profile](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_user_login_profile) | resource |
| [aws_iam_user_policy.iam-user-policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_user_policy) | resource |
| [aws_iam_user_policy.iam-user-selfservice-policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_user_policy) | resource |
| [aws_iam_user_policy_attachment.iam-user-managed-policies](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_user_policy_attachment) | resource |
| [aws_secretsmanager_secret.secretmanager](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/secretsmanager_secret) | resource |
| [aws_secretsmanager_secret_version.iam-user-secret](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/secretsmanager_secret_version) | resource |
| [random_id.secrets-random-id](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/id) | resource |
| [random_password.iam-user-pass](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/password) | resource |
| [aws_iam_policy_document.user-policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
## Inputs
| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| add-to-groups | n/a | `list(string)` | `[]` | no |
| create-access-key | n/a | `bool` | n/a | yes |
| create-password | n/a | `bool` | n/a | yes |
| iam-user-name | n/a | `any` | n/a | yes |
| iam-user-policy | n/a | `string` | `""` | no |
| iam-user-policy-name | n/a | `string` | `""` | no |
| managed-policy-arns | n/a | `any` | n/a | yes |
## Outputs
| Name | Description |
|------|-------------|
| iam-user-access-key | n/a |
| iam-user-arn | n/a |
| iam-user-name | n/a |
---
## Authorship
This module was developed by xpk.
@@ -0,0 +1,99 @@
resource "aws_iam_user" "iam-user" {
name = var.iam-user-name
force_destroy = true
}
resource "aws_iam_access_key" "iam-user-access-key" {
count = var.create-access-key ? 1 : 0
user = aws_iam_user.iam-user.name
}
resource "aws_iam_user_policy" "iam-user-policy" {
count = var.iam-user-policy != "" ? 1 : 0
name = var.iam-user-policy-name
user = aws_iam_user.iam-user.name
policy = var.iam-user-policy
}
resource "aws_iam_user_policy" "iam-user-selfservice-policy" {
name = "SelfServicePermissions"
user = aws_iam_user.iam-user.name
policy = data.aws_iam_policy_document.user-policy.json
}
data "aws_iam_policy_document" "user-policy" {
statement {
sid = "ManageOwnCredentials"
actions = [
"iam:ChangePassword",
"iam:UpdateLoginProfile",
"iam:CreateAccessKey",
"iam:DeleteAccessKey",
"iam:ListAccessKeys",
"iam:CreateVirtualMFADevice",
"iam:EnableMFADevice",
"iam:ListMFA*",
"iam:ListVirtualMFA*",
"iam:ResyncMFADevice",
"iam:GetUser"
]
effect = "Allow"
resources = ["arn:aws:iam::*:user/$${aws:username}"]
}
statement {
sid = "GetBasicUserInfo"
actions = [
"iam:GetAccountPasswordPolicy",
"iam:GetAccessKeyLastUsed",
"iam:GetUserPolicy"
]
effect = "Allow"
resources = ["*"]
}
}
resource "aws_iam_user_policy_attachment" "iam-user-managed-policies" {
count = length(var.add-to-groups) > 0 ? 0 : length(var.managed-policy-arns)
user = aws_iam_user.iam-user.name
policy_arn = var.managed-policy-arns[count.index]
}
resource "aws_iam_user_login_profile" "iam-user-profile" {
count = var.create-password ? 1 : 0
user = aws_iam_user.iam-user.name
password_length = 20
pgp_key = null
}
resource "random_id" "secrets-random-id" {
byte_length = 2
}
resource "aws_secretsmanager_secret" "secretmanager" {
count = var.create-access-key || var.create-password ? 1 : 0
name = "IamUserCredential-${random_id.secrets-random-id.dec}-${var.iam-user-name}"
description = "AWS resource credential"
}
resource "aws_secretsmanager_secret_version" "iam-user-secret" {
count = var.create-access-key || var.create-password ? 1 : 0
secret_id = aws_secretsmanager_secret.secretmanager[0].id
secret_string = jsonencode(
{
"ConsolePassword" : length(aws_iam_user_login_profile.iam-user-profile[0].password) > 0 ? aws_iam_user_login_profile.iam-user-profile[0].password : "NotSet",
"AccessKeyId" : length(aws_iam_access_key.iam-user-access-key) > 0 ? aws_iam_access_key.iam-user-access-key[0].id : "NotSet",
"KeySecret" : length(aws_iam_access_key.iam-user-access-key) > 0 ? aws_iam_access_key.iam-user-access-key[0].secret : "NotSet"
}
)
}
resource "aws_iam_group_membership" "group-membership" {
for_each = toset(var.add-to-groups)
name = "MembershipToExistingGroups"
group = each.value
users = [aws_iam_user.iam-user.name]
}
@@ -0,0 +1,15 @@
output "iam-user-name" {
value = aws_iam_user.iam-user.name
}
output "iam-user-arn" {
value = aws_iam_user.iam-user.arn
}
output "iam-user-access-key" {
value = try(aws_iam_access_key.iam-user-access-key[0].id, "none")
}
output "iam-user-secret-arn" {
value = try(aws_secretsmanager_secret_version.iam-user-secret[0].arn, "none")
}
@@ -0,0 +1,20 @@
variable "iam-user-name" {}
variable "iam-user-policy" {
type = string
default = ""
}
variable "iam-user-policy-name" {
type = string
default = ""
}
variable "create-access-key" {
type = bool
}
variable "create-password" {
type = bool
}
variable "managed-policy-arns" {}
variable "add-to-groups" {
type = list(string)
default = []
}
@@ -0,0 +1,9 @@
terraform {
required_version = ">= 1.3.9"
required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 5.0"
}
}
}
@@ -0,0 +1,2 @@
# inspector2 module
Via awscli, enable inspector2 scanning of ECR repositories
@@ -0,0 +1,11 @@
resource "null_resource" "cli-inspector2" {
provisioner "local-exec" {
when = create
command = "/bin/bash -c 'aws inspector2 enable --resource-types \"ECR\"'"
}
provisioner "local-exec" {
when = destroy
command = "/bin/bash -c 'aws inspector2 disable --resource-types \"ECR\"; sleep 30; aws inspector2 disable'"
}
}
@@ -0,0 +1,24 @@
resource "aws_s3_account_public_access_block" "default-s3-public-access-settings" {
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
lifecycle { ignore_changes = all }
}
resource "aws_ebs_encryption_by_default" "default-ebs-encryption-setting" {
enabled = true
lifecycle { ignore_changes = all }
}
resource "aws_iam_account_password_policy" "password-policy1" {
minimum_password_length = 14
require_lowercase_characters = true
require_numbers = true
require_uppercase_characters = true
require_symbols = true
allow_users_to_change_password = true
max_password_age = 90
password_reuse_prevention = 24
hard_expiry = true
}
@@ -0,0 +1,19 @@
# Overview
This module performs the following tasks
- Create IAM roles based on job functions
- Create IAM password policy
- Enable IAM access analyzer
## Inputs:
| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:-----:|
| application | name of application | string | none | yes |
| environment | capacity of environment (prd/dev/lab) | string | none | yes |
| customer-name | owner of aws resources | string | none | yes |
| project | name of project | string | none | yes |
| default-tags | tags to be added to resources | list | none | yes |
| aws-region-short | short name of aws region (e.g. apne1) | string | none | yes |
| create-cloudhealth-resources | create cloudhealth role | bool | none | yes |
| cloudheath-ext-id1 | cloudhealth role external id for sts | string | none | no |
| cloudheath-ext-id2 | cloudhealth role external id for sts | string | none | no |
@@ -0,0 +1,4 @@
resource "aws_accessanalyzer_analyzer" "iam-aa" {
analyzer_name = "IAMAcecssAnalyzer"
tags = var.default-tags
}
@@ -0,0 +1,168 @@
resource "aws_iam_role" "cloudhealth-role" {
count = var.create-cloudhealth-resources ? 1 : 0
name = "CloudHealth-Role"
tags = var.default-tags
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::454464851268:root"
},
"Action": "sts:AssumeRole",
"Condition": {
"StringEquals": {
"sts:ExternalId": [
"${var.cloudheath-ext-id1}",
"${var.cloudheath-ext-id2}"
]
}
}
}
]
}
EOF
}
resource "aws_iam_policy" "CloudHealth-Policy" {
count = var.create-cloudhealth-resources ? 1 : 0
name = "CloudHealthPolicy"
policy = jsonencode(
{
"Version" : "2012-10-17",
"Statement" : [
{
"Sid" : "CloudhealthAccess",
"Action" : [
"autoscaling:Describe*",
"cloudformation:ListStacks",
"cloudformation:ListStackResources",
"cloudformation:DescribeStacks",
"cloudformation:DescribeStackEvents",
"cloudformation:DescribeStackResources",
"cloudformation:GetTemplate",
"cloudfront:Get*",
"cloudfront:List*",
"cloudtrail:DescribeTrails",
"cloudtrail:ListTags",
"cloudtrail:Get*",
"cloudwatch:Describe*",
"cloudwatch:Get*",
"cloudwatch:List*",
"config:Get*",
"config:Describe*",
"config:Deliver*",
"config:List*",
"cur:Describe*",
"dms:Describe*",
"dms:List*",
"dynamodb:DescribeTable",
"dynamodb:List*",
"ec2:Describe*",
"ec2:DescribeRegions",
"ec2:GetReservedInstancesExchangeQuote",
"ecs:List*",
"ecs:Describe*",
"elasticache:Describe*",
"elasticache:ListTagsForResource",
"elasticbeanstalk:Check*",
"elasticbeanstalk:Describe*",
"elasticbeanstalk:List*",
"elasticbeanstalk:RequestEnvironmentInfo",
"elasticbeanstalk:RetrieveEnvironmentInfo",
"elasticfilesystem:Describe*",
"elasticloadbalancing:Describe*",
"elasticmapreduce:Describe*",
"elasticmapreduce:List*",
"es:List*",
"es:Describe*",
"es:DescribeReservedElasticsearchInstances",
"firehose:ListDeliveryStreams",
"firehose:DescribeDeliveryStream",
"fsx:Describe*",
"iam:List*",
"iam:Get*",
"iam:GenerateCredentialReport",
"kinesis:Describe*",
"kinesis:List*",
"kms:DescribeKey",
"kms:GetKeyRotationStatus",
"kms:ListKeys",
"lambda:List*",
"logs:Describe*",
"logs:List*",
"organizations:ListAccounts",
"organizations:ListTagsForResource",
"redshift:Describe*",
"route53:Get*",
"route53:List*",
"rds:Describe*",
"rds:ListTagsForResource",
"s3:GetAccountPublicAccessBlock",
"s3:GetBucketAcl",
"s3:GetBucketLocation",
"s3:GetBucketLogging",
"s3:GetBucketPolicy",
"s3:GetBucketPolicyStatus",
"s3:GetBucketPublicAccessBlock",
"s3:GetBucketTagging",
"s3:GetBucketVersioning",
"s3:GetBucketWebsite",
"s3:List*",
"sagemaker:Describe*",
"sagemaker:List*",
"savingsplans:DescribeSavingsPlans",
"sdb:GetAttributes",
"sdb:List*",
"ses:Get*",
"ses:List*",
"sns:Get*",
"sns:List*",
"sqs:GetQueueAttributes",
"sqs:ListQueues",
"storagegateway:List*",
"storagegateway:Describe*",
"workspaces:Describe*"
],
"Resource" : "*",
"Effect" : "Allow"
},
{
"Sid" : "FineGrainedBillingAccess",
"Action" : [
"account:Get*",
"billing:Get*",
"billing:List*",
"ce:Describe*",
"ce:Get*",
"ce:List*",
"consolidatedbilling:GetAccountBillingRole",
"consolidatedbilling:ListLinkedAccounts",
"cur:Get*",
"cur:ValidateReportDestination",
"freetier:Get*",
"invoicing:Get*",
"invoicing:List*",
"payments:Get*",
"payments:List*",
"purchase-orders:Get*",
"purchase-orders:List*",
"tax:Get*",
"tax:List*"
],
"Resource" : "*",
"Effect" : "Allow"
}
]
}
)
}
resource "aws_iam_role_policy_attachment" "cloudhealth-role-policy-attach" {
count = var.create-cloudhealth-resources ? 1 : 0
role = aws_iam_role.cloudhealth-role[1].name
policy_arn = aws_iam_policy.CloudHealth-Policy[1].arn
}
@@ -0,0 +1,11 @@
resource "aws_iam_account_password_policy" "password-policy1" {
minimum_password_length = 14
require_lowercase_characters = true
require_numbers = true
require_uppercase_characters = true
require_symbols = true
allow_users_to_change_password = true
max_password_age = 90
password_reuse_prevention = 24
hard_expiry = true
}
@@ -0,0 +1,128 @@
/*
Create IAM roles based on job functions
https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_job-functions.html
- Administrator
- Billing
- Database admin
- Network admin
- Developers
- Readonly and support
*/
data aws_caller_identity this {}
data aws_iam_policy_document assume-role-policy {
statement {
sid = "AllowMyAccount"
effect = "Allow"
actions = ["sts:AssumeRole"]
principals {
identifiers = [data.aws_caller_identity.this.account_id]
type = "AWS"
}
}
}
resource aws_iam_role administrator-role {
name = "${var.customer-name}-awsadmin"
description = "Provides full access to AWS services and resources."
tags = var.default-tags
assume_role_policy = data.aws_iam_policy_document.assume-role-policy.json
path = "/${var.customer-name}/"
max_session_duration = 7200
}
resource "aws_iam_role_policy_attachment" "administrator-role-policy-attach" {
role = aws_iam_role.administrator-role.name
policy_arn = "arn:aws:iam::aws:policy/AdministratorAccess"
}
resource aws_iam_role billing-role {
name = "${var.customer-name}-billing"
description = "Grants permissions for billing and cost management."
tags = var.default-tags
assume_role_policy = data.aws_iam_policy_document.assume-role-policy.json
path = "/${var.customer-name}/"
max_session_duration = 3600
}
resource "aws_iam_role_policy_attachment" "billing-role-policy-attach" {
role = aws_iam_role.billing-role.name
policy_arn = "arn:aws:iam::aws:policy/job-function/Billing"
}
resource aws_iam_role dba-role {
name = "${var.customer-name}-dba"
description = "AWS database admin role"
tags = var.default-tags
assume_role_policy = data.aws_iam_policy_document.assume-role-policy.json
path = "/${var.customer-name}/"
max_session_duration = 7200
}
resource "aws_iam_role_policy_attachment" "dba-role-policy-attach" {
role = aws_iam_role.dba-role.name
policy_arn = "arn:aws:iam::aws:policy/job-function/DatabaseAdministrator"
}
resource aws_iam_role network-admin-role {
name = "${var.customer-name}-networkadmin"
description = "AWS network admin role"
tags = var.default-tags
assume_role_policy = data.aws_iam_policy_document.assume-role-policy.json
path = "/${var.customer-name}/"
max_session_duration = 7200
}
resource "aws_iam_role_policy_attachment" "network-admin-role-policy-attach" {
role = aws_iam_role.network-admin-role.name
policy_arn = "arn:aws:iam::aws:policy/job-function/NetworkAdministrator"
}
resource aws_iam_role developer-role {
name = "${var.customer-name}-developer"
description = "Provides full access to AWS resources excluding IAM."
tags = var.default-tags
assume_role_policy = data.aws_iam_policy_document.assume-role-policy.json
path = "/${var.customer-name}/"
max_session_duration = 7200
}
resource "aws_iam_role_policy_attachment" "developer-role-policy-attach1" {
role = aws_iam_role.developer-role.name
policy_arn = "arn:aws:iam::aws:policy/PowerUserAccess"
}
resource aws_iam_role securityaudit-role {
name = "${var.customer-name}-securityaudit"
description = "Role to read security configuration metadata."
tags = var.default-tags
assume_role_policy = data.aws_iam_policy_document.assume-role-policy.json
path = "/${var.customer-name}/"
max_session_duration = 7200
}
resource "aws_iam_role_policy_attachment" "securityaudit-role-policy-attach1" {
role = aws_iam_role.securityaudit-role.name
policy_arn = "arn:aws:iam::aws:policy/SecurityAudit"
}
resource aws_iam_role support-role {
name = "${var.customer-name}-support"
description = "Role to troubleshoot and resolve issues in AWS."
tags = var.default-tags
assume_role_policy = data.aws_iam_policy_document.assume-role-policy.json
path = "/${var.customer-name}/"
max_session_duration = 7200
}
resource "aws_iam_role_policy_attachment" "support-role-policy-attach1" {
role = aws_iam_role.support-role.name
policy_arn = "arn:aws:iam::aws:policy/job-function/SupportUser"
}
resource "aws_iam_role_policy_attachment" "support-role-policy-attach2" {
role = aws_iam_role.support-role.name
policy_arn = "arn:aws:iam::aws:policy/ReadOnlyAccess"
}
@@ -0,0 +1,20 @@
variable "customer-name" {}
variable "default-tags" {}
variable "cloudtrail-retain-days" {
type = number
default = 90
}
variable "create-cloudhealth-resources" {
type = bool
default = false
}
variable "cloudheath-ext-id1" {
type = string
default = ""
}
variable "cloudheath-ext-id2" {
type = string
default = ""
}
@@ -0,0 +1,29 @@
# secretsmanager-secret module
This module creates an entry in secretsmanager, attaching a default access policy if one is
not provided from root module. A random suffix is assigned to every secret, as AWS may delay
creation of secrets with the same name, after the old one has been destroyed that is.
The default policy attached to secretsmanager prevents cross-account access.
To have this module generate a random password, set ```generate_secret``` to true.
To tag resources, please use provider default_tags.
## Example
```hcl
module "secret1" {
source = "../../modules/security_identity_compliance/secretsmanager-secret"
secret_name = "test-secret-name-1"
secret_description = "test-secret-desc-1"
secret_value = "test-secret-value"
}
module "secret2" {
source = "../../modules/security_identity_compliance/secretsmanager-secret"
secret_name = "test-secret-name-2"
secret_description = "test-secret-desc-3"
generate_secret = true
}
```
@@ -0,0 +1,15 @@
module "secret1" {
source = "../"
secret_name = "test-secret-name-1"
secret_description = "test-secret-desc-1"
secret_value = "test-secret-value"
}
module "secret2" {
source = "../"
secret_name = "test-secret-name-2"
secret_description = "test-secret-desc-3"
generate_secret = true
}
@@ -0,0 +1,59 @@
data "aws_caller_identity" "this" {}
resource "random_id" "rid" {
byte_length = 2
}
resource "aws_secretsmanager_secret" "secret1" {
name = "${var.secret_name}-${random_id.rid.dec}"
description = var.secret_description
kms_key_id = var.kms_key_id == null ? null : var.kms_key_id
}
resource "aws_secretsmanager_secret_version" "this" {
secret_id = aws_secretsmanager_secret.secret1.id
secret_string = var.generate_secret ? data.aws_secretsmanager_random_password.this.random_password : var.secret_value
}
data "aws_secretsmanager_random_password" "this" {
password_length = 22
exclude_numbers = false
exclude_characters = "o![]\\"
exclude_lowercase = false
exclude_punctuation = false
exclude_uppercase = false
include_space = false
require_each_included_type = true
}
# resource "random_password" "this" {
# count = var.generate_secret ? 1 : 0
# length = 22
# special = true
# }
resource "aws_secretsmanager_secret_policy" "policy" {
secret_arn = aws_secretsmanager_secret.secret1.arn
policy = var.secret_policy != null ? var.secret_policy : data.aws_iam_policy_document.policy-file.json
}
data "aws_iam_policy_document" "policy-file" {
statement {
sid = "DenyCrossAccountAccess"
effect = "Deny"
principals {
identifiers = ["*"]
type = "AWS"
}
condition {
test = "StringNotEquals"
values = [data.aws_caller_identity.this.account_id]
variable = "aws:PrincipalAccount"
}
actions = ["secretsmanager:GetSecretValue"]
resources = [aws_secretsmanager_secret.secret1.arn]
}
}
@@ -0,0 +1,12 @@
output "secret_arn" {
value = aws_secretsmanager_secret.secret1.arn
}
output "secret_id" {
value = "${var.secret_name}-${random_id.rid.dec}"
}
# output "generated_password" {
# value = try(random_password.this[0].result, "None")
# sensitive = true
# }
@@ -0,0 +1,9 @@
terraform {
required_version = ">= 1.3.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 5.0"
}
}
}
@@ -0,0 +1,23 @@
variable "secret_description" {}
variable "secret_name" {}
variable "secret_value" {
type = string
default = null
}
variable "secret_policy" {
type = string
default = null
description = "By default, cross-account access is denied"
}
variable "generate_secret" {
type = bool
default = false
description = "If set to true, a secure password will be generated and saved."
}
variable kms_key_id {
type = string
default = null
description = "Custom kms key id. If not specified, the default key aws/secretmanager key will be used."
}
@@ -0,0 +1,13 @@
data aws_region this-region {}
resource "aws_securityhub_account" "sh-account" {}
resource "aws_securityhub_standards_subscription" "cis" {
depends_on = [aws_securityhub_account.sh-account]
standards_arn = "arn:aws:securityhub:::ruleset/cis-aws-foundations-benchmark/v/1.2.0"
}
resource "aws_securityhub_standards_subscription" "aws" {
depends_on = [aws_securityhub_account.sh-account]
standards_arn = "arn:aws:securityhub:${data.aws_region.this-region.name}::standards/aws-foundational-security-best-practices/v/1.0.0"
}
@@ -0,0 +1,3 @@
# Module sso-aws-id-store
This module creates aws sso user using aws's builtin identity store, and put the user in a group.
The group must be created in advance.
@@ -0,0 +1,33 @@
data "aws_ssoadmin_instances" "sso1" {}
resource "aws_identitystore_user" "sso-user" {
identity_store_id = tolist(data.aws_ssoadmin_instances.sso1.identity_store_ids)[0]
display_name = "${var.firstName} ${var.lastName}"
user_name = var.username
nickname = var.username
emails {
primary = true
value = var.email
}
name {
family_name = var.lastName
given_name = var.firstName
}
}
data "aws_identitystore_group" "sso-group" {
identity_store_id = tolist(data.aws_ssoadmin_instances.sso1.identity_store_ids)[0]
alternate_identifier {
unique_attribute {
attribute_path = "DisplayName"
attribute_value = var.groupName
}
}
}
resource "aws_identitystore_group_membership" "sso-group-membership" {
identity_store_id = tolist(data.aws_ssoadmin_instances.sso1.identity_store_ids)[0]
group_id = data.aws_identitystore_group.sso-group.group_id
member_id = aws_identitystore_user.sso-user.user_id
}
@@ -0,0 +1,5 @@
variable username {}
variable firstName {}
variable lastName {}
variable email {}
variable groupName {}
@@ -0,0 +1,33 @@
# SSO permission set module
## Root module example
```
module sso {
source = "../modules/sso"
for_each = { for item in local.items : item.name => item }
default-tags = local.default-tags
pset-name = each.value.name
pset-desc = each.value.desc
pset-managed-policy-arn = each.value.mpolicy
pset-session-duration = each.value.session
}
locals {
csv_data = <<-CSV
name,desc,mpolicy,session
ViewOnly,View only access,arn:aws:iam::aws:policy/job-function/ViewOnlyAccess,PT4H
ReadOnly,Read only access,arn:aws:iam::aws:policy/ReadOnlyAccess,PT4H
FullAccess,Full admin access,arn:aws:iam::aws:policy/AdministratorAccess,PT4H
NetworkAdmin,Network admin access,arn:aws:iam::aws:policy/job-function/NetworkAdministrator,PT4H
DatabaseAdmin,Database admin access,arn:aws:iam::aws:policy/job-function/DatabaseAdministrator,PT4H
BillingAdmin,Billing admin access,arn:aws:iam::aws:policy/job-function/Billing,PT4H
SecurityAudit,Security admin access,arn:aws:iam::aws:policy/SecurityAudit,PT4H
PowerUser,Full access excluding IAM,arn:aws:iam::aws:policy/PowerUserAccess,PT4H
CSV
items = csvdecode(local.csv_data)
}
```
@@ -0,0 +1,25 @@
data "aws_ssoadmin_instances" "sso1" {}
resource "aws_ssoadmin_permission_set" "pset" {
name = var.pset-name
description = var.pset-desc
instance_arn = tolist(data.aws_ssoadmin_instances.sso1.arns)[0]
session_duration = var.pset-session-duration
tags = var.default-tags
}
resource "aws_ssoadmin_managed_policy_attachment" "psetatt" {
instance_arn = tolist(data.aws_ssoadmin_instances.sso1.arns)[0]
managed_policy_arn = var.pset-managed-policy-arn
permission_set_arn = aws_ssoadmin_permission_set.pset.arn
}
# use inline policy for additional permissions. aws sso will populate this policy to target accounts
# automatically. customer managed policies, on the other hand, needs to be created manually in the target accounts.
resource "aws_ssoadmin_permission_set_inline_policy" "pset-inline-policy1" {
count = length(var.inline-policy-json) > 0 ? 1 : 0
instance_arn = tolist(data.aws_ssoadmin_instances.sso1.arns)[0]
permission_set_arn = aws_ssoadmin_permission_set.pset.arn
inline_policy = var.inline-policy-json
}
@@ -0,0 +1,7 @@
output pset-name {
value = aws_ssoadmin_permission_set.pset.name
}
output pset-arn {
value = aws_ssoadmin_permission_set.pset.arn
}
@@ -0,0 +1,6 @@
variable pset-name {}
variable pset-desc {}
variable pset-session-duration {}
variable default-tags {}
variable pset-managed-policy-arn {}
variable inline-policy-json {}
@@ -0,0 +1,96 @@
module "terraform-user" {
source = "../iam-user"
create-access-key = true
create-password = false
default-tags = var.default-tags
iam-user-name = "${var.user-name}-${formatdate("YYYYMMDD_hhmm", timestamp())}"
managed-policy-arns = lookup(local.CannedPoliciesByServiceCategory, var.service-category)
pgp-key = var.gpg-key
}
locals {
CannedPoliciesByServiceCategory = {
NetworkingContentDelivery = [
"arn:aws:iam::aws:policy/NetworkAdministrator",
"arn:aws:iam::aws:policy/AmazonRoute53FullAccess",
"arn:aws:iam::aws:policy/GlobalAcceleratorFullAccess"
]
SecurityIdentityCompliance = [
"arn:aws:iam::aws:policy/IAMFullAccess",
"arn:aws:iam::aws:policy/SecurityAudit",
"arn:aws:iam::aws:policy/AWSSecurityHubFullAccess",
"arn:aws:iam::aws:policy/AmazonGuardDutyFullAccess",
"arn:aws:iam::aws:policy/AmazonInspectorFullAccess",
"arn:aws:iam::aws:policy/AWSSSODirectoryAdministrator",
"arn:aws:iam::aws:policy/AWSOrganizationsFullAccess",
"arn:aws:iam::aws:policy/WellArchitectedConsoleFullAccess",
"arn:aws:iam::aws:policy/AWSKeyManagementServicePowerUser",
"arn:aws:iam::aws:policy/AWSDirectoryServiceFullAccess"
]
ManagementGovernance = [
"arn:aws:iam::aws:policy/CloudWatchFullAccess",
"arn:aws:iam::aws:policy/CloudWatchLogsFullAccess",
"arn:aws:iam::aws:policy/CloudWatchEventsFullAccess",
"arn:aws:iam::aws:policy/AmazonEventBridgeFullAccess",
"arn:aws:iam::aws:policy/AmazonSSMFullAccess",
"arn:aws:iam::aws:policy/AWSResourceAccessManagerFullAccess",
"arn:aws:iam::aws:policy/AWSOrganizationsFullAccess",
"arn:aws:iam::aws:policy/AmazonSQSFullAccess",
"arn:aws:iam::aws:policy/AmazonSNSFullAccess",
"arn:aws:iam::aws:policy/AWSCloudFormationFullAccess"
]
Compute = [
"arn:aws:iam::aws:policy/AmazonEC2FullAccess",
"arn:aws:iam::aws:policy/AmazonWorkSpacesAdmin",
"arn:aws:iam::aws:policy/AWSMarketplaceFullAccess",
"arn:aws:iam::aws:policy/AutoScalingFullAccess",
"arn:aws:iam::aws:policy/AWSImageBuilderFullAccess",
"arn:aws:iam::aws:policy/AWSBackupFullAccess"
]
Containers = [
"arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryFullAccess",
"arn:aws:iam::aws:policy/AmazonECS_FullAccess",
"arn:aws:iam::aws:policy/AmazonEC2FullAccess"
]
Storage = [
"arn:aws:iam::aws:policy/AmazonS3FullAccess",
"arn:aws:iam::aws:policy/AmazonEC2FullAccess",
"arn:aws:iam::aws:policy/AmazonElasticFileSystemFullAccess",
"arn:aws:iam::aws:policy/AmazonFSxFullAccess",
"arn:aws:iam::aws:policy/AmazonGlacierFullAccess",
"arn:aws:iam::aws:policy/AWSBackupFullAccess"
]
Database = [
"arn:aws:iam::aws:policy/DatabaseAdministrator",
"arn:aws:iam::aws:policy/AWSBackupFullAccess"
]
DeveloperTools = [
"arn:aws:iam::aws:policy/AWSCodeCommitFullAccess",
"arn:aws:iam::aws:policy/AWSCodeBuildAdminAccess",
"arn:aws:iam::aws:policy/AWSCodePipeline_FullAccess"
]
Analytics = [
"arn:aws:iam::aws:policy/AmazonOpenSearchServiceFullAccess",
"arn:aws:iam::aws:policy/AmazonMSKFullAccess",
"arn:aws:iam::aws:policy/AmazonEMRFullAccessPolicy_v2",
"arn:aws:iam::aws:policy/AmazonRedshiftFullAccess"
]
MachineLearning = [
"arn:aws:iam::aws:policy/AmazonSageMakerFullAccess",
"arn:aws:iam::aws:policy/AmazonMachineLearningFullAccess",
"arn:aws:iam::aws:policy/AWSGlueConsoleFullAccess",
"arn:aws:iam::aws:policy/AWSStepFunctionsFullAccess"
]
Serverless = [
"arn:aws:iam::aws:policy/AWSLambda_FullAccess",
"arn:aws:iam::aws:policy/AdministratorAccess-AWSElasticBeanstalk",
"arn:aws:iam::aws:policy/AmazonAPIGatewayAdministrator",
"arn:aws:iam::aws:policy/AWSDirectoryServiceFullAccess",
"arn:aws:iam::aws:policy/AmazonSESFullAccess",
"arn:aws:iam::aws:policy/AmazonWorkSpacesAdmin"
]
}
}
@@ -0,0 +1,6 @@
output keys {
value = {
access-key = module.terraform-user.iam-user-access-key-pgp
secret-key = module.terraform-user.iam-user-secret-key-pgp
}
}
@@ -0,0 +1,9 @@
terraform {
required_version = ">= 1.3.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 4.40"
}
}
}
@@ -0,0 +1,7 @@
variable default-tags {}
variable user-name {
type = string
default = "terraform-role"
}
variable service-category {}
variable gpg-key {}