Skip to main content

NTC Account Factory

Release Notes Source Code Implementation Blueprint

Description​

NTC Account Factory simplifies the creation and management of AWS accounts, providing a robust foundation for scalable, secure, and compliant multi-account environments. This building block automates account provisioning, integrates with your organizational structure, and applies a consistent account baseline to ensure governance at scale. The account baseline enforces guardrails, security configurations, and can be completely customized, ensuring every account aligns with organizational standards.

With NTC Account Factory, you can rapidly deploy accounts tailored to specific workloads, environments, or business units, streamlining your AWS Landing Zone setup and accelerating cloud adoption.

Usage​

Latest Release1.8.4
# --------------------------------------------------------------------------------------------------
# Β¦ NTC ACCOUNT FACTORY
# --------------------------------------------------------------------------------------------------
module "ntc_account_factory" {
source = "github.com/nuvibit-terraform-collection/terraform-aws-ntc-account-factory?ref=X.X.X"

# this bucket stores required files for account factory
account_factory_baseline_bucket_name = "ntc-account-factory-baseline"

# this bucket stores required cloudtrail logs for account factory
account_factory_cloudtrail_bucket_name = "ntc-account-factory-cloudtrail"

# increase quotas for aws services used in account factory
increase_aws_service_quotas = {
codebuild_concurrent_runs_arm_small = 20
}

# account names and emails cannot be changed without manual intervention - set naming conventions to avoid mistakes
account_factory_naming_conventions = {
account_name_regex = "^aws-c2-[a-z0-9-]+$"
account_email_regex = "@nuvibit.com$"
}

# can be stored as HCL or alternatively as JSON for easy integration e.g. self service portal integration via git
account_factory_list = [
{
"account_name": "aws-c2-demo",
"account_email": "demo-account@nuvibit.com",
"ou_path": "/root/workloads/dev",
"close_on_deletion": true,
"account_tags": {
"AccountOwner": "demo-admin@nuvibit.com",
"AccountType": "workload",
"AccountDescription": "This is a demo account",
"AccountDecommission": false,
},
"alternate_contacts": [
{
"type": "SECURITY",
"name": "Security Contact",
"email_address": "demo-security@nuvibit.com"
}
],
"customer_values": {}
}
]

# notify on account lifecycle step functions or account baseline pipeline errors
account_factory_notification_settings = {
# identify for which AWS Organization notifications are sent
org_identifier = "c2"
# multiple subscriptions with different protocols is supported
subscriptions = [
{
protocol = "email"
endpoints = ["demo-ops@nuvibit.com"]
}
]
}

# (optional) credentials if you want to reference Terraform modules in your account baseline
# https://developer.hashicorp.com/terraform/language/modules/sources
#Β WARNING: do not store credentials in clear text in git - reference from a vault or from environment variable
#Β account_baseline_git_ssh_key = var.account_baseline_git_ssh_key
#Β account_baseline_github_access_token = var.account_baseline_github_access_token
#Β account_baseline_terraform_registry_token = var.account_baseline_terraform_registry_token
#Β account_baseline_terraform_registry_host = "spacelift.io"

# --------------------------------------------------------------------------------------------------
# Β¦ ACCOUNT LIFECYCLE CUSTOMIZATION
# --------------------------------------------------------------------------------------------------
# provide lambda packages for additional account lifecycle customization e.g. delete default vpc
# trigger for the lambda step functions are cloudtrail events with event source 'organizations.amazonaws.com'
account_lifecycle_customization_steps = [
{
# organizations event (e.g. when a new AWS account is created) that should trigger a lambda step_sequence
organizations_event_trigger = "CreateAccountResult"
# step sequence defines the order in which lambda steps are executed
step_sequence = [
# {
# step_name = "on_account_creation_enable_opt_in_regions"
# lambda_package_source_path = "INSERT_LAMBDA_SOURCE_PATH"
# lambda_handler = "main.lambda_handler"
# environment_variables = {
# "ORGANIZATIONS_MEMBER_ROLE" : "OrganizationAccountAccessRole"
# "OPT_IN_REGIONS" : jsonencode(["eu-central-2"])
# "DEFAULT_REGION" : "eu-central-1"
# }
# }
module.account_lifecycle_customization_templates.account_lifecycle_customization_steps["enable_opt_in_regions"],
module.account_lifecycle_customization_templates.account_lifecycle_customization_steps["delete_default_vpc"],
module.account_lifecycle_customization_templates.account_lifecycle_customization_steps["increase_service_quota"],
]
},
{
organizations_event_trigger = "CloseAccountResult"
step_sequence = [
module.account_lifecycle_customization_templates.account_lifecycle_customization_steps["move_to_suspended_ou"]
]
}
]

# (optional) list of user-defined organizational events to trigger account lifecycle customization step function
# this can be used to force account lifecycle actions for specified accounts
# https://docs.aws.amazon.com/organizations/latest/userguide/orgs_cloudtrail-integration.html
account_lifecycle_customization_on_demand_triggers = {
user_defined_events = [
jsonencode({
"source" : "aws.organizations",
"detail" : {
"eventSource" : "organizations.amazonaws.com",
"eventName" : "CreateAccountResult",
"serviceEventDetails" : {
"createAccountStatus" : {
"state": "SUCCEEDED",
"accountId" : "111111111111"
}
}
}
})
]
}

# list of baseline definitions for accounts in a specific scope
account_baseline_scopes = [
# --------------------------------------------------------------------------------------------------
# Β¦ ACCOUNT BASELINE - WORKLOAD ACCOUNTS
# --------------------------------------------------------------------------------------------------
{
scope_name = "workloads"
# you can use the terraform or opentofu binary for account baseline pipelines
terraform_binary = "opentofu"
# (optional) reduce parallelism to avoid api rate limits when deploying to multiple regions
terraform_parallelism = 10
# https://github.com/hashicorp/terraform/releases
# https://github.com/opentofu/opentofu/releases
terraform_version = "1.8.5"
aws_provider_version = "5.76.0"
# (optional) define provider default tags which will be applied to all baseline resources
provider_default_tags = {
ManagedBy = "ntc-account-factory",
BaselineScope = "workloads",
BaselineVersion = "1.3"
}
# (optional) schedule baseline pipelines to rerun every x hours
schedule_rerun_every_x_hours = 0
# (optional) IAM role which exists in member accounts and can be assumed by baseline pipeline
baseline_execution_role_name = "OrganizationAccountAccessRole"
# (optional) session name used by baseline pipeline in member accounts
baseline_execution_session_name = "ntc-account-factory"
# (optional) additional providers which assume a specific account role for cross account orchestration
# WARNING: removing an existing provider from 'baseline_assume_role_providers' can cause provider errors
baseline_assume_role_providers = [
{
configuration_alias = "connectivity"
role_arn = local.ntc_parameters["connectivity"]["baseline_assume_role_arn"]
session_name = "ntc-account-factory"
}
]
# add terraform code to baseline from static files or dynamic templates
baseline_terraform_files = [
module.account_baseline_templates.account_baseline_terraform_files["iam_monitoring_reader"],
module.account_baseline_templates.account_baseline_terraform_files["iam_instance_profile"],
module.account_baseline_templates.account_baseline_terraform_files["oidc_spacelift"],
module.account_baseline_templates.account_baseline_terraform_files["aws_config"],
]
# add delay to pipeline to avoid errors on first run
# in this case pipeline will wait for up to 10 minutes for dependencies to resolve
pipeline_delay_options = {
wait_for_seconds = 120
wait_retry_count = 5
wait_for_execution_role = true
wait_for_regions = false
wait_for_securityhub = false
wait_for_guardduty = false
}
# baseline terraform code will be provisioned in each specified region
baseline_regions = ["us-east-1", "eu-central-1"]
# baseline terraform code which can be provisioned in a single region (e.g. IAM)
baseline_main_region = "eu-central-1"
# accounts which should be included in baseline scope
include_accounts_all = false
include_accounts_by_ou_paths = [
"/root/workloads/prod",
"/root/workloads/dev",
"/root/workloads/test",
]
include_accounts_by_names = []
include_accounts_by_tags = [
# {
# key = "AccountType"
# value = "workload"
# }
]
# accounts which should be excluded in baseline scope
exclude_accounts_by_ou_paths = []
exclude_accounts_by_names = []
exclude_accounts_by_tags = []
# decomissioning of baseline terraform resources must be done before deleting the scope!
# decommission baseline terraform code for specific accounts in scope
decommission_accounts_all = false
decommission_accounts_by_ou_paths = []
decommission_accounts_by_names = []
decommission_accounts_by_tags = [
{
key = "AccountDecommission"
value = true
}
]
},
]

providers = {
aws = aws.euc1
aws.us_east_1 = aws.use1 # required for account lifecycle cloudtrail
}
}

