From 0f41e6a7bcb6095ab7998d6820f579ee01fc7848 Mon Sep 17 00:00:00 2001 From: DJ Lee Date: Wed, 11 Oct 2023 18:06:31 -0400 Subject: [PATCH] conditionally create instance profile and attach role to it --- copy-role.js | 53 +++++++++++++++++++++++++- package-lock.json | 94 ++++++++++++++++++++++++++++++++++------------- 2 files changed, 120 insertions(+), 27 deletions(-) diff --git a/copy-role.js b/copy-role.js index be76b63..1735ca2 100644 --- a/copy-role.js +++ b/copy-role.js @@ -10,7 +10,8 @@ let iam const sourceRole = await fetchRole(sourceRoleName) const inlinePolicies = await fetchInlinePolicies(sourceRoleName) const managedPolicies = await fetchManagedPolicies(sourceRoleName) - + const instanceProfiles = await fetchInstanceProfiles(sourceRoleName); + await createRoleFromExisting(sourceRole, targetRoleName) if (inlinePolicies.length > 0) { @@ -21,6 +22,12 @@ let iam await addManagedPolicies(targetRoleName, managedPolicies) } + if (instanceProfiles.length === 1 && instanceProfiles[0].InstanceProfileName === sourceRoleName) { + // The source role had a default instance profile with the same name as the role, + // so let's also create the equivalent for the new role. + await createMatchingInstanceProfile(targetRoleName); + } + log('\nDone!') } catch (e) { error(e.message) @@ -139,6 +146,21 @@ async function fetchManagedPolicies(roleName) { } } +async function fetchInstanceProfiles(roleName) { + log('\n--> Fetching source instance profiles...') + + let role + try { + role = (await getIam().listInstanceProfilesForRole({RoleName: roleName}).promise()).InstanceProfiles + } catch (e) { + throw new Error(`<-- Failed to fetch source instance profiles: "${e.message}"`) + } + + log('<-- Source instance profiles loaded.') + + return role +} + async function createRoleFromExisting(sourceRole, targetRoleName) { log(`\n--> Creating a new role ${targetRoleName}...`) @@ -197,6 +219,35 @@ async function addManagedPolicies(targetRoleName, policies) { log(`<-- Added ${policies.length} managed policies.`) } +async function createMatchingInstanceProfile(targetRoleName) { + log(`\n--> Creating a new instance profile ${targetRoleName}...`) + + let targetInstanceProfile + try { + targetInstanceProfile = (await getIam().createInstanceProfile({ + InstanceProfileName: targetRoleName, + }).promise()).InstanceProfile + } catch (e) { + throw new Error(`<-- Failed to create target instance profile: "${e.message}"`) + } + + log(`<-- Created instance profile ${targetRoleName}.`) + + try { + await getIam().addRoleToInstanceProfile({ + InstanceProfileName: targetRoleName, + RoleName: targetRoleName, + }).promise() + } catch (e) { + throw new Error(`<-- Failed to attach target role to instance profile: "${e.message}"`) + } + + log(`<-- Attached role ${targetRoleName} to instance profile.`) + + return targetInstanceProfile +} + + function getIam() { if (!iam) { iam = new AWS.IAM() diff --git a/package-lock.json b/package-lock.json index 91f835d..e77d946 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,14 +1,22 @@ { "name": "aws-iam-copy-role", "version": "1.0.0", - "lockfileVersion": 1, + "lockfileVersion": 3, "requires": true, - "dependencies": { - "aws-sdk": { + "packages": { + "": { + "name": "aws-iam-copy-role", + "version": "1.0.0", + "license": "MIT", + "dependencies": { + "aws-sdk": "^2.863.0" + } + }, + "node_modules/aws-sdk": { "version": "2.863.0", "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.863.0.tgz", "integrity": "sha512-krr0047EOl9qpRdhPoyYxI7+viVUpX+t+Vjbf+alXdOE172DC+hFi8y6egIM1xVV4KkMFm0y0EykBWgA93XNNA==", - "requires": { + "dependencies": { "buffer": "4.9.2", "events": "1.1.1", "ieee754": "1.1.13", @@ -18,85 +26,119 @@ "url": "0.10.3", "uuid": "3.3.2", "xml2js": "0.4.19" + }, + "engines": { + "node": ">= 0.8.0" } }, - "base64-js": { + "node_modules/base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] }, - "buffer": { + "node_modules/buffer": { "version": "4.9.2", "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", - "requires": { + "dependencies": { "base64-js": "^1.0.2", "ieee754": "^1.1.4", "isarray": "^1.0.0" } }, - "events": { + "node_modules/events": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", - "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=" + "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=", + "engines": { + "node": ">=0.4.x" + } }, - "ieee754": { + "node_modules/ieee754": { "version": "1.1.13", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" }, - "isarray": { + "node_modules/isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" }, - "jmespath": { + "node_modules/jmespath": { "version": "0.15.0", "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.15.0.tgz", - "integrity": "sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc=" + "integrity": "sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc=", + "engines": { + "node": ">= 0.6.0" + } }, - "punycode": { + "node_modules/punycode": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" }, - "querystring": { + "node_modules/querystring": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=" + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", + "deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.", + "engines": { + "node": ">=0.4.x" + } }, - "sax": { + "node_modules/sax": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", "integrity": "sha1-e45lYZCyKOgaZq6nSEgNgozS03o=" }, - "url": { + "node_modules/url": { "version": "0.10.3", "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", "integrity": "sha1-Ah5NnHcF8hu/N9A861h2dAJ3TGQ=", - "requires": { + "dependencies": { "punycode": "1.3.2", "querystring": "0.2.0" } }, - "uuid": { + "node_modules/uuid": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", - "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "bin": { + "uuid": "bin/uuid" + } }, - "xml2js": { + "node_modules/xml2js": { "version": "0.4.19", "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz", "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==", - "requires": { + "dependencies": { "sax": ">=0.6.0", "xmlbuilder": "~9.0.1" } }, - "xmlbuilder": { + "node_modules/xmlbuilder": { "version": "9.0.7", "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", - "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=" + "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=", + "engines": { + "node": ">=4.0" + } } } }