AWS Cloud Operations & Migrations Blog

Managing AWS account lifecycle in AWS Control Tower using the Account Close API

AWS Control Tower provides the easiest way for you to set up and govern your AWS environment following prescriptive AWS best practices managed on your behalf. AWS Control Tower orchestrates multiple AWS services (AWS Organizations, AWS CloudFormation StackSets, Amazon Simple Storage Service (Amazon S3), AWS Single Sign-On, AWS Config, AWS CloudTrail) to build a landing zone in under 30 minutes, and it sets preventive and detective controls (guardrails) that make sure of best practices.
The AWS Control Tower account factory enables cloud administrators to provision accounts in your landing zone. Accounts that you provision through the AWS Control Tower Account Factory can be updated, removed, or repurposed. Until recently, the steps required to close accounts under AWS Control Tower governance required multiple manual steps. The new CloseAccount API means that you can now programmatically close accounts. See this link to learn more.
In this post, I’ll demonstrate how to programmatically close AWS accounts that are registered with AWS Control Tower. This means that you can close member accounts from your organization’s management account without having to log in to each member account individually with root credentials. Furthermore, I’ll demonstrate what AWS Identity and Access Management (IAM) permissions are required to perform the account closure activity, thus giving you better control over the account lifecycle and preventing accidental account deletion.

Use case overview

You’ll encounter some of the following use cases where you might need to close AWS accounts that are under AWS Control Tower governance.

You’ve deployed AWS Control Tower in an existing Organization with existing AWS accounts, and you’ve registered these accounts with AWS Control Tower using AWS Control Tower Account Factory. When you do this, you’re essentially creating an AWS Service Catalog provisioned product for every account that you register with AWS Control Tower. When you’re transitioning or migrating your AWS accounts from AWS Landing Zone solution to AWS Control Tower, there could be accounts that you don’t need anymore, such as the AWS Landing Zone logging and security accounts, as AWS Control Tower provides these types of accounts out of the box with similar functionality. Therefore, you can either choose to keep these accounts outside of AWS Control Tower governance or close them.

Another use case is when you’re reorganizing your Organizational Unit structure and end up with account(s) that aren’t relevant to your organization anymore. In this scenario, we recommend first moving these accounts to a ‘Suspended OU’ and applying Deny-all Service Control Policy (SCP) to make sure that no action is allowed in these accounts that are to be deleted. More information on best practices regarding organizing your AWS environment using multiple accounts can be found here.

Setting up permissions

Our basic recommendation is to have two types of account lifecycle admin roles: one for closing non-production accounts only, and a second, more privileged one, for the closing of production accounts. Additional processes (i.e., change request, approval workflow) should be implemented when closing a production account so as to avoid any impacts on production workloads or loss of data.
To prevent IAM principals (i.e., IAM users or roles) in the management account with permissions to close accounts from closing specific accounts in your organization, you can attach an additional policy that explicitly denies closing those accounts. The following is a sample policy.

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "PreventCloseAccount",
            "Effect": "Deny",
            "Action": "organizations:CloseAccount",
            "Resource": [
                "arn:aws:organizations::555555555555:account/o-12345abcdef/123456789012",
                "arn:aws:organizations::555555555555:account/o-12345abcdef/123456789014"
            ]
        }
    ]
}

In the policy example above, we’re using the “Resource” policy element to list all of the production AWS accounts that we want to protect from deletion. You can alternatively leverage AWS account tags to avoid listing each account in the policy document. For this approach to work effectively, you must make sure that you tag all production accounts appropriately. See this link to learn more about Tagging Organization resources. The following is an example of a policy that uses account tags to prevent production accounts from being deleted.

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "PreventCloseAccountForTaggedAccts",
            "Effect": "Deny",
            "Action": "organizations:CloseAccount",
            "Resource": "*",
            "Condition": {
                "StringEquals": {"aws:ResourceTag/accounttype": "production"}
            }
        }
    ]
}

The policy document above leverages the “aws:ResourceTag” condition key to prevent the closing of accounts that are tagged with the “accounttype:production” key pair. By using the “StringEquals”, we not only make sure that only accounts properly tagged are protected, but also allow for the closing of AWS accounts tagged differently or not tagged at all. Whenever using tags in your security model, you must consider establishing guidelines and controls to make sure that your AWS accounts are always properly tagged. See the following policy example that will block a user or role from untagging the account.

{
        "Sid":"DenyUntaggingAccountTypeTag",
        "Effect":"Deny",
        "Action":[
           "organizations:UntagResource"
        ],
        "Resource":"*",
        "Condition":{
           "ForAnyValue:StringEquals":{
              "aws:TagKeys":[
                 "accounttype"
              ]
           }
        }
     }

For an introduction on how to use tags for assigning permissions to Organizations resources, including tag enforcement on resource creation, refer to this post.

Additionally, see the following example policies that should be associated with the non-prod admin user or role.

Note that the following example policies are Deny only, and you would need to have allow statements to enable the IAM principal to tag accounts or resource. See this link to learn more.

