How to Fix Ansible 403 Forbidden on AWS EC2
Troubleshooting Guide: Ansible 403 Forbidden on AWS EC2
As Senior DevOps Engineers, we’ve all encountered the dreaded “403 Forbidden” error. When it crops up with Ansible interacting with AWS EC2, it’s almost always a clear indicator of an AWS Identity and Access Management (IAM) permissions issue. This guide will walk you through diagnosing and resolving this common problem efficiently.
1. The Root Cause: Insufficient AWS IAM Permissions
The “Ansible 403 Forbidden” error, when performing actions against AWS EC2, directly translates to a denial of access by the AWS API. This means the AWS credentials Ansible is using do not possess the necessary IAM permissions to perform the requested operation on the specified AWS resource.
In simpler terms, Ansible is trying to do something (e.g., list EC2 instances, launch a new instance, modify a security group), but the AWS user or role it’s authenticating as has not been granted explicit permission to perform that action by its attached IAM policy.
Common Scenarios:
- Dynamic Inventory Failure: Ansible cannot list instances (
ec2:DescribeInstances) to build its inventory. - Module Execution Failure: An Ansible AWS module (e.g.,
community.aws.ec2_instance,community.aws.ec2_security_group) attempts an action (e.g.,ec2:RunInstances,ec2:CreateSecurityGroup,ec2:AuthorizeSecurityGroupIngress) for which the credentials lack permission. - Missing Specific Actions: Even if general EC2 access is granted, specific actions like tagging resources (
ec2:CreateTags) might be missing.
2. Quick Fix (CLI): Granting Necessary Permissions
The quickest way to resolve a 403 error is to identify the AWS principal (user or role) Ansible is using and ensure it has the required permissions.
Step 1: Identify the AWS Principal Ansible is Using
Ansible (via boto3) follows the standard AWS SDK credential chain:
- Environment Variables:
AWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY,AWS_SESSION_TOKEN. - Shared Credentials File:
~/.aws/credentials(and~/.aws/configfor profiles). - EC2 Instance Profile: If Ansible is running on an EC2 instance, it will assume the instance’s attached IAM role.
- ECS/EKS/Lambda: Similar to EC2 instance profiles, but for container/serverless environments.
Determine which of these applies to your Ansible execution environment. The most common are environment variables, shared credentials, or an EC2 instance profile.
Step 2: Attach a Broad (Temporary) Policy to Test
While not recommended for production, attaching a broad managed policy like AmazonEC2FullAccess or ReadOnlyAccess can quickly confirm if the issue is indeed IAM related.
For an IAM User:
# First, ensure your AWS CLI is configured with credentials that have permission
# to modify IAM policies (e.g., AdministratorAccess).
# Then, attach the policy to the IAM user Ansible is using.
aws iam attach-user-policy \
--user-name YourAnsibleIAMUser \
--policy-arn arn:aws:iam::aws:policy/AmazonEC2FullAccess
Replace YourAnsibleIAMUser with the actual IAM user name. If Ansible only needs to read information (e.g., for dynamic inventory), use arn:aws:iam::aws:policy/AmazonEC2ReadOnlyAccess.
For an IAM Role (e.g., on an EC2 Instance Profile):
# Attach the policy to the IAM role.
aws iam attach-role-policy \
--role-name YourAnsibleIAMRole \
--policy-arn arn:aws:iam::aws:policy/AmazonEC2FullAccess
Replace YourAnsibleIAMRole with the actual IAM role name.
Step 3: Test Immediately
After attaching the policy, re-run your Ansible command. If the 403 error disappears, you’ve confirmed the root cause. Proceed to the “Configuration Check” section to refine your permissions.
3. Configuration Check: Refining Permissions & Verifying Settings
Once you’ve confirmed IAM is the culprit, the next step is to refine your permissions using the principle of least privilege and verify all related configurations.
3.1. AWS IAM Policy Review
This is the most critical step. Examine the specific IAM policy (or policies) attached to the user or role.
- Locate the Policy:
- Navigate to the AWS IAM console.
- Go to “Users” or “Roles”.
- Find your principal, then click on the “Permissions” tab to see attached policies.
- Review both “Managed Policies” and “Inline Policies”.
- Identify Missing Actions:
- The AWS API error message often hints at the specific action that was denied (e.g., “User is not authorized to perform:
ec2:DescribeInstances”). - Ensure your policy explicitly
Allows the necessary actions. - Example Policy Snippet for Dynamic Inventory & Basic Instance Control:
Remember to scope{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "ec2:DescribeInstances", "ec2:DescribeRegions", "ec2:DescribeVpcs", "ec2:DescribeSubnets", "ec2:DescribeSecurityGroups", "ec2:DescribeTags" ], "Resource": "*" }, { "Effect": "Allow", "Action": [ "ec2:RunInstances", "ec2:StartInstances", "ec2:StopInstances", "ec2:TerminateInstances", "ec2:CreateTags", "ec2:DeleteTags" ], "Resource": "arn:aws:ec2:REGION::instance/*" } ] }Resourcefields as narrowly as possible in production.
- The AWS API error message often hints at the specific action that was denied (e.g., “User is not authorized to perform:
- Check for Explicit Denies: Sometimes, a
Denystatement in another policy might override anAllowstatement. Ensure there are no policies explicitly denying the required actions.
3.2. AWS CLI Configuration (~/.aws/credentials and ~/.aws/config)
Verify that the aws_access_key_id, aws_secret_access_key, and region are correctly configured for the profile Ansible is using.
# List configured profiles
aws configure list-profiles
# Verify default profile (or the one Ansible is configured to use)
aws configure list
Ensure the region is correct for the resources Ansible is trying to manage.
3.3. Ansible Configuration (ansible.cfg and Inventory)
-
ansible.cfg: Check for any[aws]or[boto3]sections that might override credentials, regions, or other AWS-related settings. While less common, these could point to credentials that don’t have the expected permissions. -
Dynamic Inventory Script/Plugin: If using the
community.aws.aws_ec2plugin (e.g.,aws_ec2.yml), ensure:plugin: aws_ec2is correctly specified.regionsare correctly defined.- If using
aws_profile, ensure that profile exists in~/.aws/credentialsand has the correct permissions.
# aws_ec2.yml example plugin: community.aws.aws_ec2 regions: - us-east-1 - us-west-2 # Uncomment and specify if you need a specific profile # aws_profile: my-ansible-profile # filters: # instance-state-name: running
4. Verification: Confirming the Fix
Once you’ve adjusted the IAM policies or other configurations, it’s time to verify your solution.
4.1. Basic Connectivity Test (Dynamic Inventory)
If you’re using dynamic inventory, run a graph command to ensure Ansible can successfully query AWS:
ansible-inventory -i your_aws_ec2_inventory.yml --graph
This should output a hierarchical list of your EC2 instances without a 403 error.
4.2. Run a Simple AWS-Related Playbook/Module
Execute a minimal playbook or ad-hoc command that uses an AWS module to perform the action that previously failed.
Example: Listing EC2 Instances (if dynamic inventory fails)
ansible localhost -m community.aws.ec2_instance_info -a 'filters="{ \"instance-state-name\": \"running\" }"'
This command, run from your Ansible controller, will attempt to describe running instances using the configured AWS credentials. If it runs successfully, returning instance information, your permissions are likely resolved.
By systematically addressing the IAM permissions and verifying your configurations, you can efficiently resolve “Ansible 403 Forbidden” errors on AWS EC2, ensuring your automation workflows run smoothly. Always strive for the principle of least privilege in your IAM policies to maintain a secure AWS environment.