By now, the use of native Infrastructure as Code (IaC) tools to prep your AWS account for third-party IaC stack should come as no surprise. With the rise of the ever-so-friendly HCL, and the versatility of the tools, many companies are forgoing native tools, such as AWS CloudFormation, and going straight to using Terraform. That may not be the best move! Most of the time, the initial config and setup are done manually, and thus never documented, which goes against the IaC methodology—unless of course you’re keeping a repo with account config code.
To fix the missing documentation steps, we’ll use CloudFormation to deploy initial resources needed for third-party IaC Stacks. I know some teams would rather create cross-account roles and assume it locally to deploy backend configs, but I find it extremely difficult to document and I’d rather have a common template.
Always start with IAM
After considering the purpose of the account and what/who is going to access the account, and which resources, we can start creating the IAM roles, users, and policies.
IAM: Jenkins Role + Instance Profile
We’ve already decided that this account will host a Jenkins box to do deployments using Pipelines from Github. With that in mind, we need to include the following in our CloudFormation template:
- Jenkins EC2 Role with “ReadOnly” access to S3. We want to be able to fetch stuff from S3
- Inline policy to enable our Jenkins box to both “Create” and “Read” SSM parameters. We are aiming fetch and sensitive information to and from our Jenkins box (SSH key for Repo Access, Initial Password, Roles, Buckets, Account Ids, etc…)
IAM: Packer User + Keys + SSM Parameter
This should be fairly simple; In a perfect world we’d be using a role that could only be assumed by the Jenkins box. However, at the time of publication, Packer does (did) not support role-based deployment. Therefore, we’ll create a user with an inline policy using the official documentation. Also, we’re going to include in our CloudFormation template a resource for access keys and SSM parameters to hold these values (be sure to use encryption). That way we can call the parameter as an input variable.
NOTE: be sure to include the following as part of the user’s inline policy. You’ll need it when giving the Packer temp instance a profile/role.
IAM: Terraform Role (Deployment Role)
As mentioned above, we’ll use this role to make pipeline deployments. With that in mind, we’ll go ahead and include the following in our CloudFormation:
- Terraform Role that can ONLY be assumed by the previous Jenkins Role. Permissions on this role should be limited to the resource you want to deploy—most people would grant PowerUser but this seems like overkill.
Note: if using cross-account access, set this up on the recipient account
S3: Terraform Bucket + Bucket Policy
We need to include a S3 Bucket with SSE + Versioning enabled.
Don’t forget to limit access with a Bucket Policy that only allows our Terraform Role since each pipeline deployment will assume the Terraform Role to fetch the TF State.
DynamoDB: Terraform Lock Table
This one is relatively simple. All we need is a simple table with a lock ID attribute and a schema by the same value. Minimum read/write capacity…5, maybe?
SSM Parameters: Bucket ARN, Terraform Role ARN, Packer Access Keys
These are the parameters we’ll pass when we perform a Packer build or Terraform init in our Jenkins Pipelines. This will allow us to avoid having hardcoded sensitive data in our Jenkins box or our Github repos where our Jenkins files will be stored.
Following the above steps in an initial CloudFormation template will provide a clear understanding of the starting path, versus performing manual setups to pick up IaC methodologies in the future. Also, the template can always be adjusted to accommodate cross-account access. If you’re interested in learning more about AWS, Terraform, Packer, or any other cloud technologies, please don’t hesitate to reach out to us. We’d love to help you get started.