Skip to content

Coalfire-CF/terraform-aws-organization

Coalfire

ACE AWS Organizations Terraform Module

Description

This module sets up an AWS Organization with org-level services, including Guard Duty, Security Hub, AWS Config, and Cloudtrail.

FedRAMP Compliance: Moderate, High

Resource List

A high-level list of resources created as a part of this module.

  • AWS Organization with org level services
    • Cloudtrail
  • AWS Organization policy
  • IAM role and policy
  • KMS keys and typically required IAM permissions for commonly used services (S3, DynamoDB, Config).
  • S3 buckets (ORG CloudTrail, Config, Backups, State)

Usage

Example for how to call the module below with generic variables:

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "=4.58"
    }
  }
}

module "aws_org" {
  source = "github.com/Coalfire-CF/terraform-aws-organization"
  service_access_principals = [
    "backup.amazonaws.com",
    "config.amazonaws.com",
    "cloudtrail.amazonaws.com",
    "guardduty.amazonaws.com",
    "malware-protection.guardduty.amazonaws.com",
    "member.org.stacksets.cloudformation.amazonaws.com",
    "ram.amazonaws.com",
    "securityhub.amazonaws.com",
    "servicecatalog.amazonaws.com",
    "ssm.amazonaws.com",
    "sso.amazonaws.com",
    "tagpolicies.tag.amazonaws.com"
  ]
  
  org_account_name      = "${var.resource_prefix}-org-root"
  enabled_policy_types  = ["SERVICE_CONTROL_POLICY"]
  create_org_cloudtrail = var.create_org_cloudtrail
  feature_set           = "ALL"
  aws_region            = var.aws_region
  default_aws_region    = var.default_aws_region
  resource_prefix       = var.resource_prefix

  account_number    = var.account_number
  create_cloudtrail = var.create_cloudtrail
  is_organization   = var.is_organization
  organization_id   = var.organization_id
}

For org deployments where a delegated admin is required/needed:

module "org" {
  ## update source once branch is merged into main
  source = "git::https://github.com/Coalfire-CF/terraform-aws-organization.git?ref=fa-aws-pakpt-update"

  service_access_principals = [
    "backup.amazonaws.com",
    "config.amazonaws.com",
    "cloudtrail.amazonaws.com",
    "guardduty.amazonaws.com",
    "malware-protection.guardduty.amazonaws.com",
    "member.org.stacksets.cloudformation.amazonaws.com",
    "ram.amazonaws.com",
    "securityhub.amazonaws.com",
    "servicecatalog.amazonaws.com",
    "ssm.amazonaws.com",
    "sso.amazonaws.com",
    "tagpolicies.tag.amazonaws.com"
  ]

  org_account_name      = "${var.resource_prefix}-org-root"
  enabled_policy_types  = ["SERVICE_CONTROL_POLICY"]
  create_org_cloudtrail = var.create_org_cloudtrail
  feature_set           = "ALL"
  aws_region            = var.aws_region
  default_aws_region    = var.default_aws_region
  resource_prefix       = var.resource_prefix

  account_number    = var.account_number
  create_cloudtrail = var.create_cloudtrail
  is_organization   = var.is_organization
  organization_id   = var.organization_id
}

## Full Delegate Admin Policy ##
resource "aws_organizations_resource_policy" "admin_delegate" {

  content = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DelegatingAdmin",
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:${local.partition}:iam::${local.mgmt_plane_account_id}:root"
      },
      "Action": [
        "organizations:CreatePolicy",
        "organizations:UpdatePolicy",
        "organizations:DeletePolicy",
        "organizations:AttachPolicy",
        "organizations:DetachPolicy",
        "organizations:EnablePolicyType",
        "organizations:DisablePolicyType",
        "organizations:DescribeOrganization",
        "organizations:DescribeOrganizationalUnit",
        "organizations:DescribeAccount",
        "organizations:DescribePolicy",
        "organizations:DescribeEffectivePolicy",
        "organizations:ListRoots",
        "organizations:ListOrganizationalUnitsForParent",
        "organizations:ListParents",
        "organizations:ListChildren",
        "organizations:ListAccounts",
        "organizations:ListAccountsForParent",
        "organizations:ListPolicies",
        "organizations:ListPoliciesForTarget",
        "organizations:ListTargetsForPolicy",
        "organizations:ListTagsForResource"
      ],
      "Resource": "*"
    }
  ]
}
EOF
}

NOTE: This resource must be commented out on the first run of your terraform plan/apply of aws-org since it will throw an error that an organization is required for this to be created ("Your account is not a member of an organization"). Once you run the first apply and obtain the organization_id, you can add this back in for the second round of terraform apply.

Service Control Policy Usage

Included in the available-SCPs directory are a set of commonly used Service Control Policies, otherwise known as SCPs. These SCPs can be used to strengthen your AWS Organization's security posture

Each of them can be modified to meet your needs, such as partition changes, additional tags, or any additional roles to be permitted to perform certain actions.

This link can help visualize how to assign your SCPs, whether root, OU or account based.

