Skip to content

Commit e39d38a

Browse files
committed
feat: Add AWS credentials file support
- Automatically reads AWS credentials from ~/.aws/credentials - Supports AWS_PROFILE and AWS_SHARED_CREDENTIALS_FILE environment variables - Adds support for temporary credentials with session tokens - Maintains backward compatibility with existing credential methods - Follows standard AWS credential precedence order Based on PR #14460 by @lefth with the following improvements: - Fixed variable naming to match existing code (access_key vs aws_access_key) - Added session token support for temporary credentials - Integrated credential discovery directly into prompts.yml - Added comprehensive tests - Added documentation Closes #14382
1 parent 7acdca0 commit e39d38a

File tree

5 files changed

+215
-4
lines changed

5 files changed

+215
-4
lines changed

docs/aws-credentials.md

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# AWS Credential Configuration
2+
3+
Algo supports multiple methods for providing AWS credentials, following standard AWS practices:
4+
5+
## Methods (in order of precedence)
6+
7+
1. **Command-line variables** (highest priority)
8+
```bash
9+
./algo -e "aws_access_key=YOUR_KEY aws_secret_key=YOUR_SECRET"
10+
```
11+
12+
2. **Environment variables**
13+
```bash
14+
export AWS_ACCESS_KEY_ID=YOUR_KEY
15+
export AWS_SECRET_ACCESS_KEY=YOUR_SECRET
16+
export AWS_SESSION_TOKEN=YOUR_TOKEN # Optional, for temporary credentials
17+
./algo
18+
```
19+
20+
3. **AWS credentials file** (lowest priority)
21+
- Default location: `~/.aws/credentials`
22+
- Custom location: Set `AWS_SHARED_CREDENTIALS_FILE` environment variable
23+
- Profile selection: Set `AWS_PROFILE` environment variable (defaults to "default")
24+
25+
## Using AWS Credentials File
26+
27+
After running `aws configure` or manually creating `~/.aws/credentials`:
28+
29+
```ini
30+
[default]
31+
aws_access_key_id = YOUR_KEY_ID
32+
aws_secret_access_key = YOUR_SECRET_KEY
33+
34+
[work]
35+
aws_access_key_id = WORK_KEY_ID
36+
aws_secret_access_key = WORK_SECRET_KEY
37+
aws_session_token = TEMPORARY_TOKEN # Optional
38+
```
39+
40+
To use a specific profile:
41+
```bash
42+
AWS_PROFILE=work ./algo
43+
```
44+
45+
## Security Considerations
46+
47+
- Credentials files should have restricted permissions (600)
48+
- Consider using AWS IAM roles or temporary credentials when possible
49+
- Tools like [aws-vault](https://github.com/99designs/aws-vault) can provide additional security by storing credentials encrypted
50+
51+
## Troubleshooting
52+
53+
If Algo isn't finding your credentials:
54+
55+
1. Check file permissions: `ls -la ~/.aws/credentials`
56+
2. Verify the profile name matches: `AWS_PROFILE=your-profile`
57+
3. Test with AWS CLI: `aws sts get-caller-identity`
58+
59+
If credentials are found but authentication fails:
60+
- Ensure your IAM user has the required permissions (see [EC2 deployment guide](deploy-from-ansible.md))
61+
- Check if you need session tokens for temporary credentials

docs/cloud-amazon-ec2.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,14 @@ Enter the number of your desired provider
8181
: 3
8282
```
8383

84-
Next, you will be asked for the AWS Access Key (Access Key ID) and AWS Secret Key (Secret Access Key) that you received in the CSV file when you setup the account (don't worry if you don't see your text entered in the console; the key input is hidden here by Algo).
84+
Next, Algo will need your AWS credentials. If you have already configured AWS CLI with `aws configure`, Algo will automatically use those credentials. Otherwise, you will be asked for the AWS Access Key (Access Key ID) and AWS Secret Key (Secret Access Key) that you received in the CSV file when you setup the account (don't worry if you don't see your text entered in the console; the key input is hidden here by Algo).
85+
86+
**Automatic credential detection**: Algo will check for credentials in this order:
87+
1. Command-line variables
88+
2. Environment variables (`AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY`)
89+
3. AWS credentials file (`~/.aws/credentials`)
90+
91+
If none are found, you'll see these prompts:
8592

8693
```
8794
Enter your aws_access_key (http://docs.aws.amazon.com/general/latest/gr/managing-aws-access-keys.html)
@@ -94,6 +101,8 @@ Enter your aws_secret_key (http://docs.aws.amazon.com/general/latest/gr/managing
94101
[ABCD...]:
95102
```
96103

104+
For more details on credential configuration, see the [AWS Credentials guide](aws-credentials.md).
105+
97106
You will be prompted for the server name to enter. Feel free to leave this as the default ("algo") if you are not certain how this will affect your setup. Here we chose to call it "algovpn".
98107

99108
```

roles/cloud-ec2/tasks/cloudformation.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
cloudformation:
44
aws_access_key: "{{ access_key }}"
55
aws_secret_key: "{{ secret_key }}"
6+
aws_session_token: "{{ session_token if session_token else omit }}"
67
stack_name: "{{ stack_name }}"
78
state: present
89
region: "{{ algo_region }}"

roles/cloud-ec2/tasks/prompts.yml

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,30 @@
11
---
2+
# Discover AWS credentials from standard locations
3+
- name: Set AWS credentials file path
4+
set_fact:
5+
aws_credentials_path: "{{ lookup('env', 'AWS_SHARED_CREDENTIALS_FILE') | default(lookup('env', 'HOME') + '/.aws/credentials', true) }}"
6+
aws_profile: "{{ lookup('env', 'AWS_PROFILE') | default('default', true) }}"
7+
8+
# Try to read credentials from file if not already provided
9+
- block:
10+
- name: Check if AWS credentials file exists
11+
stat:
12+
path: "{{ aws_credentials_path }}"
13+
register: aws_creds_file
14+
delegate_to: localhost
15+
16+
- name: Read AWS credentials from file
17+
set_fact:
18+
_file_access_key: "{{ lookup('ini', 'aws_access_key_id', section=aws_profile, file=aws_credentials_path, errors='ignore') | default('', true) }}"
19+
_file_secret_key: "{{ lookup('ini', 'aws_secret_access_key', section=aws_profile, file=aws_credentials_path, errors='ignore') | default('', true) }}"
20+
_file_session_token: "{{ lookup('ini', 'aws_session_token', section=aws_profile, file=aws_credentials_path, errors='ignore') | default('', true) }}"
21+
when: aws_creds_file.stat.exists
22+
no_log: true
23+
when:
24+
- aws_access_key is undefined
25+
- lookup('env','AWS_ACCESS_KEY_ID')|length <= 0
26+
27+
# Prompt for credentials if still not available
228
- pause:
329
prompt: |
430
Enter your AWS Access Key ID (http://docs.aws.amazon.com/general/latest/gr/managing-aws-access-keys.html)
@@ -8,6 +34,7 @@
834
when:
935
- aws_access_key is undefined
1036
- lookup('env','AWS_ACCESS_KEY_ID')|length <= 0
37+
- _file_access_key is undefined or _file_access_key|length <= 0
1138

1239
- pause:
1340
prompt: |
@@ -17,16 +44,21 @@
1744
when:
1845
- aws_secret_key is undefined
1946
- lookup('env','AWS_SECRET_ACCESS_KEY')|length <= 0
47+
- _file_secret_key is undefined or _file_secret_key|length <= 0
2048

49+
# Set final credentials with proper precedence
2150
- set_fact:
22-
access_key: "{{ aws_access_key | default(_aws_access_key.user_input|default(None)) | default(lookup('env','AWS_ACCESS_KEY_ID'), true) }}"
23-
secret_key: "{{ aws_secret_key | default(_aws_secret_key.user_input|default(None)) | default(lookup('env','AWS_SECRET_ACCESS_KEY'), true) }}"
51+
access_key: "{{ aws_access_key | default(lookup('env','AWS_ACCESS_KEY_ID')) | default(_file_access_key) | default(_aws_access_key.user_input|default(None)) }}"
52+
secret_key: "{{ aws_secret_key | default(lookup('env','AWS_SECRET_ACCESS_KEY')) | default(_file_secret_key) | default(_aws_secret_key.user_input|default(None)) }}"
53+
session_token: "{{ aws_session_token | default(lookup('env','AWS_SESSION_TOKEN')) | default(_file_session_token) | default('') }}"
54+
no_log: true
2455

2556
- block:
2657
- name: Get regions
2758
aws_region_info:
2859
aws_access_key: "{{ access_key }}"
2960
aws_secret_key: "{{ secret_key }}"
61+
aws_session_token: "{{ session_token if session_token else omit }}"
3062
region: us-east-1
3163
register: _aws_regions
3264

@@ -67,6 +99,7 @@
6799
ec2_eip_info:
68100
aws_access_key: "{{ access_key }}"
69101
aws_secret_key: "{{ secret_key }}"
102+
aws_session_token: "{{ session_token if session_token else omit }}"
70103
region: "{{ algo_region }}"
71104
register: raw_eip_addresses
72105

@@ -85,4 +118,4 @@
85118

86119
- set_fact:
87120
existing_eip: "{{ available_eip_addresses[_use_existing_eip.user_input | int -1 ]['allocation_id'] }}"
88-
when: cloud_providers.ec2.use_existing_eip
121+
when: cloud_providers.ec2.use_existing_eip

tests/test-aws-credentials.yml

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
---
2+
# Test AWS credential reading from files
3+
# Run with: ansible-playbook tests/test-aws-credentials.yml
4+
5+
- name: Test AWS credential file reading
6+
hosts: localhost
7+
gather_facts: no
8+
vars:
9+
# These would normally come from config.cfg
10+
cloud_providers:
11+
ec2:
12+
use_existing_eip: false
13+
14+
tasks:
15+
- name: Test with environment variables
16+
block:
17+
- include_tasks: ../roles/cloud-ec2/tasks/prompts.yml
18+
vars:
19+
algo_server_name: test-server
20+
21+
- assert:
22+
that:
23+
- access_key == "test_env_key"
24+
- secret_key == "test_env_secret"
25+
msg: "Environment variables should take precedence"
26+
vars:
27+
AWS_ACCESS_KEY_ID: "test_env_key"
28+
AWS_SECRET_ACCESS_KEY: "test_env_secret"
29+
environment:
30+
AWS_ACCESS_KEY_ID: "test_env_key"
31+
AWS_SECRET_ACCESS_KEY: "test_env_secret"
32+
33+
- name: Test with command line variables
34+
block:
35+
- include_tasks: ../roles/cloud-ec2/tasks/prompts.yml
36+
vars:
37+
aws_access_key: "test_cli_key"
38+
aws_secret_key: "test_cli_secret"
39+
algo_server_name: test-server
40+
region: "us-east-1"
41+
42+
- assert:
43+
that:
44+
- access_key == "test_cli_key"
45+
- secret_key == "test_cli_secret"
46+
msg: "Command line variables should take precedence over everything"
47+
48+
- name: Test reading from credentials file
49+
block:
50+
- name: Create test credentials directory
51+
file:
52+
path: /tmp/test-aws
53+
state: directory
54+
mode: '0700'
55+
56+
- name: Create test credentials file
57+
copy:
58+
dest: /tmp/test-aws/credentials
59+
mode: '0600'
60+
content: |
61+
[default]
62+
aws_access_key_id = test_file_key
63+
aws_secret_access_key = test_file_secret
64+
65+
[test-profile]
66+
aws_access_key_id = test_profile_key
67+
aws_secret_access_key = test_profile_secret
68+
aws_session_token = test_session_token
69+
70+
- name: Test default profile
71+
include_tasks: ../roles/cloud-ec2/tasks/prompts.yml
72+
vars:
73+
algo_server_name: test-server
74+
region: "us-east-1"
75+
environment:
76+
HOME: /tmp/test-aws
77+
AWS_ACCESS_KEY_ID: ""
78+
AWS_SECRET_ACCESS_KEY: ""
79+
80+
- assert:
81+
that:
82+
- access_key == "test_file_key"
83+
- secret_key == "test_file_secret"
84+
msg: "Should read from default profile"
85+
86+
- name: Test custom profile
87+
include_tasks: ../roles/cloud-ec2/tasks/prompts.yml
88+
vars:
89+
algo_server_name: test-server
90+
region: "us-east-1"
91+
environment:
92+
HOME: /tmp/test-aws
93+
AWS_PROFILE: "test-profile"
94+
AWS_ACCESS_KEY_ID: ""
95+
AWS_SECRET_ACCESS_KEY: ""
96+
97+
- assert:
98+
that:
99+
- access_key == "test_profile_key"
100+
- secret_key == "test_profile_secret"
101+
- session_token == "test_session_token"
102+
msg: "Should read from custom profile with session token"
103+
104+
- name: Cleanup test directory
105+
file:
106+
path: /tmp/test-aws
107+
state: absent

0 commit comments

Comments
 (0)