This is the multi-page printable view of this section.
Click here to print.
Return to the regular view of this page.
Getting Started
Prerequisites, bootstrapping, and first deployment.
This section walks you through getting Org Kickstart deployed into a new AWS account.
Prerequisites
Before running Org Kickstart you need:
- Terraform >= 1.0 (< 2.0)
- AWS CLI configured with credentials for the Management (Payer) account
- An S3 bucket for Terraform remote state
- A few manual “artisanal” steps completed in the AWS console (see Bootstrap)
Steps
- Complete the Bootstrap steps in the AWS Console
- Copy
examples/pipeline
to your own private repo — it includes the Makefile, backend config, and directory layout
- Create
your-org.tfvars and your-org.tfbackend for your organization
(see the Reference for all variables; name them to match your env value)
- Initialize Terraform:
make env=your-org tf-init
- Create the Security Account first (required before full apply):
terraform apply -var-file="your-org.tfvars" -target module.security_account
- Deploy everything:
make env=your-org tf-execute
This runs tf-plan followed by tf-apply — saving the plan, applying it, and writing
output-your-org.json to your state bucket.
For subsequent updates, use:
Using with an Existing Organization
If you already have an AWS Organization, see Importing an Existing Org for guidance on
importing existing resources into Terraform state.
Example tfvars
See the Reference page for a full annotated example. The
examples/pipeline
directory in the repository contains a sample private-repo layout with a Makefile, backend config
template, and scripts for CI/CD deployments.
1 - Bootstrap a New Account
Manual steps required in the AWS Console before running Terraform.
Before Org Kickstart can be deployed, a few steps must be completed via ClickOps in your new AWS
Management (Payer) account. Terraform cannot perform these actions automatically.
Root Account Tasks
Log into the root user of your new AWS “payer” account and complete the following:
- Add MFA to root
- Enable IAM access to billing
- Go to Organizations and create an Organization
- Go to AWS IAM Identity Center (SSO) and enable it
- Add yourself as a user in Identity Center
- Create a Permission Set named
TempAdministratorAccess (4-hour session recommended)
- Assign the Permission Set to the Payer/Management Account for your user
- Activate trusted access for CloudFormation StackSets — click “Activate trusted access with AWS Organizations to use service-managed permissions” (must be done via console)
Log out of root and never use it again.
Note: As of January 2026, Terraform does not support the aws login capability.
An IAM Identity Center or IAM User must be created to run Terraform.
On Your Machine
- Check email and activate your IAM Identity Center account
- Add MFA to your Identity Center account
- Configure AWS credentials in your environment:
aws configure sso
# or
export AWS_PROFILE=your-sso-profile
You are now ready to deploy Org Kickstart.
Next Steps
- Create your
tfvars file — see the Reference for all variables and a full example
- Run
terraform init and your first apply — see Getting Started
2 - Importing an Existing Organization
How to adopt Org Kickstart into an existing AWS Organization.
Org Kickstart can manage an existing AWS Organization. Several resources must be imported into
Terraform state before running terraform apply.
The minimum required imports are the organization, the management account, and the
Security Account (if it already exists). You may also want to import existing accounts,
CloudTrail buckets, billing buckets, and SSO configuration.
Automated Import
The scripts/import_org.sh script generates an import-org.tf file and a TFVars snippet for
your existing accounts.
# From the org-kickstart directory
./scripts/import_org.sh
Review the generated import-org.tf carefully before running Terraform.
Steps
- Run
import_org.sh and review import-org.tf
- Add your CloudTrail or billing bucket to the import file if applicable
- Review SCPs you want to import:
aws organizations list-policies --filter SERVICE_CONTROL_POLICY \
--query 'Policies[].[Id,Name]' --output text
- Iterate with
tf-plan until no unwanted changes appear:
make env=your-org tf-plan
make env=your-org tf-show # review the plan output
- Once satisfied, apply:
make env=your-org tf-apply
- Incrementally enable additional features
Manual Import Examples
Organizational Units
ROOT_OU=$(aws organizations list-roots --query Roots[0].Id --output text)
aws organizations list-organizational-units-for-parent \
--parent-id $ROOT_OU \
--query 'OrganizationalUnits[].[Id,Name]' --output text
import {
to = module.organization.aws_organizations_organizational_unit.TF_VARS_KEY
id = "ou-xxxx-xxxxxxxx"
}
IAM Identity Center (SSO)
To opt out of managing an existing Identity Center, set disable_sso_management = true in your
tfvars. This is recommended for existing orgs with complex SSO configurations.
IDENTITY_STORE_ID=$(aws sso-admin list-instances \
--query Instances[0].IdentityStoreId --output text)
SSO_INSTANCE_ARN=$(aws sso-admin list-instances \
--query Instances[0].InstanceArn --output text)
import {
to = module.organization.aws_identitystore_group.admin_group
id = "$IDENTITY_STORE_ID/$GROUP_ID"
}
import {
to = module.organization.aws_ssoadmin_permission_set.admin_permission_set
id = "$PERMISSION_SET_ARN,$SSO_INSTANCE_ARN"
}
import {
to = module.organization.aws_ssoadmin_managed_policy_attachment.admin_policy_attachments
id = "arn:aws:iam::aws:policy/AdministratorAccess,$PERMISSION_SET_ARN,$SSO_INSTANCE_ARN"
}
CloudTrail
aws cloudtrail list-trails --query Trails[].TrailARN --output text
import {
to = module.organization.aws_s3_bucket.cloudtrail_bucket[0]
id = "YOUR_EXISTING_BUCKET_NAME"
}
import {
to = module.organization.aws_cloudtrail.org_cloudtrail[0]
id = "TRAIL_ARN"
}
Service Control Policies
aws organizations list-policies --filter SERVICE_CONTROL_POLICY \
--query 'Policies[].[Id,Name]' --output text
# Get OUs targeted by a policy
aws organizations list-targets-for-policy --policy-id p-xxxxxx
import {
to = module.organization.module.scp["POLICY_BLOCK_IDENTIFIER_FROM_TFVARS"].aws_organizations_policy.org_policy
id = "p-xxxxxx"
}
Disabling Features for Initial Import
When importing an existing org, you may want to temporarily disable features to prevent
unintended changes on the first apply:
- CloudTrail:
cloudtrail_bucket_name = null
- SSO:
disable_sso_management = true
- Audit Role StackSet:
deploy_audit_role = false
- Security Services: use the
security_services block with disable_* flags
3 - Example tfvars
A complete annotated example tfvars file.
The following is a complete example tfvars file showing all major configuration options.
Copy this as a starting point and customize for your organization.
organization = {
organization_name = "my-org"
payer_name = "My Org Management Account"
payer_email = "aws+payer@example.com"
security_account_name = "my-org-security"
security_account_root_email = "aws+security@example.com"
cloudtrail_bucket_name = "my-org-cloudtrail"
cloudtrail_loggroup_name = "CloudTrail/DefaultLogGroup"
billing_data_bucket_name = "my-org-cur"
cur_report_frequency = "DAILY" # DAILY, HOURLY, or MONTHLY
session_duration = "PT8H"
admin_permission_set_name = "AdministratorAccess"
admin_group_name = "AllAdmins"
disable_sso_management = false
deploy_audit_role = true
audit_role_name = "security-audit"
audit_role_stack_set_template_url = "https://s3.amazonaws.com/pht-cloudformation/aws-account-automation/AuditRole-Template.yaml"
declarative_policy_bucket_name = "my-org-account-status"
vpc_flowlogs_bucket_name = "my-org-flowlogs"
macie_bucket_name = "my-org-macie-findings"
# Custom OUs (in addition to the four required OUs)
organization_units = {
"Platform" = {
name = "Platform"
is_child_of_root = true
}
}
# AWS Accounts
accounts = {
dev = {
account_name = "my-org-dev"
account_email = "aws+dev@example.com"
monthly_budget_amount = 500
}
prod = {
account_name = "my-org-prod"
account_email = "aws+prod@example.com"
parent_ou_name = "Workloads"
}
sandbox = {
account_name = "my-org-sandbox"
account_email = "aws+sandbox@example.com"
parent_ou_name = "Sandbox"
}
sso = {
account_name = "my-org-sso"
account_email = "aws+sso@example.com"
parent_ou_name = "Governance"
delegated_admin = ["sso.amazonaws.com"]
}
}
# Alternate contacts applied to all accounts (can be overridden per account)
global_billing_contact = {
name = "Finance Team"
title = "CFO"
email_address = "billing@example.com"
phone_number = "+14041234567"
}
global_security_contact = {
name = "Security Team"
title = "CISO"
email_address = "security@example.com"
phone_number = "+14041234567"
}
global_primary_contact = {
full_name = "IT Operations"
company_name = "My Org, Inc."
address_line_1 = "123 Main Street"
city = "Atlanta"
state_or_region = "GA"
postal_code = "30332"
country_code = "US"
email_address = "aws@example.com"
phone_number = "+14041234567"
}
# Service Control Policies
service_control_policies = {
deny_root = {
policy_name = "DenyRoot"
policy_description = "Denies use of root user"
policy_json_file = "policies/DenyRootSCP.json"
}
security_controls = {
policy_name = "DefaultSecurityControls"
policy_description = "Base security controls for all accounts"
policy_json_file = "policies/SecurityControlsSCP.json.tftpl"
policy_vars = {
audit_role_name = "security-audit"
}
}
}
# Resource Control Policies
resource_control_policies = {
s3_data_perimeter = {
policy_name = "S3DataPerimeter"
policy_description = "Restricts S3 to principals inside the org"
policy_json_file = "policies/RCP_S3DataPerimeter.json.tftpl"
policy_vars = {
org_id = "o-xxxxxxxxxxxx"
}
}
}
# Declarative Policies
declarative_policies = {
deny_public_ami = {
policy_name = "Block_Public_AMIs"
policy_description = "Deny public sharing of all AMIs"
policy_type = "DECLARATIVE_POLICY_EC2"
policy_json_file = "policies/EC2ImageBPA_DCP.json"
policy_targets = ["Workloads", "Governance", "Sandbox"]
}
}
# Security Services
security_services = {
disable_guardduty = false
disable_securityhub = false
disable_macie = false
}
# Billing Alerts
billing_alerts = {
levels = {
level1 = 100
level2 = 500
oh_shit = 1000
}
subscriptions = ["billing-alerts@example.com"]
}
budget_defaults = {
alert_recipients = ["finance@example.com"]
currency = "USD"
warning_percentage = 80
organizational_budget = 1000
}
}
backend_bucket = "my-org-terraform-state-123456789012"
Backend Config File
Create a my-org.tfbackend file alongside your tfvars:
bucket = "my-org-terraform-state-123456789012"
key = "org-kickstart.tfstate"
region = "us-east-1"
Then initialize with:
terraform init -backend-config="my-org.tfbackend"