Skip to content

Commit eb70354

Browse files
lehmanmjkellertk
andauthoredFeb 8, 2025··
feat: idempotent fetch (#1289)
* Add functionality to re-use existing credentials * Finish adding use-existing-credentials logic * Add testing for use-existing-credentials * Update README * feat: finalize use-exisiting-credentials feature --------- Co-authored-by: Tom Keller <kellertk@amazon.com>
1 parent 3478c15 commit eb70354

File tree

6 files changed

+47
-0
lines changed

6 files changed

+47
-0
lines changed
 

‎README.md

+1
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ See [action.yml](./action.yml) for more detail.
116116
| disable-retry | Disabled retry/backoff logic for assume role calls. By default, retries are enabled. | No |
117117
| retry-max-attempts | Limits the number of retry attempts before giving up. Defaults to 12. | No |
118118
| special-characters-workaround | Uncommonly, some environments cannot tolerate special characters in a secret key. This option will retry fetching credentials until the secret access key does not contain special characters. This option overrides disable-retry and retry-max-attempts. | No |
119+
| use-existing-credentials | When set, the action will check if existing credentials are valid and exit if they are. Defaults to false. | No |
119120

120121
#### Credential Lifetime
121122
The default session duration is **1 hour**.

‎action.yml

+2
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ inputs:
7373
special-characters-workaround:
7474
description: Some environments do not support special characters in AWS_SECRET_ACCESS_KEY. This option will retry fetching credentials until the secret access key does not contain special characters. This option overrides disable-retry and retry-max-attempts. This option is disabled by default
7575
required: false
76+
use-existing-credentials:
77+
description: When enabled, this option will check if there are already valid credentials in the environment. If there are, new credentials will not be fetched. If there are not, the action will run as normal.
7678
outputs:
7779
aws-account-id:
7880
description: The AWS account ID for the provided credentials

‎src/helpers.ts

+13
Original file line numberDiff line numberDiff line change
@@ -144,3 +144,16 @@ export function isDefined<T>(i: T | undefined | null): i is T {
144144
return i !== undefined && i !== null;
145145
}
146146
/* c8 ignore stop */
147+
148+
export async function areCredentialsValid(credentialsClient: CredentialsClient) {
149+
const client = credentialsClient.stsClient;
150+
try {
151+
const identity = await client.send(new GetCallerIdentityCommand({}));
152+
if (identity.Account) {
153+
return true;
154+
}
155+
return false;
156+
} catch (_) {
157+
return false;
158+
}
159+
}

‎src/index.ts

+13
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import type { AssumeRoleCommandOutput } from '@aws-sdk/client-sts';
33
import { CredentialsClient } from './CredentialsClient';
44
import { assumeRole } from './assumeRole';
55
import {
6+
areCredentialsValid,
67
errorMessage,
78
exportAccountId,
89
exportCredentials,
@@ -60,6 +61,8 @@ export async function run() {
6061
const specialCharacterWorkaroundInput =
6162
core.getInput('special-characters-workaround', { required: false }) || 'false';
6263
const specialCharacterWorkaround = specialCharacterWorkaroundInput.toLowerCase() === 'true';
64+
const useExistingCredentialsInput = core.getInput('use-existing-credentials', { required: false }) || 'false';
65+
const useExistingCredentials = useExistingCredentialsInput.toLowerCase() === 'true';
6366
let maxRetries = Number.parseInt(core.getInput('retry-max-attempts', { required: false })) || 12;
6467
switch (true) {
6568
case specialCharacterWorkaround:
@@ -116,6 +119,16 @@ export async function run() {
116119
let sourceAccountId: string;
117120
let webIdentityToken: string;
118121

122+
//if the user wants to attempt to use existing credentials, check if we have some already
123+
if (useExistingCredentials) {
124+
const validCredentials = await areCredentialsValid(credentialsClient);
125+
if (validCredentials) {
126+
core.notice('Pre-existing credentials are valid. No need to generate new ones.');
127+
return;
128+
}
129+
core.notice('No valid credentials exist. Running as normal.');
130+
}
131+
119132
// If OIDC is being used, generate token
120133
// Else, export credentials provided as input
121134
if (useGitHubOIDCProvider()) {

‎test/index.test.ts

+13
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ describe('Configure AWS Credentials', {}, () => {
2727
vi.spyOn(core, 'setOutput').mockImplementation((_n, _v) => {});
2828
vi.spyOn(core, 'debug').mockImplementation((_m) => {});
2929
vi.spyOn(core, 'info').mockImplementation((_m) => {});
30+
vi.spyOn(core, 'notice').mockImplementation((_m) => {});
3031
// Remove any existing environment variables before each test to prevent the
3132
// SDK from picking them up
3233
process.env = { ...mocks.envs };
@@ -299,5 +300,17 @@ describe('Configure AWS Credentials', {}, () => {
299300
await run();
300301
expect(core.setFailed).toHaveBeenCalled();
301302
});
303+
it('gets new creds if told to reuse existing but they\'re invalid', {}, async () => {
304+
vi.spyOn(core, 'getInput').mockImplementation(mocks.getInput(mocks.USE_EXISTING_CREDENTIALS_INPUTS));
305+
mockedSTSClient.on(GetCallerIdentityCommand).rejects();
306+
await run();
307+
expect(core.notice).toHaveBeenCalledWith('No valid credentials exist. Running as normal.')
308+
});
309+
it('doesn\'t get new creds if there are already valid ones and we said use them', {}, async () => {
310+
vi.spyOn(core, 'getInput').mockImplementation(mocks.getInput(mocks.USE_EXISTING_CREDENTIALS_INPUTS));
311+
mockedSTSClient.on(GetCallerIdentityCommand).resolves(mocks.outputs.GET_CALLER_IDENTITY);
312+
await run();
313+
expect(core.setFailed).not.toHaveBeenCalled();
314+
})
302315
});
303316
});

‎test/mockinputs.test.ts

+5
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@ const inputs = {
2727
'role-chaining': 'true',
2828
'aws-region': 'fake-region-1',
2929
},
30+
USE_EXISTING_CREDENTIALS_INPUTS: {
31+
'aws-region': 'fake-region-1',
32+
'use-existing-credentials': 'true',
33+
'role-to-assume': 'arn:aws:iam::111111111111:role/MY-ROLE',
34+
}
3035
};
3136

3237
const envs = {

0 commit comments

Comments
 (0)
Please sign in to comment.