{
         "Sid":"DenyModifyingNonDevTags",
         "Effect":"Deny",
         "Action":"organizations:TagResource",
         "Resource":"arn:aws:organizations::*:account/*",
         "Condition":{
            "StringNotEquals":{
               "aws:ResourceTag/accounttype":[
                  "dev",
                  "sandbox"
               ]
            }
         }
      },
      {
         "Sid":"DenyTaggingWithNonDevTags",
         "Effect":"Deny",
         "Action":"organizations:TagResource",
         "Resource":"arn:aws:organizations::*:account/*",
         "Condition":{
            "StringNotEquals":{
               "aws:RequestTag/accounttype":[
                  "dev",
                  "sandbox"
               ]
            }
         }
      }

Closing the accounts

Before closing AWS accounts enrolled with AWS Control Tower, you must first delete the Service Catalog Provisioned Product for the account. Once the provisioned product is terminated, it will show as ‘Not enrolled’ in the AWS Control Tower dashboard. Then, you can use the CloseAccount API to close the account from your Organizations.

The following steps are to close an AWS account which is enrolled with AWS Control Tower:

  1. Open the AWS Service Catalog console in your web browser here.
  2. From the left navigation pane, choose Provisioned products list.
  3. From the list of provisioned accounts, choose the name of the account that you want AWS Control Tower to no longer manage.
  4. On the Provisioned product details page, from the Actions menu, choose Terminate.
  5. From the dialog box that appears, choose Terminate.
  6. When the account has been unmanaged, its status changes to Not Enrolled.

Once you have unmanaged the account, you can now run the CloseAccount API to programmatically close the account. The following figure shows the CLI command snippet that accomplishes this task using the AWS Command Line Interface (AWS CLI).

aws organizations close-account --account-id 555555555555{"CloseAccountStatus": {"AccountId": "555555555555",
      "State": "IN_PROGRESS",
      "RequestedTimestamp": "2022-03-27T19:51:03.563000-07:00",
      "CompletedTimestamp": "2022-03-27T19:51:04.589000-07:00"}}

Once the account is closed, it moves to the Root organizational unit of the organization in suspended state.

Note that you can centrally close up to 10% of the number of active accounts in your organization within a 30-day rolling time window, with a maximum of 200 accounts (for more information about the Close Account quota and rate limits check out our existing quotas documentation).

Considerations

If you don’t delete the provisioned product first and close the account which is under AWS Control Tower governance, then the account is suspended, but you’ll still have a Service Catalog provisioned product for that account in the Available state in the Service Catalog console. With this scenario, the re-registration of the organizational unit will fail with the error:

‘ failed one or more pre-checks.’

This is because AWS Control Tower is now unable to assume the AWSControlTowerExecution role in the account. Furthermore, you won’t be able to terminate the Service Catalog provisioned product for this account. Therefore, you would need to get access to this suspended account, make sure that AWS Control Tower can assume the AWSControlTowerExecution role in the account, terminate the Service Catalog provisioned product for the account, then re-register the organizational unit.

As a defense in depth mechanism, you can implement a detective control to notify when an account in your organization has been closed. You can create an Amazon EventBridge rule that publishes a message to an SNS topic every time that an account is closed that can include the details of who closed the account and when. Organizations provides a 90-day grace period before an account is terminated after calling the CloseAccount API.

See the following sample ‘CloseAccount’ event to configure.

{
    "eventVersion": "1.08",
    "userIdentity": {
        "type": "IAMUser",
        "principalId": "AIDACKCEVSQ6C2EXAMPLE",
        "arn": "arn:aws:iam::123456789012:user/org-account-admin",
        "accountId": "123456789012",
        "accessKeyId": "AKIAIOSFODNN7EXAMPLE",
        "userName": "org-account-admin"
    },
    "eventTime": "2022-04-04T21:29:06Z",
    "eventSource": "organizations.amazonaws.com",
    "eventName": "CloseAccount",
    "awsRegion": "us-east-1",
    "sourceIPAddress": "203.0.113.92",
    "userAgent": "aws-cli/2.5.2 Python/3.9.11 Darwin/19.6.0 exe/x86_64 prompt/off command/organizations.close-account",
    "requestParameters": {
        "accountId": "111122223333"
    },
    "responseElements": null,
    "requestID": "9f2720f5-9d50-4b47-a651-6f14b8a587cc",
    "eventID": "245e572a-9687-47fb-8017-e3ec60575481",
    "readOnly": false,
    "eventType": "AwsApiCall",
    "managementEvent": true,
    "recipientAccountId": "123456789012",
    "eventCategory": "Management"
}

Conclusion

The release of the CloseAccount API lets you programmatically close your AWS accounts that are governed by AWS Control Tower. In this post, I showed you the permissions that you need to setup to provide access to close AWS accounts programmatically, as well as considerations for when you want to close accounts governed by AWS Control Tower.

Author:

Gaurav Gupta

Gaurav Gupta is a Sr. Product Manager in AWS Control Tower team working on core features of AWS Control Tower. In his spare time he plays competitive pool, reads and travel.