Copy and save any applicable SCP terraform files in the same directory as where you called the terraform-aws-organization module

Below is some examples of how you can use and apply SCPs to your organization.

Creating a policy:

resource "aws_organizations_policy" "DenyLeavingOrg" {
  name    = "Prevent member accounts from leaving the organization"
  content = data.aws_iam_policy_document.example.json
}

Account targeted SCP

resource "aws_organizations_policy_attachment" "ProdDenyOrgLeavy" {
  policy_id = aws_organizations_policy.DenyLeavingOrg.id
  target_id = "123456789012"
}

Org Root targeted SCP

resource "aws_organizations_policy_attachment" "RootDenyOrgLeavy" {
  policy_id = aws_organizations_policy.DenyLeavingOrg.id
  target_id = aws_organizations_organization.gov-org.roots[0].id
}

Org Unit targeted SCP

resource "aws_organizations_policy_attachment" "ProdOUDenyOrgLeavy" {
  policy_id = aws_organizations_policy.DenyLeavingOrg.id
  target_id = aws_organizations_organizational_unit.prod.id
}

Environment Setup

Establish a secure connection to the Management AWS account used for the build:

IAM user authentication:

- Download and install the AWS CLI (https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html)
- Log into the AWS Console and create AWS CLI Credentials (https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-quickstart.html)
- Configure the named profile used for the project, such as 'aws configure --profile example-mgmt'

SSO-based authentication (via IAM Identity Center SSO):

- Login to the AWS IAM Identity Center console, select the permission set for MGMT, and select the 'Access Keys' link.
- Choose the 'IAM Identity Center credentials' method to get the SSO Start URL and SSO Region values.
- Run the setup command 'aws configure sso --profile example-mgmt' and follow the prompts.
- Verify you can run AWS commands successfully, for example 'aws s3 ls --profile example-mgmt'.
- Run 'export AWS_PROFILE=example-mgmt' in your terminal to use the specific profile and avoid having to use '--profile' option.

Deployment

  1. Navigate to the Terraform project and create a parent directory in the upper level code, for example:

    ../{CLOUD}/terraform/{REGION}/management-account/example

    If multi-account management plane:

    ../{CLOUD}/terraform/{REGION}/{ACCOUNT_TYPE}-mgmt-account/example
  2. Create a properly defined main.tf file via the template found under 'Usage' while adjusting 'auto.tfvars' as needed. Example parent directory:

     ├── Example/
     │   ├── example.auto.tfvars   
     │   ├── main.tf
     │   ├── outputs.tf
     │   ├── providers.tf
     │   ├── required-providers.tf
     │   ├── remote-data.tf
     │   ├── variables.tf 
     │   ├── ...

    Example 'auto.tfvars':

    aws_region         = "us-gov-west-1"
    default_aws_region = "us-gov-west-1"
    account_number     = "01010101010101"
    resource_prefix    = "test-org"
    create_cloudtrail = true # This is always set to true to create the cloudtrail S3 bucket using account setup PAK
    
    # Set both to FALSE on first apply, then run again with TRUE to apply the org cloudtrail and organization ID to policies
    create_org_cloudtrail = false
    is_organization       = false
    
    # Uncomment on second run with ORG ID obtained from the output of the first run
    #organization_id      = ""
  3. Configure Terraform local backend and stage remote backend. For the first run, the entire contents of the 'remote-data.tf' file must be commented out with terraform local added to facilitate local state setup, like below:

    //terraform {
    //  backend "s3" {
    //    bucket         = "{resource_prefix}-{region}-tf-state"
    //    region         = "{region}"
    //    key            = "{resource_prefix}-{region}-org-setup.tfstate"
    //    encrypt        = true
    //    use_lockfile   = true
    //  }
    //}
    terraform {
    backend "local"{}
    }
  4. Initialize the Terraform working directory:

    terraform init

    Create an execution plan and verify the resources being created:

    terraform plan

    Apply the configuration:

    terraform apply
  5. After the deployment has succeeded, uncomment the contents of 'remote-state.tf' and remove the terraform local code block.

  6. Follow the directions on the example 'auto.tfvars' previously provided to create cloudtrail and update policies. You will need to update 'create_org_cloudtrail' and 'is_organization' to 'true' while uncommenting and adding 'organization_id' value. You can also add in the Delegate Admin Policy resource now too.

  7. Create an execution plan and verify the resources being created:

    terraform plan

    Apply the configuration:

    terraform apply
  8. Run 'terraform init -migrate-state' and follow the prompts to migrate the local state file to the appropriate S3 bucket in the AWS Master Payer account.

Post Deployment Configuration

Ensure you invite other AWS accounts to the organization, then accept the invitations in each child account.
Depending on the context of the build out of this module, the role OrganizationAccountAccessRole may need to be manually created in each child project. The trust relationship of this role must include other AWS accounts that use cross project resource provisioning. See reference https://docs.aws.amazon.com/organizations/latest/userguide/orgs_manage_accounts_create-cross-account-role.html

Requirements

No requirements.

Providers

Name Version
aws n/a

Modules

