AWS Organizations + Cloudformation
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
- 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.