AWS Organizations has been available since late 2016 but got the Cloudformation support just recently. Until now you had to write your own custom resources to manage accounts and organizations in Cloudformation, that then made you responsible of maintaining attached code and lambda functions. With the announced Cloudformation support, it is now possible to create, and to some extent manage, organizations tree, policies and AWS accounts in Cloudformation stacks without extra hassle.

AWS::Organizations resource types

AWS::Organizations has 3 resource types

OrganizationalUnit

AWS::Organizations::OrganizationalUnit creates an organizational unit (OU) within organization. It does pretty much what the name says. However it is good to note that while you can in theory update OU resources, it isn’t possible to move an OU within organizations if you have accounts attached to it. In practice this makes OU pretty much immutable, except the tags.

Policy

AWS::Organizations::Policy creates SCP, tag, backup and AI services opt-out policies attached to a root, an organizational unit (OU), or an individual AWS account. First thing to notice is you must enable policy type for the organization before you can create one. Unfortunately there isn’t support for this in Cloudformation, but it is typically a configuration you do just once, so it is fine to do that manually. Second thing that is easy to miss, is size limit of policy document. SCP can be max 5120 bytes, but you can get around the limit by splitting it into multiple policies and using policy inheritance in organization. Though it would have been nice if AWS would do splitting automatically on your behalf.

Account

AWS::Organizations::Account is the most interesting and complex of new resource types. There is a lot of fine-print how it differs from normal resources and what you can and can not do with it.

If you include multiple accounts in a single template, you must use the DependsOn attribute on each account resource type so that the accounts are created sequentially. If you create multiple accounts at the same time, Organizations returns an error and the stack operation fails.

Even if it is allowed to create 5 accounts concurrently, in Cloudformation you must create accounts concurrently. I think this restriction was put into place to make book keeping easier in Cloudformation backend, I think.

You can’t modify the following list of Account resource parameters using AWS CloudFormation updates.

  • AccountName
  • Email
  • RoleName

If you attempt to update the listed parameters, CloudFormation will attempt the update, but you will receive an error message as those updates are not supported from an Organizations management account or a registered delegated administrator account. Both the update and the update roll-back will fail, so you must skip the account resource update.

Resources having immutable attributes isn’t anything new, but it does sound bit strange that both update and roll-back will fail. It might sound bit odd how there can be roll-back if update already fails before that. But when you have multiple resources in the stack, failing to create any of those could trigger a roll-back and if the account was already created, it couldn’t be removed.

When you create an account in an organization using AWS CloudFormation, you can’t specify a value for the CreateAccount operation parameter IamUserAccessToBilling. The default value for parameter IamUserAccessToBilling is ALLOW, and IAM users and roles with the required permissions can access billing information for the new account.

Ok. I think this is good default choice. Cost transparency will help engineers to create more efficient services and if it is a must to restrict access to billing data, it can be done with SCP or IAM policies.

If you get an exception …

… that indicates DescribeCreateAccountStatus returns IN_PROGRESS state before time out. You must check the account creation status using the DescribeCreateAccountStatus operation. If the account state returns as SUCCEEDED, you can import the account into AWS CloudFormation management using resource import.

… that indicates you have exceeded your account quota for the organization, you can request an increase by using the Service Quotas console.

… that indicates the operation failed because your organization is still initializing, wait one hour and then try again. If the error persists, contact AWS Support.

We don’t recommend that you use the CreateAccount operation to create multiple temporary accounts. You can close accounts using the CloseAccount operation or from the AWS Organizations console in the organization’s management account. For information on the requirements and process for closing an account, see Closing an AWS account in the AWS Organizations User Guide.

Couldn’t agree more :-) Default quota for accounts in organization is just 10 accounts, and one of them is the root account. And before going and creating 9 accounts while testing this new feature, do remind yourself it is possible to close max 10% of accounts in organization, i.e. 1 account when you have 10 or less, within 30 days. At that rate it will take a year1 (!!!) to clean-up test accounts after hitting organization account limit.

1 9 months to initiate 9 account closures + 90 days grace period in SUSPENDED state.

Importing Resources

Both Account and OrganizationalUnit support import and drift detection, even though these are not mentioned on “Resources that support import and drift detection”. I suppose document will be updated shortly …

Importing resources to stack is often more tricky than it seems. Maybe because this isn’t part of normal day-to-day workflow. During a quick testing I ran into 2 issues that were not totally obvious before hitting them.

First when you create a stack with imported resources, you must first provide existing resources before defining parameters or resolving conditions. Therefore if you have conditional resources, like Account1 below, you must supply an account id for it, even if that wouldn’t be part of the stack created. And in the end account will be part of the stack, but drift detection will flag it because parameters of the account don’t match with stack.

Conditions:

  CreateAccount1: !And
    - !Not [!Equals [!Ref AccountName1, "" ]]
    - !Not [!Equals [!Ref AccountEmail1, "" ]]
  
Resources:

  OU:
    Type: AWS::Organizations::OrganizationalUnit
    DeletionPolicy: Retain
    Properties:
        Name: !Ref OUName
        ParentId: !Ref OUParent

  Account1:
    Type: AWS::Organizations::Account
    Condition: CreateAccount1
    DeletionPolicy: Retain
    Properties: 
      AccountName: !Ref AccountName1
      Email: !Ref AccountEmail1
      ParentIds: 
        - !Ref OU

Second thing was, it isn’t possible to fix drift in AccountName, AccountEmail or RoleName. In normal case I would update template parameters after the import and that would trigger dummy update even when there would not be any changes to actual settings. However Cloudformation refuses to ‘update’ Account resources. One can say this isn’t a bug, but feature, and you were warmed about it above. However it would have been nice to check if update is a real update trying to change account settings (and failing on that), or just a dummy update to adjust stack state to match with reality.

Summary

Dispite some shortcomings there are some real use cases for managing AWS organizations and accounts with Cloudformation. With this it should be possible to AWS Service Catalog as simple account provisioning tool. The Fach you must deploy the stack containing the account into organization root account limits the usefullnes a bit. It would nice to be able to delegate account creation permissions to other accounts, maybe so that it was limited to OU the creating account belongs to.

Another interesting case might be, modelling organization structure and accounts in Cloudformation stack on root account. There it would become handy to import already existing organization structure. Just need to be very careful to get the details right first time, as there are no updates for accounts ;-)

If you want to test this yourself, here is a small template I wrote for myself. Just be careful not to create more account you can delete.