Account Lifecycle Management
The Account Lifecycle Management feature in NTC Account Factory provides event-driven, reactive automation for specific moments in an account's lifecycle, such as creation, tagging changes, or closure.
Overview
Account Lifecycle Management uses AWS CloudTrail events from AWS Organizations to trigger Step Functions that execute a series of Lambda functions in a defined sequence. This allows you to automate quick, targeted operations at key moments in an account's lifecycle.
How It Works
- CloudTrail captures AWS Organizations events
- EventBridge rules filter and route these events to a Step Function
- Step Functions execute a series of Lambda functions in sequence
- Each Lambda function performs a specific task related to account setup or configuration
Available Event Triggers
NTC Account Factory comes with built-in support for the following AWS Organizations events:
Event Name | Description | Pre-built Templates Available |
---|---|---|
CreateAccountResult | Triggered when a new AWS account is successfully created | enable_opt_in_regions, delete_default_vpc, increase_service_quota, tag_shared_resources, enable_enterprise_support, create_account_alias |
CloseAccountResult | Triggered when an AWS account is closed | move_to_suspended_ou |
While the module can be extended to support other events like TagResource
and UntagResource
, the templates above are available out-of-the-box for the most common account lifecycle events.
NTC Account Lifecycle Templates
The NTC Account Lifecycle Templates module provides a set of pre-built, reusable templates for common account lifecycle tasks. These templates can be easily integrated into your Account Factory configuration to automate routine account management tasks.
Available Templates
The following templates are available in the NTC Account Lifecycle Templates Templates module:
Template | Description | When to Use |
---|---|---|
enable_opt_in_regions | Enables specific AWS opt-in regions | When new accounts need access to regions beyond the default set |
delete_default_vpc | Removes default VPCs from all enabled regions | To enforce security best practices for new accounts |
increase_service_quota | Increases service quotas for specific AWS services | When default service limits are too restrictive |
tag_shared_resources | Automatically applies tags to AWS resources | To ensure consistent resource tagging |
move_to_suspended_ou | Moves closed accounts to a suspended OU | When accounts are closed but need to remain in the organization |
enable_enterprise_support | Configures Enterprise Support for new accounts | When accounts require advanced AWS support |
create_account_alias | Creates AWS account IAM alias based on Account Tag | When you want to set a friendly name for the account. It will show up in the AWS console top right corner. |
Implementation
The NTC Account Lifecycle Templates can be implemented in your NTC Account Factory configuration through a few simple steps:
- Include the Templates Module
First, include the NTC Account Lifecycle Templates module in your Terraform configuration:
module "account_lifecycle_customization_templates" {
source = "github.com/nuvibit-terraform-collection/terraform-aws-ntc-account-lifecycle-templates?ref=X.X.X"
account_lifecycle_customization_templates = [
{
template_name = "enable_opt_in_regions"
organizations_event_trigger = "CreateAccountResult"
organizations_member_role = "OrganizationAccountAccessRole"
opt_in_regions = ["eu-central-2", "eu-south-1"]
},
{
template_name = "delete_default_vpc"
organizations_event_trigger = "CreateAccountResult"
organizations_member_role = "OrganizationAccountAccessRole"
},
{
template_name = "increase_service_quota"
organizations_event_trigger = "CreateAccountResult"
organizations_member_role = "OrganizationAccountAccessRole"
quota_increases = [
{
region = "us-east-1"
quota_name = "Managed policies per role"
service_code = "iam"
value = 20
}
]
},
{
template_name = "tag_shared_resources"
organizations_event_trigger = "CreateAccountResult"
organizations_member_role = "OrganizationAccountAccessRole"
shared_resources_regions = ["eu-central-1", "eu-central-2"]
},
{
template_name = "move_to_suspended_ou"
organizations_event_trigger = "CloseAccountResult"
organizations_member_role = "OrganizationAccountAccessRole"
suspended_ou_id = "ou-abc1-123def45"
},
{
template_name = "enable_enterprise_support"
organizations_event_trigger = "CreateAccountResult"
company_name = "Example Company"
cc_email_addresses = ["accounts@example.com"]
},
{
template_name = "create_account_alias"
organizations_event_trigger = "CreateAccountResult"
account_alias_tag_key = "AccountAlias"
}
]
}
- Reference Templates in Account Factory Configuration
After defining the templates, reference them in your NTC Account Factory configuration:
module "ntc_account_factory" {
source = "github.com/nuvibit-terraform-collection/terraform-aws-ntc-account-factory?ref=X.X.X"
account_factory_baseline_bucket_name = "ntc-account-factory-baseline"
account_factory_cloudtrail_bucket_name = "ntc-account-factory-cloudtrail"
# Other account factory configuration...
account_lifecycle_customization_steps = [
{
organizations_event_trigger = "CreateAccountResult"
step_sequence = [
# Reference pre-defined templates
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"],
module.account_lifecycle_customization_templates.account_lifecycle_customization_steps["tag_shared_resources"],
# Add custom steps as needed
{
step_name = "on_account_creation_do_something_custom_that_should_be_better_done_in_a_lambda_than_as_a_terraform_step",
lambda_package_source_path = "files/custom-lifecycle-lambda"
lambda_handler = "main.lambda_handler"
environment_variables = {
"ORGANIZATIONS_MEMBER_ROLE": "OrganizationAccountAccessRole",
"DEFAULT_REGION": "eu-central-1",
"PARTITION": "aws",
"ORGANIZATION_MANAGEMENT_ACCOUNT_ID": data.aws_caller_identity.current.account_id
}
}
]
},
{
organizations_event_trigger = "CloseAccountResult"
step_sequence = [
module.account_lifecycle_customization_templates.account_lifecycle_customization_steps["move_to_suspended_ou"]
]
}
]
}
- Testing with On-Demand Triggers
You can test your account lifecycle customization steps without waiting for actual events:
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"
}
}
}
})
]
}
Custom Lifecycle Templates
While the pre-built templates cover many common scenarios, you may need to create custom lifecycle templates for unique requirements. Here's how to create and use custom templates:
Creating Custom Lambda Functions
-
Create the Lambda Function Directory
Create a new directory in your filesystem for your Lambda function:
files/
└── custom-lifecycle-lambda/
└── main.py -
Implement the Lambda Function
Create a Python file with your Lambda function logic:
import json
import os
import logging
import boto3
from botocore.exceptions import ClientError
# Configure logging
logger = logging.getLogger()
logger.setLevel(logging.INFO)
formatter = logging.Formatter('[ %(asctime)s ] [ %(levelname)s ] %(message)s')
for handler in logger.handlers:
handler.setFormatter(formatter)
def assume_role(role_arn, session_name, region_name=None):
"""Assume an IAM role and return temporary credentials"""
sts_client = boto3.client('sts', region_name=region_name)
try:
response = sts_client.assume_role(
RoleArn=role_arn,
RoleSessionName=session_name,
DurationSeconds=900
)
return response['Credentials']
except ClientError as error:
logger.error(f"Failed to assume role {role_arn}: {error.response['Error']['Message']}")
raise Exception(f"Could not assume role {role_arn}")
def lambda_handler(event, context):
"""Main Lambda handler function"""
# Get environment variables
account_role = env.get('ORGANIZATIONS_MEMBER_ROLE')
default_region = env.get('DEFAULT_REGION', 'eu-central-1')
partition = env.get('PARTITION', 'aws')
org_mgmt_account_id = env.get('ORGANIZATION_MANAGEMENT_ACCOUNT_ID')
# Process the event
try:
event_details = event['detail']['serviceEventDetails']['createAccountStatus']
if event_details['state'] == 'SUCCEEDED':
account_id = event_details['accountId']
logger.info(f"Processing account: {account_id}")
else:
raise Exception(f"Account creation was not successful: {event_details['state']}")
except KeyError as e:
logger.error(f"Missing key in event: {e}")
raise Exception(f"Could not access event details: {str(e)}")
# Implement your custom logic here
# For example, you might want to:
# - Set up a specific IAM role
# - Configure specific services
# - Apply additional tags
logger.info("Custom account lifecycle step completed successfully")
return {
"accountId": account_id,
"success": True,
"action": "custom_template_completed"
} -
Add Your Custom Lambda to Account Factory Configuration
Reference your custom Lambda in the account_lifecycle_customization_steps section:
account_lifecycle_customization_steps = [
{
organizations_event_trigger = "CreateAccountResult"
step_sequence = [
// Pre-defined templates
module.account_lifecycle_customization_templates.account_lifecycle_customization_steps["delete_default_vpc"],
// Your custom template
{
step_name = "my_custom_account_setup",
lambda_package_source_path = "files/custom-lifecycle-lambda",
lambda_handler = "main.lambda_handler",
environment_variables = {
"ORGANIZATIONS_MEMBER_ROLE": "OrganizationAccountAccessRole",
"DEFAULT_REGION": "eu-central-1",
"PARTITION": "aws",
"ORGANIZATION_MANAGEMENT_ACCOUNT_ID": data.aws_caller_identity.current.account_id,
// Add any custom environment variables your Lambda needs
"CUSTOM_CONFIG_PARAMETER": "custom-value"
}
}
]
}
]
Differences from Account Baseline
While Account Baseline provides comprehensive, ongoing governance through CI/CD pipelines and Terraform, Account Lifecycle Management is focused on:
Account Lifecycle Management | Account Baseline |
---|---|
Deployment Method | Serverless architecture (Lambda and Step Functions) |
Operational Mode | Quick, targeted operations for specific events |
State Management | Stateless operations without ongoing state management |
Execution Timing | Event-driven automation triggered by account events |
Account Targeting | Event-based triggers applying to qualifying accounts |
Typical Use Cases | Account creation setup, resource cleanup, tag-based actions |
Best Practices
- Idempotency: Ensure your Lambda functions are idempotent, as they may be executed multiple times for the same event
- Error Handling: Implement robust error handling in your Lambda functions
- Logging: Use detailed logging to troubleshoot issues
- Permissions: Ensure your Lambda functions have the necessary permissions to perform their actions
- Testing: Test your customization steps thoroughly using on-demand triggers
- Reuse: Create reusable templates for common tasks to maintain consistency
- Sequence: Consider the order of execution when combining multiple templates
FAQ
How can I create my own Account Lifecycle Template and invoke it?
See the "Custom Lifecycle Templates" section above for detailed instructions on creating and using custom account lifecycle templates.
Can I use both pre-defined and custom templates together?
Yes, you can combine pre-defined templates from the NTC Account Lifecycle Templates module with your custom templates in the same step sequence. This allows you to leverage existing solutions for common tasks while still implementing custom logic for your specific requirements.
How do I handle errors in my lifecycle templates?
Your Lambda functions should include comprehensive error handling with appropriate logging. The Step Function state machine will handle retries and error paths. You can also set up notifications through the Account Factory notification system to alert you when lifecycle steps fail.
Can I modify an existing template instead of creating a new one?
It's recommended to create your own custom template rather than modifying the pre-defined templates. This approach makes it easier to upgrade to newer versions of the NTC Account Lifecycle Templates module without losing your customizations.
How do I create a custom lifecycle template for tag change events?
To create a lifecycle template for TagResource
or UntagResource
events, follow these steps:
-
Create a Lambda function for tag events:
def lambda_handler(event, context):
# Process tag events
try:
event_name = event['detail']['eventName']
resource_id = event['detail']['requestParameters'].get('ResourceId')
if event_name == 'TagResource':
# Process added/updated tags
tags = event['detail']['requestParameters'].get('Tags', [])
# Implement your logic for new or updated tags
logger.info(f"Processing tag additions for resource: {resource_id}")
elif event_name == 'UntagResource':
# Process removed tags
tag_keys = event['detail']['requestParameters'].get('TagKeys', [])
# Implement your logic for removed tags
logger.info(f"Processing tag removals for resource: {resource_id}")
except KeyError as e:
logger.error(f"Missing key in event: {e}")
raise Exception(f"Could not access event details: {str(e)}")
# Your custom logic here
return {
"resourceId": resource_id,
"success": True
} -
Configure the tag event listener in Account Factory:
account_lifecycle_customization_steps = [
# Your existing event listeners ...
{
organizations_event_trigger = "TagResource,UntagResource"
step_sequence = [
{
step_name = "process_account_tag_changes",
lambda_package_source_path = "files/tag-handler-lambda",
lambda_handler = "main.lambda_handler",
environment_variables = {
"ORGANIZATIONS_MEMBER_ROLE": "OrganizationAccountAccessRole",
"DEFAULT_REGION": "eu-central-1",
"PARTITION": "aws",
"ORGANIZATION_MANAGEMENT_ACCOUNT_ID": data.aws_caller_identity.current.account_id
}
}
]
}
] -
Test the tag event handling:
You can test your tag event handling using on-demand triggers:
account_lifecycle_customization_on_demand_triggers = {
user_defined_events = [
jsonencode({
"source": "aws.organizations",
"detail": {
"eventSource": "organizations.amazonaws.com",
"eventName": "TagResource",
"requestParameters": {
"ResourceId": "111111111111",
"Tags": [
{
"Key": "Environment",
"Value": "Production"
}
]
}
}
})
]
}
By setting up tag event handlers, you can implement dynamic responses to changes in account tags, such as updating configurations, applying specific policies, or adjusting resource settings based on tag changes.