Requirements​

The following requirements are needed by this module:

  • terraform (>= 1.3.1)

  • archive (>= 2.0.0)

  • aws (>= 5.11.0)

Providers​

The following providers are used by this module:

  • archive (>= 2.0.0)

  • aws (>= 5.11.0)

Modules​

The following Modules are called:

account_factory_cloudtrail​

Source: ./modules/cloudtrail

Version:

baseline_artifacts_bucket​

Source: ./modules/bucket

Version:

baseline_bucket​

Source: ./modules/bucket

Version:

lambda_pretty_notifications​

Source: ./modules/lambda

Version:

lambda_step_function​

Source: ./modules/lambda

Version:

lambda_user_defined_event_triggers​

Source: ./modules/lambda

Version:

ou_path​

Source: ./modules/ou-path

Version:

Resources​

The following resources are used by this module:

Required Inputs​

The following input variables are required:

account_factory_baseline_bucket_name​

Description: The name for account factory S3 bucket which stores account baseline files.

Type: string

account_factory_cloudtrail_bucket_name​

Description: The name for NTC Account Factory cloudtrail logging bucket.

Type: string

Optional Inputs​

The following input variables are optional (have default values):

account_baseline_git_ssh_key​

Description: SSH key for Git. Required if you want to reference Terraform modules in your account baseline which are hosted in Git (via SSH). Git must be reachable via Internet.

