Ansible and AWS cross-account access

To access AWS resources in another account you can create dedicated IAM user or use IAM roles. If you decide to create user, you need to generate username and password for AWS Console or access keys for API. Baking this keys in your code is almost always a bad idea.

By setting up roles for cross-account access you don’t need to create individual IAM users in each account and access key are automatically rotated for you. Lets take a look on an example how to use it with Ansible.

Example

Lets assume that we have Ansible running on EC2 instance in our source account. We want to create Route53 hosted zone in another account (target) using Ansible playbook. It may as well be some other resource, like EC2 or S3 bucket. Our requirement is that we do not want to hardcode any keys into playbooks or encrypt it using vault. To achieve that we can delegate access across AWS accounts using IAM roles.

Ansible and AWS cross-account access

Creating role in target account

1. Create IAM role with Another Account option. For account ID type source account ID.

Create role screen – another AWS account

2. Assign appropriate policy. Specify rights that you want to allow on destination account. In this example I wanted to create new hosted zone in Route53. For simplicity I used full access to Route53, but you should always narrow policy just for needed actions. You can read more about IAM in my previous post.

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": "route53:*",
            "Resource": "*"
        }
    ]
}

3. Write down ARN of created role.

Creating role in source account

1. Create role with AWS Service EC2 option in source account.


Create role screen – AWS EC2 service

2. Create custom policy to allow sts:AssumeRole for target account role.

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": "sts:AssumeRole",
            "Resource": "arn:aws:iam::{TARGET_ARN_ROLE}”
        }
    ]
}

Assigning role to EC2 instance with Ansible

Assign source role to EC2 instance where you have installed your RedHat Ansible.

Assign role to Ansible EC2 instance

Creating Ansible playbook

Now you can use Ansible to create Route53 zone without adding access key/secret key into the code. First you must assume created role from target account. Next you can use route53_zone module to create mydomain.local zone. Instead of specifying key you can pass sts_assume_role returned values.

	- name: Route53 add zone
	  hosts: localhost
	
	  tasks:
	    - sts_assume_role:
	        role_arn: "arn:aws:iam::ARN_TARGET_ACCOUNT"
	        role_session_name: "someRoleSession"
	        region: eu-west-1
	      register: assumed_role
	
	    - route53_zone:
	        aws_access_key: "{{ assumed_role.sts_creds.access_key }}"
	        aws_secret_key: "{{ assumed_role.sts_creds.secret_key }}"
	        security_token: "{{ assumed_role.sts_creds.session_token }}"
	        zone: mydomain.local
	        vpc_id: 'vpc-ID'
	        vpc_region: eu-west-1

How this works?

How Ansible knows the credentials? To use an IAM role, you have to make an API call to STS:AssumeRole, which will return a temporary access key ID, secret key, and security token that can then be used to sign future API calls. STS generates temporary credentials with a relatively short expiration time (1 hour, by default). You can access them via metadata from you Ansible EC2 instance:

http://169.254.169.254/latest/meta-data/iam/security-credentials/{role-name}

Ansible does the calls behind the scenes to obtain access keys. Same principle is used with other tools, like AWS CLI or it is included in AWS SDKs.

Materials

Delegate Access Across AWS Accounts Using IAM Roles
How to Use a Single IAM User to Easily Access All Your Accounts by Using the AWS CLI

Comments 2

Leave a Reply