Repository: aws-actions/configure-aws-credentials
Pull request #29 (2020-02-28 21:40:11)

feat: Have an ability to configure session name
Hi, 

I submit this PR for an ability to configure role session name after assuming a role with `configure-aws-credentials` Actions.

The story behind this PR is, recently, AWS has released a new feature for IAM - "CalledVia." It's a cool feature and no need to give extra write permissions to people to create stacks with CloudFormation. Now, we are changing our way of working by integrating GitHub Actions to deploy CloudFormation templates, but also want to know who update templates by using AWS CloudTrail. 

We figure out the CloudTrail will take session name as user name. Therefore we want to have a configurable session name to tag meaningful information on it instead of GitHubActions.


Patch:
diff --git a/README.md b/README.md
index de70f3f..7b0361a 100644
--- a/README.md
+++ b/README.md
@@ -53,7 +53,8 @@ We recommend following [Amazon IAM best practices](https://docs.aws.amazon.com/I
 ## Assuming a role
 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`.
 The role credentials will then be output instead of the ones you have provided.
-The default session duration is 6 hours, but if you would like to adjust this you can pass a duration to `role-duration-seconds`.
+The default session duration is 6 hours, but if you would like to adjust this you can pass a duration to `role-duration-seconds`. 
+The default session name is GitHubActions, and you can modify it by specifying the desired name in `role-session-name`.
 
 Example:
 ```yaml
@@ -65,6 +66,7 @@ Example:
         aws-region: us-east-2
         role-to-assume: arn:aws:iam::123456789100:role/role-to-assume
         role-duration-seconds: 1200
+        role-session-name: MySessionName
 ```
 
 ### Session tagging
diff --git a/action.yml b/action.yml
index a3bf25b..b3dd470 100644
--- a/action.yml
+++ b/action.yml
@@ -25,6 +25,9 @@ inputs:
   role-duration-seconds:
     description: "Role duration in seconds (default: 6 hours)"
     required: false
+  role-session-name:
+    description: 'Role session name (default: GitHubActions)'
+    required: false
 outputs:
   aws-account-id:
     description: 'The AWS account ID for the provided credentials'
diff --git a/dist/index.js b/dist/index.js
index 61be0ff..5b31793 100644
--- a/dist/index.js
+++ b/dist/index.js
@@ -135,15 +135,16 @@ const util = __webpack_require__(1669);
 const MAX_ACTION_RUNTIME = 6 * 3600;
 const USER_AGENT = 'configure-aws-credentials-for-github-actions';
 const MAX_TAG_VALUE_LENGTH = 256;
-const SANITIZATION_CHARACTER = '_'
+const SANITIZATION_CHARACTER = '_';
+const ROLE_SESSION_NAME = 'GitHubActions';
 
 async function assumeRole(params) {
   // Assume a role to get short-lived credentials using longer-lived credentials.
   const isDefined = i => !!i;
 
-  const {roleToAssume, roleDurationSeconds, accessKeyId, secretAccessKey, sessionToken, region} = params;
+  const {roleToAssume, roleDurationSeconds, roleSessionName, accessKeyId, secretAccessKey, sessionToken, region} = params;
   assert(
-      [roleToAssume, roleDurationSeconds, accessKeyId, secretAccessKey, region].every(isDefined),
+      [roleToAssume, roleDurationSeconds, roleSessionName, accessKeyId, secretAccessKey, region].every(isDefined),
       "Missing required input when assuming a Role."
   );
 
@@ -160,7 +161,7 @@ async function assumeRole(params) {
   });
   return sts.assumeRole({
     RoleArn: roleToAssume,
-    RoleSessionName: 'GitHubActions',
+    RoleSessionName: roleSessionName,
     DurationSeconds: roleDurationSeconds,
     Tags: [
       {Key: 'GitHub', Value: 'Actions'},
@@ -248,11 +249,12 @@ async function run() {
     const maskAccountId = core.getInput('mask-aws-account-id', { required: false });
     const roleToAssume = core.getInput('role-to-assume', {required: false});
     const roleDurationSeconds = core.getInput('role-duration-seconds', {required: false}) || MAX_ACTION_RUNTIME;
+    const roleSessionName = core.getInput('role-session-name', { required: false }) || ROLE_SESSION_NAME;
 
     // Get role credentials if configured to do so
     if (roleToAssume) {
       const roleCredentials = await assumeRole(
-          {accessKeyId, secretAccessKey, sessionToken, region, roleToAssume, roleDurationSeconds}
+          {accessKeyId, secretAccessKey, sessionToken, region, roleToAssume, roleDurationSeconds, roleSessionName}
       );
       exportCredentials(roleCredentials);
     } else {
diff --git a/index.js b/index.js
index a6dd5e2..d525bce 100644
--- a/index.js
+++ b/index.js
@@ -8,15 +8,16 @@ const util = require('util');
 const MAX_ACTION_RUNTIME = 6 * 3600;
 const USER_AGENT = 'configure-aws-credentials-for-github-actions';
 const MAX_TAG_VALUE_LENGTH = 256;
-const SANITIZATION_CHARACTER = '_'
+const SANITIZATION_CHARACTER = '_';
+const ROLE_SESSION_NAME = 'GitHubActions';
 
 async function assumeRole(params) {
   // Assume a role to get short-lived credentials using longer-lived credentials.
   const isDefined = i => !!i;
 
-  const {roleToAssume, roleDurationSeconds, accessKeyId, secretAccessKey, sessionToken, region} = params;
+  const {roleToAssume, roleDurationSeconds, roleSessionName, accessKeyId, secretAccessKey, sessionToken, region} = params;
   assert(
-      [roleToAssume, roleDurationSeconds, accessKeyId, secretAccessKey, region].every(isDefined),
+      [roleToAssume, roleDurationSeconds, roleSessionName, accessKeyId, secretAccessKey, region].every(isDefined),
       "Missing required input when assuming a Role."
   );
 
@@ -33,7 +34,7 @@ async function assumeRole(params) {
   });
   return sts.assumeRole({
     RoleArn: roleToAssume,
-    RoleSessionName: 'GitHubActions',
+    RoleSessionName: roleSessionName,
     DurationSeconds: roleDurationSeconds,
     Tags: [
       {Key: 'GitHub', Value: 'Actions'},
@@ -121,11 +122,12 @@ async function run() {
     const maskAccountId = core.getInput('mask-aws-account-id', { required: false });
     const roleToAssume = core.getInput('role-to-assume', {required: false});
     const roleDurationSeconds = core.getInput('role-duration-seconds', {required: false}) || MAX_ACTION_RUNTIME;
+    const roleSessionName = core.getInput('role-session-name', { required: false }) || ROLE_SESSION_NAME;
 
     // Get role credentials if configured to do so
     if (roleToAssume) {
       const roleCredentials = await assumeRole(
-          {accessKeyId, secretAccessKey, sessionToken, region, roleToAssume, roleDurationSeconds}
+          {accessKeyId, secretAccessKey, sessionToken, region, roleToAssume, roleDurationSeconds, roleSessionName}
       );
       exportCredentials(roleCredentials);
     } else {


Test patch:
diff --git a/index.test.js b/index.test.js
index 4fd6747..151c179 100644
--- a/index.test.js
+++ b/index.test.js
@@ -238,6 +238,28 @@ describe('Configure AWS Credentials', () => {
         })
     });
 
+    test('role assumption session name provided', async () => {
+        core.getInput = jest
+            .fn()
+            .mockImplementation(mockGetInput({...ASSUME_ROLE_INPUTS, 'role-session-name': 'MySessionName'}));
+
+        await run();
+        expect(mockStsAssumeRole).toHaveBeenCalledWith({
+            RoleArn: ROLE_NAME,
+            RoleSessionName: 'MySessionName',
+            DurationSeconds: 6 * 3600,
+            Tags: [
+                {Key: 'GitHub', Value: 'Actions'},
+                {Key: 'Repository', Value: ENVIRONMENT_VARIABLE_OVERRIDES.GITHUB_REPOSITORY},
+                {Key: 'Workflow', Value: ENVIRONMENT_VARIABLE_OVERRIDES.GITHUB_WORKFLOW},
+                {Key: 'Action', Value: ENVIRONMENT_VARIABLE_OVERRIDES.GITHUB_ACTION},
+                {Key: 'Actor', Value: GITHUB_ACTOR_SANITIZED},
+                {Key: 'Branch', Value: ENVIRONMENT_VARIABLE_OVERRIDES.GITHUB_REF},
+                {Key: 'Commit', Value: ENVIRONMENT_VARIABLE_OVERRIDES.GITHUB_SHA},
+            ]
+        })
+    });
+
     test('workflow name sanitized in role assumption tags', async () => {
         core.getInput = jest
             .fn()