UNPKG

@cloud-copilot/iam-collect

Version:

Collect IAM information from AWS Accounts

296 lines 11.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.AuthorizationDetailsSync = void 0; exports.getAuthorizationDetails = getAuthorizationDetails; const client_iam_1 = require("@aws-sdk/client-iam"); const client_tools_js_1 = require("../../utils/client-tools.js"); const log_1 = require("@cloud-copilot/log"); const tags_js_1 = require("../../utils/tags.js"); const sync_js_1 = require("../sync.js"); exports.AuthorizationDetailsSync = { awsService: 'iam', name: 'authorizationDetails', global: true, execute: async (accountId, region, credentials, storage, endpoint, syncOptions) => { const client = syncOptions.clientPool.client(client_iam_1.IAMClient, credentials, region, endpoint); const authDetails = await getAuthorizationDetails(client, syncOptions.workerPool, syncOptions.customConfig); const roles = authDetails.roles || []; const roleData = roles.map((role) => { return { arn: role.Arn, 'managed-policies': role.AttachedManagedPolicies?.map((p) => p.PolicyArn), 'trust-policy': role.AssumeRolePolicyDocument, 'instance-profiles': role.InstanceProfileList?.map((i) => i.Arn), 'inline-policies': role.RolePolicyList, tags: (0, tags_js_1.convertTagsToRecord)(role.Tags), metadata: { arn: role.Arn, name: role.RoleName, id: role.RoleId, path: role.Path, created: role.CreateDate, permissionBoundary: role.PermissionsBoundary?.PermissionsBoundaryArn } }; }); await (0, sync_js_1.syncData)(roleData, storage, accountId, { service: 'iam', resourceType: 'role', account: accountId }, syncOptions.writeOnly); const groupArns = {}; const groupData = authDetails.groups.map((group) => { groupArns[group.GroupName] = group.Arn; return { arn: group.Arn, 'inline-policies': group.GroupPolicyList, 'managed-policies': group.AttachedManagedPolicies?.map((p) => p.PolicyArn), metadata: { arn: group.Arn, path: group.Path, name: group.GroupName, id: group.GroupId, created: group.CreateDate } }; }); await (0, sync_js_1.syncData)(groupData, storage, accountId, { service: 'iam', resourceType: 'group', account: accountId }, syncOptions.writeOnly); const customerPolicyData = authDetails.policies.map((policy) => { return { arn: policy.Arn, metadata: { arn: policy.Arn, name: policy.PolicyName, id: policy.PolicyId, description: policy.Description, defaultVersionId: policy.DefaultVersionId, path: policy.Path, permissionsBoundaryUsageCount: policy.PermissionsBoundaryUsageCount, isAttachable: policy.IsAttachable, createDate: policy.CreateDate, updateDate: policy.UpdateDate }, 'current-policy': policy.PolicyVersionList?.filter((version) => version.IsDefaultVersion).at(0)?.Document, policy: undefined, tags: (0, tags_js_1.convertTagsToRecord)(policy.Tags) }; }); await (0, sync_js_1.syncData)(customerPolicyData, storage, accountId, { service: 'iam', resourceType: 'policy', account: accountId }, syncOptions.writeOnly); const awsManagedPolicyData = authDetails.awsManagedPolicies.map((policy) => { return { arn: policy.Arn, metadata: { arn: policy.Arn, name: policy.PolicyName, id: policy.PolicyId, description: policy.Description, defaultVersionId: policy.DefaultVersionId, path: policy.Path, permissionsBoundaryUsageCount: policy.PermissionsBoundaryUsageCount, isAttachable: policy.IsAttachable, createDate: policy.CreateDate, updateDate: policy.UpdateDate }, 'current-policy': policy.PolicyVersionList?.filter((version) => version.IsDefaultVersion).at(0)?.Document, policy: undefined }; }); await (0, sync_js_1.syncData)(awsManagedPolicyData, storage, accountId, { service: 'iam', resourceType: 'policy', account: 'aws' }, syncOptions.writeOnly); const userData = authDetails.users.map((user) => { return { arn: user.Arn, 'managed-policies': user.AttachedManagedPolicies?.map((p) => p.PolicyArn), 'inline-policies': user.UserPolicyList, groups: user.GroupList?.map((g) => groupArns[g]), tags: (0, tags_js_1.convertTagsToRecord)(user.Tags), metadata: { arn: user.Arn, path: user.Path, permissionBoundary: user.PermissionsBoundary?.PermissionsBoundaryArn, id: user.UserId, name: user.UserName, created: user.CreateDate } }; }); (0, sync_js_1.syncData)(userData, storage, accountId, { service: 'iam', resourceType: 'user', account: accountId }, syncOptions.writeOnly); } }; /** * Return the results of the Authorization Details call for this account. * Excludes users and AWS managed policies. * * @param credentials The credentials to use for the API call * @returns Returns the results of the Authorization Details call for this account */ async function getAuthorizationDetails(client, concurrentWorkerPool, customConfig) { let isTruncated = false; let getDetailsCommand; let response; let marker; const groupDetails = []; const roleDetails = []; const policyDetails = []; const awsManagedPolicies = []; const userDetails = []; do { getDetailsCommand = new client_iam_1.GetAccountAuthorizationDetailsCommand({ Marker: marker, Filter: ['Role', 'Group', 'LocalManagedPolicy', 'AWSManagedPolicy', 'User'] }); response = await client.send(getDetailsCommand); groupDetails.push(...(response.GroupDetailList?.map(parseGroupDocs) || [])); roleDetails.push(...(response.RoleDetailList?.map(parseRoleDocs) || [])); userDetails.push(...(response.UserDetailList?.map(parseUserDocs) || [])); for (const policy of response.Policies || []) { const policyDetail = parsePolicyDocs(policy); if (policyDetail.Arn?.startsWith('arn:aws:iam::aws:policy/')) { awsManagedPolicies.push(policyDetail); } else { policyDetails.push(policyDetail); } } isTruncated = !!response.IsTruncated; marker = response.Marker; } while (isTruncated); let policiesWithTags = policyDetails; if (customConfig?.includePolicyTags) { log_1.log.info('Including policy tags in authorization details sync'); policiesWithTags = await getTagsForManagedPolicies(client, policyDetails, concurrentWorkerPool); } return { groups: groupDetails, roles: roleDetails, policies: policiesWithTags, awsManagedPolicies, users: userDetails }; } /** * Download the tags for the managed policies. * * @param client the IAM client to use for the API call * @param policies the policies to get the tags for * @returns the policies with the tags added */ async function getTagsForManagedPolicies(client, policies, concurrentWorkerPool) { const policiesWithTags = []; const execute = async (context) => { const { policy } = context.properties; const command = new client_iam_1.ListPolicyTagsCommand({ PolicyArn: policy.Arn }); const tags = await (0, client_tools_js_1.runAndCatch404)(async () => { const result = await client.send(command); return result.Tags; }); return { ...policy, Tags: tags }; }; const jobs = policies.map((policy) => ({ properties: { policy }, execute })); const jobResults = await Promise.all(concurrentWorkerPool.enqueueAll(jobs)); for (const jobResult of jobResults) { if (jobResult.status === 'fulfilled') { policiesWithTags.push(jobResult.value); } else { // Log the error but continue processing other policies const policyArn = jobResult.properties.policy.Arn; log_1.log.error('Failed to get tags for policy', jobResult.reason, { policyArn }); throw new Error(`Failed to get tags for policy ${policyArn}. See logs for details.`); } } return policiesWithTags; } /** * Decodes and parses the policy documents in the group. * * @param group The GroupDetail object to parse the policy documents for * @returns Returns the GroupDetail object with the policy documents decoded and parsed */ function parseGroupDocs(group) { if (group.GroupPolicyList) { group.GroupPolicyList.forEach((policy) => { if (policy.PolicyDocument) { policy.PolicyDocument = JSON.parse(decodeURIComponent(policy.PolicyDocument)); } }); } return group; } /** * Decodes and parses the policy documents in the role. * * @param role the RoleDetail object to parse the policy documents for * @returns Returns the RoleDetail object with the policy documents decoded and parsed */ function parseRoleDocs(role) { if (role.AssumeRolePolicyDocument) { role.AssumeRolePolicyDocument = JSON.parse(decodeURIComponent(role.AssumeRolePolicyDocument)); } if (role.RolePolicyList) { role.RolePolicyList.forEach((policy) => { if (policy.PolicyDocument) { policy.PolicyDocument = JSON.parse(decodeURIComponent(policy.PolicyDocument)); } }); } return role; } /** * Decodes and parses the policy documents in the managed policy. * * @param policy The ManagedPolicyDetail object to parse the policy documents for * @returns Returns the ManagedPolicyDetail object with the policy documents decoded and parsed */ function parsePolicyDocs(policy) { if (policy.PolicyVersionList) { policy.PolicyVersionList.forEach((version) => { if (version.Document) { version.Document = JSON.parse(decodeURIComponent(version.Document)); } }); } return policy; } /** * Decodes and parses the policy documents attached directly to a user. * * @param user The userDetail object to parse the policy documents for * @returns Returns the UserDetail object with the policy documents decoded and parsed */ function parseUserDocs(user) { if (user.UserPolicyList) { user.UserPolicyList.forEach((policy) => { if (policy.PolicyDocument) { policy.PolicyDocument = JSON.parse(decodeURIComponent(policy.PolicyDocument)); } }); } return user; } //# sourceMappingURL=authorizationDetails.js.map