From 560bddf06cd5c1d42b89a710c5242b0bfb1ef946 Mon Sep 17 00:00:00 2001 From: Sylvain Verly Date: Wed, 19 Mar 2025 18:30:06 +0100 Subject: [PATCH] Add possibility to input custom session tags --- .gitignore | 1 + src/assumeRole.ts | 3 +++ src/index.ts | 8 ++++++++ test/index.test.ts | 37 +++++++++++++++++++++++++++++++++++++ test/mockinputs.test.ts | 14 ++++++++++++++ 5 files changed, 63 insertions(+) diff --git a/.gitignore b/.gitignore index bc18168..6841d21 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +.history node_modules coverage .DS_Store diff --git a/src/assumeRole.ts b/src/assumeRole.ts index 879d81c..f4296c3 100644 --- a/src/assumeRole.ts +++ b/src/assumeRole.ts @@ -78,6 +78,7 @@ export interface assumeRoleParams { webIdentityToken?: string; inlineSessionPolicy?: string; managedSessionPolicies?: { arn: string }[]; + customTags?: { Key: string; Value: string }[]; } export async function assumeRole(params: assumeRoleParams) { @@ -94,6 +95,7 @@ export async function assumeRole(params: assumeRoleParams) { webIdentityToken, inlineSessionPolicy, managedSessionPolicies, + customTags, } = { ...params }; // Load GitHub environment variables @@ -110,6 +112,7 @@ export async function assumeRole(params: assumeRoleParams) { { Key: 'Action', Value: GITHUB_ACTION }, { Key: 'Actor', Value: sanitizeGitHubVariables(GITHUB_ACTOR) }, { Key: 'Commit', Value: GITHUB_SHA }, + ...(customTags || []), ]; if (process.env.GITHUB_REF) { tagArray.push({ diff --git a/src/index.ts b/src/index.ts index 5ea2a32..7f10f4d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -43,6 +43,13 @@ export async function run() { const roleSkipSessionTagging = getBooleanInput('role-skip-session-tagging', { required: false }); const transitiveTagKeys = core.getMultilineInput('transitive-tag-keys', { required: false }); const proxyServer = core.getInput('http-proxy', { required: false }) || process.env.HTTP_PROXY; + const customTagsInput = core.getInput('custom-tags', { required: false }); + const customTags = customTagsInput + ? (typeof customTagsInput === 'string' && customTagsInput.trim().startsWith('{') + ? Object.entries(JSON.parse(customTagsInput)) + : Object.entries(customTagsInput) + ).map(([Key, Value]) => ({ Key, Value: String(Value) })) + : []; const inlineSessionPolicy = core.getInput('inline-session-policy', { required: false }); const managedSessionPolicies = core.getMultilineInput('managed-session-policies', { required: false }).map((p) => { return { arn: p }; @@ -209,6 +216,7 @@ export async function run() { webIdentityToken, inlineSessionPolicy, managedSessionPolicies, + customTags, }); }, !disableRetry, diff --git a/test/index.test.ts b/test/index.test.ts index f27ee13..6b03a4c 100644 --- a/test/index.test.ts +++ b/test/index.test.ts @@ -238,6 +238,43 @@ describe('Configure AWS Credentials', {}, () => { }); }); + describe('Custom Tags', {}, () => { + beforeEach(() => { + mockedSTSClient.on(AssumeRoleCommand).resolvesOnce(mocks.outputs.STS_CREDENTIALS); + mockedSTSClient.on(GetCallerIdentityCommand).resolves({ ...mocks.outputs.GET_CALLER_IDENTITY }); + // biome-ignore lint/suspicious/noExplicitAny: any required to mock private method + vi.spyOn(CredentialsClient.prototype as any, 'loadCredentials') + .mockResolvedValueOnce({ accessKeyId: 'MYAWSACCESSKEYID' }) + .mockResolvedValueOnce({ accessKeyId: 'STSAWSACCESSKEYID' }); + }); + + it('handles JSON string custom tags', async () => { + vi.spyOn(core, 'getInput').mockImplementation(mocks.getInput(mocks.CUSTOM_TAGS_JSON_INPUTS)); + await run(); + expect(core.info).toHaveBeenCalledWith('Assuming role with user credentials'); + expect(core.info).toHaveBeenCalledWith('Authenticated as assumedRoleId AROAFAKEASSUMEDROLEID'); + expect(mockedSTSClient.commandCalls(AssumeRoleCommand)[0].args[0].input).toMatchObject({ + Tags: expect.arrayContaining([ + { Key: 'Environment', Value: 'Production' }, + { Key: 'Team', Value: 'DevOps' } + ]) + }); + }); + + it('handles object custom tags', async () => { + vi.spyOn(core, 'getInput').mockImplementation(mocks.getInput(mocks.CUSTOM_TAGS_OBJECT_INPUTS)); + await run(); + expect(core.info).toHaveBeenCalledWith('Assuming role with user credentials'); + expect(core.info).toHaveBeenCalledWith('Authenticated as assumedRoleId AROAFAKEASSUMEDROLEID'); + expect(mockedSTSClient.commandCalls(AssumeRoleCommand)[0].args[0].input).toMatchObject({ + Tags: expect.arrayContaining([ + { Key: 'Environment', Value: 'Production' }, + { Key: 'Team', Value: 'DevOps' } + ]) + }); + }); + }); + describe('Odd inputs', {}, () => { it('fails when github env vars are missing', {}, async () => { vi.mocked(core.getInput).mockImplementation(mocks.getInput(mocks.IAM_USER_INPUTS)); diff --git a/test/mockinputs.test.ts b/test/mockinputs.test.ts index 1caa1fe..f651995 100644 --- a/test/mockinputs.test.ts +++ b/test/mockinputs.test.ts @@ -6,6 +6,20 @@ const inputs = { 'aws-region': 'fake-region-1', 'special-characters-workaround': 'true', }, + CUSTOM_TAGS_JSON_INPUTS: { + 'aws-access-key-id': 'MYAWSACCESSKEYID', + 'aws-secret-access-key': 'MYAWSSECRETACCESSKEY', + 'role-to-assume': 'arn:aws:iam::111111111111:role/MY-ROLE', + 'aws-region': 'fake-region-1', + 'custom-tags': '{"Environment": "Production", "Team": "DevOps"}', + }, + CUSTOM_TAGS_OBJECT_INPUTS: { + 'aws-access-key-id': 'MYAWSACCESSKEYID', + 'aws-secret-access-key': 'MYAWSSECRETACCESSKEY', + 'role-to-assume': 'arn:aws:iam::111111111111:role/MY-ROLE', + 'aws-region': 'fake-region-1', + 'custom-tags': { Environment: 'Production', Team: 'DevOps' }, + }, IAM_USER_INPUTS: { 'aws-access-key-id': 'MYAWSACCESSKEYID', 'aws-secret-access-key': 'MYAWSSECRETACCESSKEY',