This is a collection of Terraform "core" modules I would consider to be building blocks of every reasonable AWS account setup. Please refer to to the AWS Kickstarter to see their application.
Contributions are more than welcome and encouraged!
The module configures AWS Config to monitor your account for non-compliant resources.
You can freely choose which checks to use or discard by modifying the enable_config_rules
, disable_config_rules
and complex_config_rules
variables.
As an example, if you'd wish to enable AUTOSCALING_CAPACITY_REBALANCING
and disable the INSTANCES_IN_VPC
check, which is enabled by default, you could use the following code:
module "aws_config" {
source = "git::https://github.com/moritzheiber/terraform-aws-core-modules//config"
enable_simple_rules = ["AUTOSCALING_CAPACITY_REBALANCING"]
disable_simple_rules = ["INSTANCE_IN_VPC"]
}
If you wanted to change parameters on the CLOUDWATCH_ALARM_ACTION_CHECK
complex rule you could pass it to the complex_config_rules
variable:
module "aws_config" {
source = "git::https://github.com/moritzheiber/terraform-aws-core-modules//config"
complex_config_rules = {
CLOUDWATCH_ALARM_ACTION_CHECK = {
alarmActionRequired = "false"
insufficientDataActionRequired = "true"
okActionRequired = "true"
}
}
}
For a list of available managed rules you can refer to the AWS Config documentation. As a rule of thumb:
- if they require no parameters you can use either
enable_config_rules
ordisable_config_rules
to manage them. - if they require parameters you can use the
complex_config_rules
map to add them and their input parameters as aidentifier = { parameter = value }
map.
For both cases you need to use their uppercase, snake case identifier (e.g. autoscaling-capacity-rebalancing
becomes AUTOSCALING_CAPACITY_REBALANCING
)
For a few rules there is special treatment using variables:
IAM_PASSWORD_POLICY
: See thepassword_policy
variableIAM_USER_GROUP_MEMBERSHIP_CHECK
: See theiam_user_groups
variableAPPROVED_AMIS_BY_TAG
: See theamis_by_tag_key_and_value_list
variableACCESS_KEYS_ROTATED
: See themax_access_key_age
variableDESIRED_INSTANCE_TYPE
: See thedesired_instance_types
variable (_Note: the identifier saystype
but this is a list_)
You can disable any of these complex rules by simply unsetting the corresponding variable.
Name | Version |
---|---|
terraform | >= 1 |
aws | ~> 4 |
Name | Description | Type | Default | Required |
---|---|---|---|---|
amis_by_tag_key_and_value_list | Required AMI tags for EC2 instances | list(string) |
[] |
no |
bucket_account_id | The AWS account ID the S3 bucket lives in that AWS Config is writing its records to. Defaults to the ID of the current account | string |
"" |
no |
bucket_key_prefix | The prefix of the keys AWS Config writes to | string |
"aws_config" |
no |
bucket_prefix | The prefix for the S3 bucket AWS Config Recorder writes to | string |
"aws-config" |
no |
complex_config_rules | A range of more complex Config rules you wish to have applied. They usually carry input parameters. | map(map(string)) |
{ |
no |
config_delivery_channel_name | The name of the delivery channel for AWS Config | string |
"config" |
no |
config_recorder_name | The name of the recorder for AWS Config | string |
"config" |
no |
delivery_frequency | The frequency at which AWS Config delivers its recorded findings to S3 | string |
"Three_Hours" |
no |
desired_instance_types | A string of comma-delimited instance types | set(string) |
[] |
no |
disable_config_rules | A set with simple rules you wish to disable. Otherwise all the rules are applied by default. | set(string) |
[] |
no |
enable_config_rules | A set with simple rules you wish to enable. The defaults are pretty solid. If you wish to only disable a few rules take a look at the 'disable_config_rules' variable. | set(string) |
[ |
no |
enable_lifecycle_management_for_s3 | Whether or not to enable lifecycle management for the S3 bucket AWS Config writes to | bool |
false |
no |
iam_role_name | The name of the IAM role created for delegating permissions to AWS Config | string |
"config" |
no |
iam_user_groups | A list of mandatory groups for IAM users | list(string) |
[] |
no |
lifecycle_bucket_expiration | The number of days after which artifacts in the Config S3 bucket are expiring | number |
365 |
no |
max_access_key_age | The maximum amount of days an access key can live without being rotated | string |
"90" |
no |
password_policy | A map of values describing the password policy parameters AWS Config is looking for | map(string) |
{ |
no |
s3_kms_sse_encryption_key_arn | The ARN for the KMS key to use for S3 server-side bucket encryption. If none if specified the module creates a KMS key for customer managed encryption. | string |
"" |
no |
Name | Description |
---|---|
config_s3_bucket_arn | The ARN of the S3 bucket AWS Config writes its findings into |
A module to configure the "users" account modeled after a common security principle of separating users from resource accounts through a MFA-enabled role-assumption bridge:
These strict separation of privileges follow an article I wrote a while ago.
You can also create IAM users and IAM groups with this module and assign the users to specific groups. The module will create two default groups, one for admins and users, which you can disable by setting the admin_group_name
and user_group_name
to an empty string.
Creating additional users is done by passing a map called users
to the module, with a group mapping attached to them (the best practice is to never have users live "outside" of groups).
variable "iam_users" {
type = map(map(set(string)))
default = {
my_user = {
groups = ["admins"]
}
}
}
module "iam_users" {
source = "git::https://github.com/moritzheiber/terraform-aws-core-modules.git//iam-users"
iam_users = var.iam_users
}
This will run the module and create all the necessary permissions along with a user belonging to the admins
groups.
Name | Version |
---|---|
terraform | >= 1 |
aws | ~> 4 |
Name | Description | Type | Default | Required |
---|---|---|---|---|
additional_admin_groups | A list of additional groups to create associated with administrative privileges | list(string) |
[] |
no |
additional_user_groups | A list of additional groups to create associated with regular users | list(string) |
[] |
no |
admin_group_name | The name of the initial group created for administrators | string |
"admins" |
no |
admin_multi_factor_auth_age | The amount of time (in minutes) for a admin session to be valid | number |
60 |
no |
iam_account_alias | A globally unique, human-readable identifier for your AWS account | string |
null |
no |
iam_users | A list of maps of users and their groups. Default is to create no users. | map(map(list(string))) |
{} |
no |
password_policy | A map of password policy parameters you want to set differently from the defaults | map(string) |
{ |
no |
resource_admin_role_name | The name of the administrator role one is supposed to assume in the resource account | string |
"resource-admin" |
no |
resource_user_role_name | The name of the user role one is supposed to assume in the resource account | string |
"resource-user" |
no |
resources_account_id | The account ID of the AWS account you want to start resources in | string |
"" |
no |
user_group_name | The name of the initial group created for users | string |
"users" |
no |
user_multi_factor_auth_age | The amount of time (in minutes) for a user session to be valid | number |
240 |
no |
Name | Description |
---|---|
admin_group_names | The names of the admin groups |
user_group_names | The name of the user groups |
A module to configure the "resources" account modelled after the common security principle of separating users from resource accounts through a MFA-enabled role-assumption bridge. Please see the iam-users module for further explanation. It is generally assumed that this module isn't deployed on its own.
module "iam_resources" {
source = "git::https://github.com/moritzheiber/terraform-aws-core-modules.git//iam-resources"
Name | Version |
---|---|
terraform | >= 1 |
aws | ~> 4 |
Name | Description | Type | Default | Required |
---|---|---|---|---|
admin_access_role_name | Name of the admin role | string |
"resource-admin" |
no |
admin_multi_factor_auth_age | The amount of time (in minutes) for a admin session to be valid | number |
60 |
no |
iam_account_alias | A globally unique identifier, human-readable for your AWS account | string |
null |
no |
user_access_role_name | Name of the user role | string |
"resource-user" |
no |
user_multi_factor_auth_age | The amount of time (in minutes) for a user session to be valid | number |
240 |
no |
users_account_id | The account ID of where the users are living in | string |
null |
no |
Name | Description |
---|---|
resource_admin_role_arn | The ARN of the role users are able to assume to attain admin privileges |
resource_admin_role_name | The name of the role users are able to assume to attain admin privileges |
resource_user_role_arn | The ARN of the role users are able to assume to attain user privileges |
resource_user_role_name | The name of the role users are able to assume to attain user privileges |
This module builds a VPC with the default CIDR range of 10.0.0.0/16
, three subnets in a "public" configuration (attached to and routed through an AWS Internet Gateway) and three subnets in a "private" configuration (attached to and routed through three separate AWS NAT Gateways):
Add the following statement to your variables.tf
to use the vpc
module:
module "core_vpc" {
source = "git::https://github.com/moritzheiber/terraform-aws-core-modules.git//vpc"
tags = {
Resource = "my_team_name"
Cost_Center = "my_billing_tag"
}
}
and run terraform init
to download the required module files.
All created subnets will have a tag attached to them which specifies their scope (i.e. "public" for public subnets and "private" for private subnets) which you can use to filter for the right networks using Terraform data sources:
data "aws_vpc" "core" {
tags = {
# `core_vpc` is the default, the variable is `vpc_name`
Name = "core_vpc"
}
}
data "aws_subnets" "public" {
filter {
name = "vpc-id"
values = [data.aws_vpc.core.id]
}
tags = {
Scope = "Public"
}
}
data "aws_subnets" "public" {
filter {
name = "vpc-id"
values = [data.aws_vpc.core.id]
}
tags = {
Scope = "Private"
}
}
The result is a list of subnet IDs, either in the public or private VPC zone, you can use to create other resources such as Load Balancers or AutoScalingGroups.
Name | Version |
---|---|
terraform | >= 1 |
aws | ~> 4 |
Name | Description | Type | Default | Required |
---|---|---|---|---|
enable_dns_hostnames | Whether or not to enable VPC DNS hostname support | bool |
true |
no |
enable_dns_support | Whether or not to enable VPC DNS support | bool |
true |
no |
private_subnet_offset | The amount of IP space between the public and the private subnet | number |
2 |
no |
private_subnet_prefix | The prefix to attach to the name of the private subnets | string |
"" |
no |
private_subnet_size | The size of the private subnet (default: 1022 usable addresses) | number |
6 |
no |
public_subnet_prefix | The prefix to attach to the name of the public subnets | string |
"" |
no |
public_subnet_size | The size of the public subnet (default: 1022 usable addresses) | number |
6 |
no |
tags | A map of tags to apply to all VPC resources | map(string) |
{} |
no |
vpc_cidr_range | The IP address space to use for the VPC | string |
"10.0.0.0/16" |
no |
vpc_name | The name of the VPC | string |
"core_vpc" |
no |
Name | Description |
---|---|
private_subnet_ids | A list of private subnet IDs |
public_subnet_ids | A list of public subnet IDs |
vpc_id | The ID of the created VPC |