I’ve been told that when the company I work for first started to create new AWS accounts, they would have to set aside a day for it. A director also had to be tracked down in order to get hold of the company credit card details.
Fast forward a few years and this process still took at least an hour – maybe two hours depending on what it was you were creating. There was a 42 page word document to follow with step by step instructions, policies that had to be typed in by hand (following screenshots) and cross account access rights that had to be in place.
The likelihood of missing something or a user inputting a typo was just too high and added another layer of complexity in that every new account had to be double checked.
I was tasked with automating as much of this process as I could to save time.
I started this by enabling AWS Organizations and bringing all of the accounts under control properly. Previously, cloudtrail was set up on new accounts manually, setting up all of the S3 bucket rights as you went, making sure that the trail created properly, that it was logging where it should, etc.
By turning on AWS Organizations and enabling cloudtrail, I was able to set an organisation wide cloudtrail policy that applied to all accounts old and new automatically. I was then able to delete the manually created trail configs in each of the old accounts.
I was trying to find a little guidance on how to leverage cloudformation for the creation of accounts and I was lucky in that Amazon had written this blog post earlier this year and it looked to be the starting point for what it was we needed.
The problem being – it wasn’t quite what we needed, and it certainly wasn’t the drag and drop solution I was hoping that it would be.
For starters, this tooling assumes that your users will be in the master account for the organisation. We have them in a separate organisation.
It also asks for allsorts of Region and AZ config, along with CIDR ranges for network addresses – we don’t need this level of customisation. I set about stripping out what it did, from both the cloudformation template used in the service catalogue and the python script which creates the account and all necessary resources.
It also asked for the baseline template, s3 bucket name and various other variables that would just never change for us. Hardcoding them and removing them from the user form meant further improvements in speed and accuracy.
I didn’t want an admin user creating, we don’t need one – so I stripped that out.
It also created a service catalog (at a charge of $5 per month per account) with a load of stuff in – that we just don’t need right now (though we will be moving to this in the future I would think).
We have a different role name that we use for cross account access – I had to make changes there.
This cross account access needed to have a trust relationship to the user account, and not the master org account, so again I had to make some changes there.
There were also special policies that restrict the creation of IAM Users and Groups that I needed to put in place along with other organisation accounts and policies.
attach_policy_response = iam_client.attach_role_policy(RoleName=newrole,PolicyArn=iammarn)
It’s all well and good when you’re assigning AWS built in policies, that arn never changes.
When you want to create a policy, in your new account, and then assign that policy to a new role – then things get a little trickier – but it is possible by creating a variable based off other variables and text:
iammarn = ‘arn:aws:iam::‘+account_id+’:policy/IAM_User_Restrictions’
I also added the function to set up the IAM alias for login, based on the accountname provided by the user. This was done using:
create_alias_response = iam_client.create_account_alias(AccountAlias=accountname)
Through a lot of trial and error (and a lot of created dummy accounts) I’ve finally gotten us to a place where a new account is set up automatically and the admin user needs only provide the name of the new account and the email address for the root user.
Due to the way that cloudformation triggered the python code, and the lack of any ability to edit an account in-situ, I had to make changes and then fire a new dummy account (which needed its own unique email address).
The entire process takes less than 3 minutes. 3 years ago – this took a person out for an entire day. Now, it takes less than 3 minutes and most of that is non-interactive time. The user time this takes up is probably less than 20 seconds.
The finished product is:
A brand new AWS account within our AWS Organisation.
A trust relationship back to the user AWS account, allowing admin access through assume role.
Special company policies that we have in place for restricting the creation of users and groups.
Denies access to the billing screens.
Creates 2 company specific roles, with their own policies (will create as many as we need to have in a standard account) and assigns these policies on the fly.
Sets up CloudTrail.
Sets up the IAM alias so that users can log in using a friendly name rather than an account ID.
It probably does a little bit more magic too – it doesn’t sound like a lot, but it took hours and hours and hours of engineering.
I’m not finished yet – there are a few more tweaks I need to make and the code is definitely not the best it could be – but I’m a sysop/engineer by trade and my python is a little rusty. AD integration is also coming in the next few months taking away even more of the setup from the user account side of things – everything will be managed with RBAC groups and it’ll just be a case of setting up a group in the AWS account that ties back to that AD group with the relevant STS assume role permission for that particular group of users – be it developer or ReadOnly access for an architect.
Automating laborious, long winded and repeatable processes to reduce human error and free up more engineering time for service improvements and maybe even some professional development to ensure that that you’re in the best position for supporting the business going forward is the key here.
Cloudformation is a fierce beast, especially when partnered with lambda functions running python scripts from S3.