Name Source Version
account_setup git::https://github.com/Coalfire-CF/terraform-aws-account-setup v0.2.4

Resources

Name Type
aws_cloudtrail.org-trail resource
aws_iam_role.aws_config_org_role resource
aws_iam_role_policy_attachment.organization resource
aws_organizations_organization.org resource
aws_caller_identity.current data source
aws_iam_policy_document.assume_role data source
aws_iam_policy_document.controltower_kms data source
aws_partition.current data source

Inputs

Name Description Type Default Required
account_number The AWS account number resources are being deployed into string n/a yes
aws_region n/a string n/a yes
config_cross_account_ids AWS account IDs allowed cross-account access to the Config bucket list(string) [] no
create_cloudtrail Whether or not to create cloudtrail resources bool n/a yes
create_org_cloudtrail True/False statement whether to enable AWS Cloudtrail in the Organization bool n/a yes
default_aws_region The default AWS region to create resources in string n/a yes
enabled_policy_types List of Organizations policy types to enable in the Organization Root. Organization must have feature_set set to ALL. For additional information about valid policy types (e.g., AISERVICES_OPT_OUT_POLICY, BACKUP_POLICY, SERVICE_CONTROL_POLICY, and TAG_POLICY) list(string)
[
""
]
no
feature_set Feature set to be used with Org and member accounts Specify ALL(default) or CONSOLIDATED_BILLING. string "ALL" no
is_organization Whether or not to enable certain settings for AWS Organization bool n/a yes
org_account_name value to be used for the org account name string n/a yes
organization_id AWS Organization ID string n/a yes
resource_prefix n/a string n/a yes
service_access_principals List of AWS Service Access Principals that you want to enable for organization integration list(string)
[
"cloudtrail.amazonaws.com",
"config.amazonaws.com",
"config-multiaccountsetup.amazonaws.com",
"member.org.stacksets.cloudformation.amazonaws.com",
"sso.amazonaws.com",
"ssm.amazonaws.com",
"servicecatalog.amazonaws.com",
"guardduty.amazonaws.com",
"controltower.amazonaws.com",
"securityhub.amazonaws.com",
"ram.amazonaws.com",
"tagpolicies.tag.amazonaws.com"
]
no

Outputs

Name Description
accounts List of org accounts including master
additional_kms_key_arns n/a
additional_kms_key_ids n/a
backup_kms_key_arn n/a
backup_kms_key_id n/a
cloudtrail_arn n/a
cloudwatch_kms_key_arn n/a
cloudwatch_kms_key_id n/a
config_kms_key_arn n/a
config_kms_key_id n/a
dynamo_kms_key_arn n/a
dynamo_kms_key_id n/a
dynamodb_table_name n/a
ebs_kms_key_arn n/a
ebs_kms_key_id n/a
eks_node_role_arn n/a
eks_node_role_name n/a
lambda_kms_key_arn n/a
lambda_kms_key_id n/a
master_account_id Master account ID
nfw_kms_key_arn n/a
nfw_kms_key_id n/a
org_id Organization ID
packer_iam_role_arn n/a
packer_iam_role_name n/a
rds_kms_key_arn n/a
rds_kms_key_id n/a
s3_access_logs_arn n/a
s3_access_logs_id n/a
s3_backups_arn n/a
s3_backups_id n/a
s3_cloudtrail_arn n/a
s3_cloudtrail_bucket_arn n/a
s3_cloudtrail_bucket_name n/a
s3_cloudtrail_id n/a
s3_config_arn n/a
s3_config_id n/a
s3_elb_access_logs_arn n/a
s3_elb_access_logs_id n/a
s3_fedrampdoc_arn n/a
s3_fedrampdoc_id n/a
s3_installs_arn n/a
s3_installs_id n/a
s3_kms_key_arn n/a
s3_kms_key_id n/a
s3_tstate_bucket_name n/a
sm_kms_key_arn n/a
sm_kms_key_id n/a
sns_kms_key_arn n/a
sns_kms_key_id n/a

Contributing

Relative or absolute link to contributing.md

License

License

Coalfire Pages

Absolute link to any relevant Coalfire Pages

Copyright

Copyright © 2023 Coalfire Systems Inc.

Tree

.
|-- CHANGELOG.md
|-- CONTRIBUTING.md
|-- LICENSE
|-- README.md
|-- account-setup.tf
|-- available-SCPs
|   |-- DenyLeaveOrg.tf
|   |-- S3_public_access_block.tf
|   |-- aws_region_lock.tf
|   |-- deny-resource-creation-without-tags.tf
|   |-- disable_use_of_instance_types.tf
|   |-- mfa_required_changes.tf
|   |-- prevent_aws_iam_changes.tf
|   |-- prevent_changes_to_cloudtrail.tf
|   |-- prevent_changes_to_guardduty.tf
|   |-- prevent_vpcflowlog_deletion.tf
|-- cloudtrail.tf
|-- coalfire_logo.png
|-- data.tf
|-- iam.tf
|-- org.tf
|-- outputs.tf
|-- release-please-config.json
|-- variables.tf

Contributors

Languages