Skip to main content

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

  1. CloudTrail captures AWS Organizations events
  2. EventBridge rules filter and route these events to a Step Function
  3. Step Functions execute a series of Lambda functions in sequence
  4. Each Lambda function performs a specific task related to account setup or configuration

NTC Account Factory Account Lifecycle Flow

Available Event Triggers

NTC Account Factory comes with built-in support for the following AWS Organizations events:

Event NameDescriptionPre-built Templates Available
CreateAccountResultTriggered when a new AWS account is successfully createdenable_opt_in_regions, delete_default_vpc, increase_service_quota, tag_shared_resources, enable_enterprise_support, create_account_alias
CloseAccountResultTriggered when an AWS account is closedmove_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:

TemplateDescriptionWhen to Use
enable_opt_in_regionsEnables specific AWS opt-in regionsWhen new accounts need access to regions beyond the default set
delete_default_vpcRemoves default VPCs from all enabled regionsTo enforce security best practices for new accounts
increase_service_quotaIncreases service quotas for specific AWS servicesWhen default service limits are too restrictive
tag_shared_resourcesAutomatically applies tags to AWS resourcesTo ensure consistent resource tagging
move_to_suspended_ouMoves closed accounts to a suspended OUWhen accounts are closed but need to remain in the organization
enable_enterprise_supportConfigures Enterprise Support for new accountsWhen accounts require advanced AWS support
create_account_aliasCreates AWS account IAM alias based on Account TagWhen 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:

  1. 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"
}
]
}
  1. 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"]
]
}
]
}
  1. 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

  1. Create the Lambda Function Directory

    Create a new directory in your filesystem for your Lambda function:

    files/
    └── custom-lifecycle-lambda/
    └── main.py
  2. 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"
    }
  3. 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 ManagementAccount Baseline
Deployment MethodServerless architecture (Lambda and Step Functions)
Operational ModeQuick, targeted operations for specific events
State ManagementStateless operations without ongoing state management
Execution TimingEvent-driven automation triggered by account events
Account TargetingEvent-based triggers applying to qualifying accounts
Typical Use CasesAccount creation setup, resource cleanup, tag-based actions

Best Practices

  1. Idempotency: Ensure your Lambda functions are idempotent, as they may be executed multiple times for the same event
  2. Error Handling: Implement robust error handling in your Lambda functions
  3. Logging: Use detailed logging to troubleshoot issues
  4. Permissions: Ensure your Lambda functions have the necessary permissions to perform their actions
  5. Testing: Test your customization steps thoroughly using on-demand triggers
  6. Reuse: Create reusable templates for common tasks to maintain consistency
  7. 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:

  1. 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
    }
  2. 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
    }
    }
    ]
    }
    ]
  3. 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.