Skip to content

Commit 1c435bb

Browse files
authored
feat: Add option to provide external ID (#32)
Fixes #28
1 parent 4d0082a commit 1c435bb

File tree

4 files changed

+62
-11
lines changed

4 files changed

+62
-11
lines changed

README.md

+6-4
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,9 @@ We recommend following [Amazon IAM best practices](https://docs.aws.amazon.com/I
5151
* [Monitor the activity](https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html#keep-a-log) of the credentials used in GitHub Actions workflows.
5252
5353
## Assuming a role
54-
If you would like to use the credentials you provide to this action to assume a role, you can do so by specifying the role ARN in `role-to-assume`.
55-
The role credentials will then be output instead of the ones you have provided.
56-
The default session duration is 6 hours, but if you would like to adjust this you can pass a duration to `role-duration-seconds`.
54+
If you would like to use the static credentials you provide to this action to assume a role, you can do so by specifying the role ARN in `role-to-assume`.
55+
The role credentials will then be configured in the Actions environment instead of the static credentials you have provided.
56+
The default session duration is 6 hours, but if you would like to adjust this you can pass a duration to `role-duration-seconds`.
5757
The default session name is GitHubActions, and you can modify it by specifying the desired name in `role-session-name`.
5858

5959
Example:
@@ -64,10 +64,12 @@ Example:
6464
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
6565
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
6666
aws-region: us-east-2
67-
role-to-assume: arn:aws:iam::123456789100:role/role-to-assume
67+
role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }}
68+
role-external-id: ${{ secrets.AWS_ROLE_EXTERNAL_ID }}
6869
role-duration-seconds: 1200
6970
role-session-name: MySessionName
7071
```
72+
In this example, the secret `AWS_ROLE_TO_ASSUME` contains a string like `arn:aws:iam::123456789100:role/role-to-assume`.
7173

7274
### Session tagging
7375
The session will have the name "GitHubActions" and be tagged with the following tags:

action.yml

+11-2
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,26 @@ inputs:
1717
description: 'AWS Region, e.g. us-east-2'
1818
required: true
1919
mask-aws-account-id:
20-
description: "Whether to set the AWS account ID for these credentials as a secret value, so that it is masked in logs. Valid values are 'true' and 'false'. Defaults to true"
20+
description: >-
21+
Whether to set the AWS account ID for these credentials as a secret value,
22+
so that it is masked in logs. Valid values are 'true' and 'false'.
23+
Defaults to true
2124
required: false
2225
role-to-assume:
23-
description: "Use the provided credentials to assume a Role and output the assumed credentials for that Role rather than the provided credentials"
26+
description: >-
27+
Use the provided credentials to assume an IAM role and configure the Actions
28+
environment with the assumed role credentials rather than with the provided
29+
credentials
2430
required: false
2531
role-duration-seconds:
2632
description: "Role duration in seconds (default: 6 hours)"
2733
required: false
2834
role-session-name:
2935
description: 'Role session name (default: GitHubActions)'
3036
required: false
37+
role-external-id:
38+
description: 'The external ID of the role to assume'
39+
required: false
3140
outputs:
3241
aws-account-id:
3342
description: 'The AWS account ID for the provided credentials'

index.js

+21-4
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,16 @@ async function assumeRole(params) {
1515
// Assume a role to get short-lived credentials using longer-lived credentials.
1616
const isDefined = i => !!i;
1717

18-
const {roleToAssume, roleDurationSeconds, roleSessionName, accessKeyId, secretAccessKey, sessionToken, region} = params;
18+
const {
19+
roleToAssume,
20+
roleExternalId,
21+
roleDurationSeconds,
22+
roleSessionName,
23+
accessKeyId,
24+
secretAccessKey,
25+
sessionToken,
26+
region,
27+
} = params;
1928
assert(
2029
[roleToAssume, roleDurationSeconds, roleSessionName, accessKeyId, secretAccessKey, region].every(isDefined),
2130
"Missing required input when assuming a Role."
@@ -32,7 +41,8 @@ async function assumeRole(params) {
3241
const sts = new aws.STS({
3342
accessKeyId, secretAccessKey, sessionToken, region, endpoint, customUserAgent: USER_AGENT
3443
});
35-
return sts.assumeRole({
44+
45+
const assumeRoleRequest = {
3646
RoleArn: roleToAssume,
3747
RoleSessionName: roleSessionName,
3848
DurationSeconds: roleDurationSeconds,
@@ -45,7 +55,13 @@ async function assumeRole(params) {
4555
{Key: 'Branch', Value: GITHUB_REF},
4656
{Key: 'Commit', Value: GITHUB_SHA},
4757
]
48-
})
58+
};
59+
60+
if (roleExternalId) {
61+
assumeRoleRequest.ExternalId = roleExternalId;
62+
}
63+
64+
return sts.assumeRole(assumeRoleRequest)
4965
.promise()
5066
.then(function (data) {
5167
return {
@@ -121,13 +137,14 @@ async function run() {
121137
const sessionToken = core.getInput('aws-session-token', { required: false });
122138
const maskAccountId = core.getInput('mask-aws-account-id', { required: false });
123139
const roleToAssume = core.getInput('role-to-assume', {required: false});
140+
const roleExternalId = core.getInput('role-external-id', { required: false });
124141
const roleDurationSeconds = core.getInput('role-duration-seconds', {required: false}) || MAX_ACTION_RUNTIME;
125142
const roleSessionName = core.getInput('role-session-name', { required: false }) || ROLE_SESSION_NAME;
126143

127144
// Get role credentials if configured to do so
128145
if (roleToAssume) {
129146
const roleCredentials = await assumeRole(
130-
{accessKeyId, secretAccessKey, sessionToken, region, roleToAssume, roleDurationSeconds, roleSessionName}
147+
{accessKeyId, secretAccessKey, sessionToken, region, roleToAssume, roleExternalId, roleDurationSeconds, roleSessionName}
131148
);
132149
exportCredentials(roleCredentials);
133150
} else {

index.test.js

+24-1
Original file line numberDiff line numberDiff line change
@@ -260,11 +260,34 @@ describe('Configure AWS Credentials', () => {
260260
})
261261
});
262262

263+
test('role external ID provided', async () => {
264+
core.getInput = jest
265+
.fn()
266+
.mockImplementation(mockGetInput({...ASSUME_ROLE_INPUTS, 'role-external-id': 'abcdef'}));
267+
268+
await run();
269+
expect(mockStsAssumeRole).toHaveBeenCalledWith({
270+
RoleArn: ROLE_NAME,
271+
RoleSessionName: 'GitHubActions',
272+
DurationSeconds: 6 * 3600,
273+
Tags: [
274+
{Key: 'GitHub', Value: 'Actions'},
275+
{Key: 'Repository', Value: ENVIRONMENT_VARIABLE_OVERRIDES.GITHUB_REPOSITORY},
276+
{Key: 'Workflow', Value: ENVIRONMENT_VARIABLE_OVERRIDES.GITHUB_WORKFLOW},
277+
{Key: 'Action', Value: ENVIRONMENT_VARIABLE_OVERRIDES.GITHUB_ACTION},
278+
{Key: 'Actor', Value: GITHUB_ACTOR_SANITIZED},
279+
{Key: 'Branch', Value: ENVIRONMENT_VARIABLE_OVERRIDES.GITHUB_REF},
280+
{Key: 'Commit', Value: ENVIRONMENT_VARIABLE_OVERRIDES.GITHUB_SHA},
281+
],
282+
ExternalId: 'abcdef'
283+
})
284+
});
285+
263286
test('workflow name sanitized in role assumption tags', async () => {
264287
core.getInput = jest
265288
.fn()
266289
.mockImplementation(mockGetInput(ASSUME_ROLE_INPUTS));
267-
290+
268291
process.env = {...process.env, GITHUB_WORKFLOW: 'Workflow!"#$%&\'()*+, -./:;<=>?@[]^_`{|}~🙂💥🍌1yFvMOeD3ZHYsHrGjCceOboMYzBPo0CRNFdcsVRG6UgR3A912a8KfcBtEVvkAS7kRBq80umGff8mux5IN1y55HQWPNBNyaruuVr4islFXte4FDQZexGJRUSMyHQpxJ8OmZnET84oDmbvmIjgxI6IBrdihX9PHMapT4gQvRYnLqNiKb18rEMWDNoZRy51UPX5sWK2GKPipgKSO9kqLckZai9D2AN2RlWCxtMqChNtxuxjqeqhoQZo0oaq39sjcRZgAAAAAAA'};
269292

270293
const sanitizedWorkflowName = 'Workflow__________+, -./:;<=>?@____________1yFvMOeD3ZHYsHrGjCceOboMYzBPo0CRNFdcsVRG6UgR3A912a8KfcBtEVvkAS7kRBq80umGff8mux5IN1y55HQWPNBNyaruuVr4islFXte4FDQZexGJRUSMyHQpxJ8OmZnET84oDmbvmIjgxI6IBrdihX9PHMapT4gQvRYnLqNiKb18rEMWDNoZRy51UPX5sWK2GKPipgKSO9kqLckZa'

0 commit comments

Comments
 (0)