Type: string

Default: ""

account_baseline_git_ssh_key_secret_name​

Description: Name of the secrets manager secret where the 'account_baseline_git_ssh_key' will be stored so that the baseline pipeline can access it.

Type: string

Default: "git_ssh_key"

account_baseline_github_access_token​

Description: Personal Access Token for Github. Required if you want to reference Terraform modules in your account baseline which are hosted in Github. Github must be reachable via Internet.

Type: string

Default: ""

account_baseline_github_access_token_secret_name​

Description: Name of the secrets manager secret where the 'account_baseline_github_access_token' will be stored so that the baseline pipeline can access it.

Type: string

Default: "github_access_token"

account_baseline_pipeline_event_rule_iam_role_name​

Description: The name of the IAM role that will be created for baseline pipeline triggers via cloudwatch event rules.

Type: string

Default: "ntc-af-pipeline-trigger-role"

account_baseline_pipeline_iam_role_name​

Description: The name of the IAM role that will be created for baseline pipelines.

Type: string

Default: "ntc-af-pipeline-role"

account_baseline_pipeline_logs_enabled​

Description: Set to false to disable cloudwatch logs for account baseline pipelines.

Type: bool

Default: true

account_baseline_pipeline_name_prefix​

Description: The prefix name for baseline pipelines.

Type: string

Default: "ntc-af"

account_baseline_pipeline_policy_name​

Description: The name for the baseline pipelines IAM policy.

Type: string

Default: "ntc-af-pipeline-policy"

account_baseline_scopes​

Description: List of baseline scopes which define which member accounts will be baselined with a specific Terraform baseline configuration.

Type:

list(object({
scope_name = string
terraform_parallelism = optional(number, 10)
terraform_binary = optional(string, "terraform")
terraform_version = string
aws_provider_version = string
provider_default_tags = optional(map(string), { ManagedBy = "ntc-account-factory" })
pipeline_compute_type = optional(string, "BUILD_GENERAL1_SMALL")
pipeline_delay_options = optional(object({
wait_for_seconds = optional(number, 120)
wait_retry_count = optional(number, 5)
wait_for_execution_role = optional(bool, true)
wait_for_regions = optional(bool, true)
wait_for_securityhub = optional(bool, false)
wait_for_guardduty = optional(bool, false)
}), {})
schedule_rerun_every_x_hours = optional(number, 0)
baseline_execution_role_name = optional(string, "OrganizationAccountAccessRole")
baseline_execution_session_name = optional(string, "ntc-account-factory")
baseline_assume_role_providers = optional(list(object({
configuration_alias = string
role_arn = string
session_name = optional(string, "ntc-account-factory")
})), [])
baseline_terraform_files = list(object({
file_name = string
content = string
terraform_version_minimum = optional(string, "")
aws_provider_version_minimum = optional(string, "")
}))
baseline_regions = list(string)
baseline_main_region = string
include_accounts_all = optional(bool, false)
include_accounts_by_ou_paths = optional(list(string), [])
include_accounts_by_names = optional(list(string), [])
include_accounts_by_tags = optional(list(object({
key = string
value = string
})), [])
exclude_accounts_by_ou_paths = optional(list(string), [])
exclude_accounts_by_names = optional(list(string), [])
exclude_accounts_by_tags = optional(list(object({
key = string
value = string
})), [])
decommission_accounts_all = optional(bool, false)
decommission_accounts_by_ou_paths = optional(list(string), [])
decommission_accounts_by_names = optional(list(string), [])
decommission_accounts_by_tags = optional(list(object({
key = string
value = string
})), [])
}))

