0.3.0
New Features
- Add a generic CloudFormation Stackset Capability
- Add
payer_cloudformation_stacksandsecurity_account_stacksto deploy arbitrary CloudFormation stacks into the payer and security accounts directly from Terraform. Each is a map keyed by an HCL identifier and expanded across optionalregions(composite addresses likeaws_cloudformation_stack.payer["billing_alerts-us-east-1"]so adding a region later doesn't rename existing stacks). Templates come from a localtemplate_file(relative topath.root) or an HTTPS/S3template_url— exactly one is required. - Optionally manage the Terraform state bucket. With
manage_state_bucket = true(the default), org-kickstart adopts the existingbackend_bucket— via animportblock in the calling module (Terraform ignores import blocks inside child modules), seeexamples/local-deploy/import.tf— and enforces versioning, public-access-block, and AES256 encryption on it. The bucket hasprevent_destroyset so Terraform will never delete it. Setmanage_state_bucket = falseto leave the bucket unmanaged. examples/local-deploy/import.tfadopts the three foundational resources (the AWS Organization, the payer account, and the state bucket) declaratively viaimportblocks, usingdata.aws_organizations_organizationfor the org and payer IDs. A brand-new org no longer needs to runscripts/import_org.shfor these — that script is now only required to adopt an existing org's accounts, OUs, and policies.- Account attribute allows you to specify which AWS Organizations services the account is Delegated Admin for
- Add Organizational Budget support
- Add individual account budget support
- Add a (required) security_account block to hold settings for the security account. This gives the security account configuration feature parity with all the other accounts.
- Add the ability to set the close-on-deletion setting for when an account is removed from the org.
- Add optional support for DataTrails PR14
- The Security Account is made the delegated administrator for Personal Health Dashboard - it will receive PHD events from all accounts in the organization
- Ability to control stackset delegation to the security account. Thanks Ashex!
- Ability to explicitly exclude certian
aws_service_access_principalsandenabled_policy_types, viaaws_service_access_principals_to_excludeandorganization_policy_types_to_excludeThanks Ashex! - New script to generate a granted config from the output of the account list.
Major Breaking Change
- The Security Account is no longer default assigned as Delegated Admin for IAM Identity Center (sso).
- A security account block is now required in the tfvars file. SSO Delegated Admin can be applied here if needed.
Minor Updates
- New EC2 Image Declarative policy to limit AMIs to only AWS & Marketplace
- WARNING: This may prevent org-shared AMI Launchings. Caveat Emptor
- Added EC2ImageSnapshotBPA_ALLOW_DCP.json declarative policy to override the Block of AMIs & Snapshots
- Improved Variable and Validations - Thanks skwashd
- Fixing a number of things flagged by Checkov:
- Added a aws_s3_bucket_public_access_block to the billing_logs bucket
- configure AWS KMS on the cloudtrail_s3_notification_topic and billing_alerts SNS Topics
- Fixed the block public access setting on VPC FlowLogs to restrict_public_buckets
- Output Account Listing - Thanks skwashd
- Enable User Notification service for Trusted access (Note: tf provider support doens't yet exist to configure this service.)
- Account OUs can now be specified by both OU Name and OU ID.
examples/local-deploygained anaccount-configuratorMake target that pulls in the pht-account-configurator submodule, seeds<env>-account-config.yamlfrom the sample if missing, packages the CloudFormation template into the state bucket, and deterministically rewrites theaccount_configuratorstanza in<env>.tfvars(removing any stale or commented-out copy) sotemplate/account_factory_config_filepoint at the freshly packaged template and config — thenmake tf-applydeploys it. See the Account Configurator docs.examples/local-deployREADME trimmed to a concise day-to-day operations guide that points to aws-kickstart.org for full docs, setup, and bootstrap steps;.gitignoresynced with the reference deployment.- Added
examples/local-deployas the sample layout for running Org Kickstart from a workstation (theMakefileflow); it is now the canonical sample referenced by the docs. (examples/pipelineis retained for in-progress CI/CD tooling.) - README slimmed to a project overview that links to aws-kickstart.org for full docs, with release/versioning info at the top; Future Work moved to
TODO.md.
Minor Breaking Change
-
PR#9 changed how the SCP attachments are named, rather than being based on count, they are based on foreach. This means that the SCP/RCP/DP attachments will be destroyed and recreated as such
# module.organization.module.scp["deny_root"].aws_organizations_policy_attachment.scp_attachment[0] will be destroyed # (because resource does not use count) - resource "aws_organizations_policy_attachment" "scp_attachment" { - id = "r-xxxx:p-yyyyy" -> null - policy_id = "p-yyyyy" -> null - target_id = "r-xxxx" -> null } # module.organization.module.scp["deny_root"].aws_organizations_policy_attachment.scp_attachment["Root"] will be created + resource "aws_organizations_policy_attachment" "scp_attachment" { + id = (known after apply) + policy_id = "p-yyyyy" + target_id = "r-xxxx" }This is a cleaner way to handle the attachments, and won't risk further unexpected changes if the list of attachment OUs change and a reindex occurs.
-
The SCP/RCP/Decalaritive Policies have been refactored into a single module for any form or Organizational Policy. This caused resource renaming to occur. So in addition to the Policy attachments changing, the policies themselves will be renamed. You can preserve them via
terraform state mvlike so:terraform state mv 'module.organization.module.scp["deny_root"].aws_organizations_policy.scp' 'module.organization.module.scp["deny_root"].aws_organizations_policy.org_policy'
Otherwise they'll be recreated.
Bug Fixes
- Added explicit
depends_onto several Organizations resources that previously raced creation on a first apply:- The AI opt-out policy (
aws_organizations_policy.ai_policy), RAM organization sharing (aws_ram_sharing_with_organization.enable), and the SCP / RCP / Declarative policy modules now depend onaws_organizations_organization.org. These resources only referenced the org indirectly (through a separate policy attachment), so Terraform could create them before the org existed, producingAWSOrganizationsNotInUseException("Your account is not a member of an organization") errors. - The CUR report definition (
aws_cur_report_definition.cur_report_definition) now depends on the billing bucket policy (aws_s3_bucket_policy.allow_billing_logging) so CUR's create-time bucket-permission check doesn't run before the policy is in place.
- The AI opt-out policy (
- Marked the
grantedMakefile target.PHONYsomake env=<env> grantedruns even when agranted/directory exists (Make previously reportedgranted is up to dateand skipped the script). - The
grantedMakefile target now writes its output togranted/aws-config(creating the directory if needed) instead of stdout, so it populates the path the samplegranted.ymlreferences for use as a Granted Registry.
Known Bugs
- You can't use the
parent_ou_namefor OUs that are not direct descendants of the Root OU
Migrating Security Services from Region Based Providers to Region Variable.
With the introduction of the region variable in the 6.x version of the AWS Provider, the need for the generate_regions.sh script has gone away. If you were managing security services via the dedicated providers for payer and security account, you will need to tell terraform to "move" them to the new resource locations.
This migration script should handle generating the moved resources:
#!/bin/bash
> moves.tf
REGIONS=`aws ec2 describe-regions | jq -r '.Regions[].RegionName'`
for r in $REGIONS ; do
cat <<EOF >> moves.tf
# $r
moved {
from = module.security-services-$r.aws_guardduty_detector.payer_detector[0]
to = module.organization.module.security-services["$r"].aws_guardduty_detector.payer_detector[0]
}
moved {
from = module.security-services-$r.aws_guardduty_detector.security_detector[0]
to = module.organization.module.security-services["$r"].aws_guardduty_detector.security_detector[0]
}
moved {
from = module.security-services-$r.aws_guardduty_organization_admin_account.guardduty[0]
to = module.organization.module.security-services["$r"].aws_guardduty_organization_admin_account.guardduty[0]
}
moved {
from = module.security-services-$r.aws_guardduty_organization_configuration.organization[0]
to = module.organization.module.security-services["$r"].aws_guardduty_organization_configuration.organization[0]
}
EOF
done