Skip to content

A collection of Terraform "core" modules I would consider to be building blocks of any reasonable AWS account setup

License

Notifications You must be signed in to change notification settings

moritzheiber/terraform-aws-core-modules

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

51 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

AWS Core Modules

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!

Available modules

Config

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 or disable_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 a identifier = { parameter = value } map.

For both cases you need to use their uppercase, snake case identifier (e.g. autoscaling-capacity-rebalancing becomes AUTOSCALING_CAPACITY_REBALANCING)

Special cases

For a few rules there is special treatment using variables:

  • IAM_PASSWORD_POLICY: See the password_policy variable
  • IAM_USER_GROUP_MEMBERSHIP_CHECK: See the iam_user_groups variable
  • APPROVED_AMIS_BY_TAG: See the amis_by_tag_key_and_value_list variable
  • ACCESS_KEYS_ROTATED: See the max_access_key_age variable
  • DESIRED_INSTANCE_TYPE: See the desired_instance_types variable (_Note: the identifier says type but this is a list_)

You can disable any of these complex rules by simply unsetting the corresponding variable.

Requirements

Name Version
terraform >= 1
aws ~> 4

Inputs

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))
{
"CLOUDWATCH_ALARM_ACTION_CHECK": {
"alarmActionRequired": "true",
"insufficientDataActionRequired": "false",
"okActionRequired": "false"
}
}
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)
[
"INSTANCES_IN_VPC",
"EC2_VOLUME_INUSE_CHECK",
"EIP_ATTACHED",
"ENCRYPTED_VOLUMES",
"INCOMING_SSH_DISABLED",
"CLOUD_TRAIL_ENABLED",
"IAM_GROUP_HAS_USERS_CHECK",
"IAM_USER_NO_POLICIES_CHECK",
"ROOT_ACCOUNT_MFA_ENABLED",
"S3_BUCKET_PUBLIC_READ_PROHIBITED",
"S3_BUCKET_PUBLIC_WRITE_PROHIBITED",
"S3_BUCKET_SSL_REQUESTS_ONLY",
"S3_BUCKET_SERVER_SIDE_ENCRYPTION_ENABLED",
"S3_BUCKET_VERSIONING_ENABLED",
"EBS_OPTIMIZED_INSTANCE",
"AUTOSCALING_GROUP_ELB_HEALTHCHECK_REQUIRED",
"RDS_INSTANCE_PUBLIC_ACCESS_CHECK",
"RDS_SNAPSHOTS_PUBLIC_PROHIBITED",
"IAM_POLICY_NO_STATEMENTS_WITH_ADMIN_ACCESS",
"IAM_ROOT_ACCESS_KEY_CHECK"
]
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)
{
"max_password_age": "90",
"minimum_password_length": "32",
"password_reuse_prevention": "5",
"require_lowercase_chars": "true",
"require_numbers": "true",
"require_symbols": "true",
"require_uppercase_chars": "true"
}
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

Outputs

Name Description
config_s3_bucket_arn The ARN of the S3 bucket AWS Config writes its findings into

iam-users

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:

AWS IAM setup illustration

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.

Requirements

Name Version
terraform >= 1
aws ~> 4

Inputs

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)
{
"max_password_age": "90",
"minimum_password_length": "32",
"password_reuse_prevention": "5",
"require_lowercase_chars": "true",
"require_numbers": "true",
"require_symbols": "true",
"require_uppercase_chars": "true"
}
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

Outputs

Name Description
admin_group_names The names of the admin groups
user_group_names The name of the user groups

iam-resources

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.

Usage example

module "iam_resources" {
  source            = "git::https://github.com/moritzheiber/terraform-aws-core-modules.git//iam-resources"

Requirements

Name Version
terraform >= 1
aws ~> 4

Inputs

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

Outputs

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

vpc

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):

AWS VPC illustration

Usage example

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.

Requirements

Name Version
terraform >= 1
aws ~> 4

Inputs

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

Outputs

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