Default: []

account_baseline_terraform_registry_host​

Description: Name of Terraform Cloud or Enterprise host. Required if you want to reference Terraform modules in your account baseline from a Terraform Cloud or Enterprise private registry. Registry must be reachable via Internet.

Type: string

Default: "app.terraform.io"

account_baseline_terraform_registry_token​

Description: Team- or User-Token for Terraform Cloud or Enterprise. Required if you want to reference Terraform modules in your account baseline from a Terraform Cloud or Enterprise private registry. Registry must be reachable via Internet.

Type: string

Default: ""

account_baseline_terraform_registry_token_secret_name​

Description: Name of the secrets manager secret where the 'account_baseline_terraform_registry_token' will be stored so that the baseline pipeline can access it.

Type: string

Default: "terraform_registry_token"

account_factory_baseline_artifacts_bucket_suffix_name​

Description: The name for account factory S3 bucket which stores codebuild artifacts.

Type: string

Default: "-artifacts"

account_factory_bucket_access_logging_target_bucket_name​

Description: Name of the bucket where S3 access logging should be stored. Requires "account_factory_bucket_enable_access_logging" to be true.

Type: string

Default: ""

account_factory_bucket_access_logging_target_prefix​

Description: Prefix used for S3 access logging. Requires "account_factory_bucket_enable_access_logging" to be true.

Type: string

Default: "logs/"

account_factory_bucket_enable_access_logging​

Description: Set to true to log S3 access logging.

Type: bool

Default: false

account_factory_bucket_force_destroy​

Description: Set to true if you want to allow the account factory s3 bucket to be force destroyed. WARNING: This is intended for automated testing and should otherwise stay false.

Type: bool

Default: false

account_factory_cloudtrail_kms_alias_name​

Description: The alias name for account factory pipeline encryption kms key.

Type: string

Default: "ntc-af-cloudtrail-encryption"

account_factory_forward_crossregion_events_via_lambda​

Description: Set to true to forward events via lambda for destination regions which don't support EventBridge forwarding. https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-cross-region.html

Type: bool

Default: false

account_factory_forward_events_policy_name​

Description: The name of IAM policy used to forward events.

Type: string

Default: "ntc-af-event-forwarding-policy"

account_factory_forward_events_role_name​

Description: The name of IAM role used to forward events.

Type: string

Default: "ntc-af-event-forwarding-role"

account_factory_forward_events_rule_prefix​

Description: The name of event rule used to forward events.

Type: string

Default: "ntc-af-event-forwarding"

account_factory_lambda_execution_role_name​

Description: The execution role name for account factory lambdas.

Type: string

Default: "ntc-af-lambda-execution-role"

account_factory_lambda_policy_name​

Description: The policy name for account factory lambdas.

Type: string

Default: "ntc-af-lambda-policy"

account_factory_list​

Description: List of member accounts that should be provisioned and managed in AWS Organizations.

Type:

list(object({
account_name = string
account_email = string
ou_path = string
close_on_deletion = bool
ignore_naming_conventions = optional(bool, false)
account_tags = optional(map(string), {})
alternate_contacts = optional(list(object({
type = string
name = string
email_address = string
title = optional(string, "Mx")
phone_number = optional(string, "00000000000")
})), [])
customer_values = optional(any, {})
}))

Default: []

account_factory_naming_conventions​

Description: Regex patterns for optional naming conventions. Account name and email are critical values that cannot be easily modified after creation.

Type:

object({
account_name_regex = optional(string, "")
account_email_regex = optional(string, "")
})

Default: {}

account_factory_notification_settings​

Description: SNS topic settings to send account factory notifications.

Type:

object({
enable_notifications = optional(bool, true)
org_identifier = optional(string, "")
sns_topic_name = optional(string, "ntc-af-notification-topic")
event_rule_prefix = optional(string, "ntc-af-notification")
notification_lambda_name = optional(string, "ntc-af-notification-lambda")
subscriptions = optional(list(object({
protocol = optional(string, "email")
endpoints = optional(list(string), [])
subscription_role_arn = optional(string, null)
})), [])
})

Default: {}

account_factory_org_events_trail_name​

Description: The name of NTC Account Factory cloudtrail which records Organizations account lifecycle events.

Type: string

Default: "ntc-af-org-events-trail"

account_factory_pipeline_kms_alias_name​

Description: The alias name for account factory pipeline encryption kms key.

Type: string

Default: "ntc-af-encryption"

account_factory_secretsmanager_prefix​

Description: Grant read access to account factory for existing secrets with a specific prefix name.

Type: string

Default: "ntc-af-"

account_lifecycle_customization_on_demand_triggers​

Description: Trigger the account lifecycle customization on demand with user-defined events.

Type:

object({
lambda_name = optional(string, "ntc-af-on-demand-triggers-lambda")
user_defined_events = optional(list(string), [])
})

Default: {}

account_lifecycle_customization_state_machine_name​

Description: The state machine name for account lifecycle step functions.

Type: string

Default: "ntc-af-state-machine"

account_lifecycle_customization_steps​

Description: List of lifecycle customization steps which define which lambda functions will be executed when a specific event occurs.

Type:

list(object({
organizations_event_trigger = string
step_sequence = list(object({
step_name = string
description = optional(string, null)
lambda_package_source_path = string
lambda_handler = string
lambda_execution_role_arn = optional(string, null)
lambda_timeout_in_seconds = optional(number, 720)
environment_variables = optional(map(string), {})
runtime = optional(string, "python3.9")
}))
}))

Default: []

arm_based_compute​

Description: Set to false to use x86_64 instead of arm for pipelines and lambdas.

Type: bool

Default: true

create_organizations_member_role_in_current_account​

Description: The Organizations member role is required in current account for 'account lifecycle customzation' and 'account baseline'. Set to false if Organizations member role already exists in current account.

Type: bool

Default: true

increase_aws_service_quotas​

Description: Manage service quotas for services used in account factory.

Type:

object({
codebuild_concurrent_runs_arm_small = optional(number, 0)
codebuild_concurrent_runs_arm_large = optional(number, 0)
codebuild_concurrent_runs_linux_small = optional(number, 0)
codebuild_concurrent_runs_linux_medium = optional(number, 0)
codebuild_concurrent_runs_linux_large = optional(number, 0)
codebuild_concurrent_runs_linux_2xlarge = optional(number, 0)
codepipelines_max_count = optional(number, 0)
event_rules_max_count = optional(number, 0)
})

Default: {}

lambda_runtime​

Description: The runtime with which all the lambda function runs

Type: string

Default: "python3.11"

organizations_member_role_in_current_account_policy_arn​

Description: By default the Organizations member role in current account will get assigned AdministratorAccess.

Type: string

Default: ""

organizations_member_role_name​

Description: The name of an IAM role that Organizations automatically preconfigures in the new member account.

Type: string

Default: "OrganizationAccountAccessRole"

secretsmanager_recovery_window_in_days​

Description: Number of days that AWS Secrets Manager waits before it can delete account factory secrets. Set to 0 to force deletion.

Type: number

Default: 7

tracing_mode_lambdas​

Description: Whether to to sample and trace a subset of incoming requests with AWS X-Ray for all Account Factory Lambdas.
Can be either PassThrough or Active and if omitted tracing is disabled.
If PassThrough, Lambdas will only trace the request from an upstream service if it contains a tracing header with "sampled=1".
If Active, Lambdas will respect any tracing header it receives from an upstream service.
If no tracing header is received, Lambdas will call X-Ray for a tracing decision.

Type: string

Default: null

Outputs​

The following outputs are exported:

account_factory_account_ids​

Description: Map of account factory account identifiers by account name.

account_factory_baseline_iam_role_arns​

Description: List of account factory baseline IAM role ARNs.

account_factory_codebuild_projects​

Description: Map of all account factory codebuild projects including Names and ARNs.

account_factory_notifications_sns_topic_arn​

Description: ARN of SNS topic which notifies about Account Factory pipeline and step function errors.

account_lifecycle_customization_state_machine_arn​

Description: ARN of step function state machine wich is responsible for account lifecycle customization.

account_lifecycle_customization_state_machine_definition​

Description: Definition of step function state machine which is responsible for account lifecycle customization.