UNPKG

@cloud-copilot/iam-collect

Version:

Collect IAM information from AWS Accounts

439 lines 18.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.AwsConfigIamClient = void 0; const client_iam_1 = require("@aws-sdk/client-iam"); const iam_utils_1 = require("@cloud-copilot/iam-utils"); const ClientPool_js_1 = require("../../aws/ClientPool.js"); const AbstractClient_js_1 = require("../../customClients/AbstractClient.js"); const AwsConfigClientContext_js_1 = require("../AwsConfigClientContext.js"); const awsConfigUtils_js_1 = require("../awsConfigUtils.js"); /** * AWS Config-based IAM client implementation */ class AwsConfigIamClient extends AbstractClient_js_1.AbstractClient { static clientName = client_iam_1.IAMClient.name; constructor(options, customContext) { super(options, customContext); } /** * Register all IAM command implementations */ registerCommands() { this.registerCommand(AwsConfigGetAccountAuthorizationDetailsCommand); this.registerCommand(AwsConfigGetOpenIDConnectProviderCommand); this.registerCommand(AwsConfigGetSAMLProviderCommand); this.registerCommand(AwsConfigListInstanceProfilesCommand); this.registerCommand(AwsConfigListOpenIDConnectProvidersCommand); this.registerCommand(AwsConfigListSAMLProvidersCommand); this.registerCommand(AwsConfigListPolicyTagsCommand); } } exports.AwsConfigIamClient = AwsConfigIamClient; /** * Config-based implementation of IAM GetAccountAuthorizationDetailsCommand * * Aggregates IAM data from multiple Config resource types: * - AWS::IAM::Role for roles * - AWS::IAM::User for users * - AWS::IAM::Group for groups * - AWS::IAM::Policy for managed policies */ const AwsConfigGetAccountAuthorizationDetailsCommand = (0, AwsConfigClientContext_js_1.awsConfigCommand)({ command: client_iam_1.GetAccountAuthorizationDetailsCommand, execute: async (input, context) => { const nativeIamClient = ClientPool_js_1.AwsClientPool.defaultInstance.client(client_iam_1.IAMClient, context.configCredentials, context.region, undefined); const awsManagedPoliciesToFetch = new Set(); // Query roles from Config const rolesQuery = ` SELECT configuration.arn, configuration.roleName, configuration.path, configuration.roleId, configuration.createDate, configuration.assumeRolePolicyDocument, configuration.attachedManagedPolicies, configuration.instanceProfileList, configuration.rolePolicyList, configuration.permissionsBoundary, configuration.roleLastUsed, tags WHERE resourceType = 'AWS::IAM::Role' AND accountId = '${context.accountId}' AND ${awsConfigUtils_js_1.resourceStatusWhereClause} `; // Query users from Config const usersQuery = ` SELECT configuration.arn, configuration.userName, configuration.path, configuration.userId, configuration.createDate, configuration.attachedManagedPolicies, configuration.groupList, configuration.userPolicyList, configuration.permissionsBoundary, tags WHERE resourceType = 'AWS::IAM::User' AND accountId = '${context.accountId}' AND ${awsConfigUtils_js_1.resourceStatusWhereClause} `; // Query groups from Config const groupsQuery = ` SELECT configuration.arn, configuration.groupName, configuration.path, configuration.groupId, configuration.createDate, configuration.attachedManagedPolicies, configuration.groupPolicyList, tags WHERE resourceType = 'AWS::IAM::Group' AND accountId = '${context.accountId}' AND ${awsConfigUtils_js_1.resourceStatusWhereClause} `; // Query policies from Config const policiesQuery = ` SELECT configuration.policyName, configuration.policyId, configuration.arn, configuration.path, configuration.createDate, configuration.updateDate, configuration.policyVersionList, configuration.attachmentCount, configuration.permissionsBoundaryUsageCount, configuration.isAttachable, configuration.description, tags WHERE resourceType = 'AWS::IAM::Policy' AND accountId = '${context.accountId}' AND ${awsConfigUtils_js_1.resourceStatusWhereClause} `; // Execute all queries const [rolesResults, usersResults, groupsResults, policiesResults] = await Promise.all([ (0, awsConfigUtils_js_1.executeConfigQuery)(rolesQuery, context), (0, awsConfigUtils_js_1.executeConfigQuery)(usersQuery, context), (0, awsConfigUtils_js_1.executeConfigQuery)(groupsQuery, context), (0, awsConfigUtils_js_1.executeConfigQuery)(policiesQuery, context) ]); // Transform Config data to IAM format and collect AWS managed policies const roles = rolesResults.map((resultString) => { const { configuration, tags } = (0, awsConfigUtils_js_1.parseConfigItem)(resultString); // Collect AWS managed policies from this role const attachedPolicies = configuration.attachedManagedPolicies || []; attachedPolicies.forEach((policy) => { if (policy.policyArn && isAwsManagedPolicy(policy.policyArn)) { awsManagedPoliciesToFetch.add(policy.policyArn); } }); return { Arn: configuration.arn, RoleName: configuration.roleName, Path: configuration.path, RoleId: configuration.roleId, CreateDate: configuration.createDate ? new Date(configuration.createDate) : undefined, AssumeRolePolicyDocument: configuration.assumeRolePolicyDocument, AttachedManagedPolicies: configuration.attachedManagedPolicies?.map((ap) => ({ PolicyName: ap.policyName, PolicyArn: ap.policyArn })), InstanceProfileList: configuration.instanceProfileList, RolePolicyList: configuration.rolePolicyList.map((rp) => ({ PolicyName: rp.policyName, PolicyDocument: rp.policyDocument })), Tags: tags, PermissionsBoundary: configuration.permissionsBoundary, RoleLastUsed: configuration.roleLastUsed }; }); const users = usersResults.map((resultString) => { const { configuration, tags } = (0, awsConfigUtils_js_1.parseConfigItem)(resultString); // Collect AWS managed policies from this user const attachedPolicies = configuration.attachedManagedPolicies || []; attachedPolicies.forEach((policy) => { if (policy.policyArn && isAwsManagedPolicy(policy.policyArn)) { awsManagedPoliciesToFetch.add(policy.policyArn); } }); return { Arn: configuration.arn, UserName: configuration.userName, Path: configuration.path, UserId: configuration.userId, CreateDate: configuration.createDate ? new Date(configuration.createDate) : undefined, AttachedManagedPolicies: configuration.attachedManagedPolicies.map((ap) => ({ PolicyName: ap.policyName, PolicyArn: ap.policyArn })), GroupList: configuration.groupList, UserPolicyList: configuration.userPolicyList.map((up) => ({ PolicyName: up.policyName, PolicyDocument: up.policyDocument })), Tags: tags, PermissionsBoundary: configuration.permissionsBoundary }; }); const groups = groupsResults.map((resultString) => { const { configuration, tags } = (0, awsConfigUtils_js_1.parseConfigItem)(resultString); // Collect AWS managed policies from this group const attachedPolicies = configuration.attachedManagedPolicies || []; attachedPolicies.forEach((policy) => { if (policy.policyArn && isAwsManagedPolicy(policy.policyArn)) { awsManagedPoliciesToFetch.add(policy.policyArn); } }); return { Arn: configuration.arn, GroupName: configuration.groupName, Path: configuration.path, GroupId: configuration.groupId, CreateDate: configuration.createDate ? new Date(configuration.createDate) : undefined, AttachedManagedPolicies: putInArray(configuration.attachedManagedPolicies).map((ap) => ({ PolicyName: ap.policyName, PolicyArn: ap.policyArn })), Tags: tags ? Object.entries(tags).map(([key, value]) => ({ Key: key, Value: value })) : [], GroupPolicyList: putInArray(configuration.groupPolicyList).map((gp) => ({ PolicyName: gp.policyName, PolicyDocument: gp.policyDocument })) }; }); const policies = policiesResults.map((resultString) => { const { configuration, tags } = (0, awsConfigUtils_js_1.parseConfigItem)(resultString); return { PolicyName: configuration.policyName, PolicyId: configuration.policyId, Arn: configuration.arn, Path: configuration.path, CreateDate: configuration.createDate ? new Date(configuration.createDate) : undefined, UpdateDate: configuration.updateDate ? new Date(configuration.updateDate) : undefined, PolicyVersionList: putInArray(configuration.policyVersionList).map((pv) => ({ Document: pv.document, VersionId: pv.versionId, IsDefaultVersion: pv.isDefaultVersion })), AttachmentCount: configuration.attachmentCount, PermissionsBoundaryUsageCount: configuration.permissionsBoundaryUsageCount, IsAttachable: configuration.isAttachable, Description: configuration.description, Tags: tags }; }); // Fetch AWS managed policy details using native IAM client const awsManagedPolicies = []; for (const policyArn of awsManagedPoliciesToFetch) { const policyDetails = await nativeIamClient.send(new client_iam_1.GetPolicyCommand({ PolicyArn: policyArn })); const policyDocument = await nativeIamClient.send(new client_iam_1.GetPolicyVersionCommand({ PolicyArn: policyArn, VersionId: policyDetails.Policy?.DefaultVersionId })); if (policyDocument.PolicyVersion) { awsManagedPolicies.push({ ...policyDetails.Policy, PolicyVersionList: [ { Document: policyDocument.PolicyVersion.Document, VersionId: policyDocument.PolicyVersion.VersionId, IsDefaultVersion: policyDocument.PolicyVersion.IsDefaultVersion } ] }); } } // Combine customer-managed policies from Config with AWS managed policies from native IAM const allPolicies = [...policies, ...awsManagedPolicies]; return { UserDetailList: users, GroupDetailList: groups, RoleDetailList: roles, Policies: allPolicies, IsTruncated: false }; } }); /** * Config-based implementation of IAM ListInstanceProfilesCommand * * Note: AWS Config doesn't track InstanceProfile as a separate resource type. * Instance profiles are extracted from Role configuration data. */ const AwsConfigListInstanceProfilesCommand = (0, AwsConfigClientContext_js_1.awsConfigCommand)({ command: client_iam_1.ListInstanceProfilesCommand, execute: async (input, context) => { // Get roles from Config and extract instance profiles const query = ` SELECT configuration.instanceProfileList WHERE resourceType = 'AWS::IAM::Role' AND accountId = '${context.accountId}' AND ${awsConfigUtils_js_1.resourceStatusWhereClause} `; const results = await (0, awsConfigUtils_js_1.executeConfigQuery)(query, context); // Extract unique instance profiles from all roles const instanceProfilesMap = new Map(); results.forEach((resultString) => { const { configuration } = (0, awsConfigUtils_js_1.parseConfigItem)(resultString); const instanceProfiles = configuration.instanceProfileList || []; instanceProfiles.forEach((profile) => { if (profile.arn && !instanceProfilesMap.has(profile.arn)) { instanceProfilesMap.set(profile.arn, { Path: profile.path, InstanceProfileName: profile.instanceProfileName, InstanceProfileId: profile.instanceProfileId, Arn: profile.arn, CreateDate: profile.createDate ? new Date(profile.createDate) : undefined, Roles: profile.roles?.map((r) => ({ Arn: r.arn })) || [], Tags: [] // Tags not available in role's instance profile data }); } }); }); return { InstanceProfiles: Array.from(instanceProfilesMap.values()), IsTruncated: false // TODO: Handle pagination properly }; } }); /** * Config-based implementation of IAM ListOpenIDConnectProvidersCommand */ const AwsConfigListOpenIDConnectProvidersCommand = (0, AwsConfigClientContext_js_1.awsConfigCommand)({ command: client_iam_1.ListOpenIDConnectProvidersCommand, execute: async (input, context) => { const query = ` SELECT configuration.Arn, configuration.Url, configuration.ClientIdList, configuration.ThumbprintList, tags WHERE resourceType = 'AWS::IAM::OIDCProvider' AND accountId = '${context.accountId}' AND ${awsConfigUtils_js_1.resourceStatusWhereClause} `; const results = await (0, awsConfigUtils_js_1.executeConfigQuery)(query, context); const providers = results.map((resultString) => { const { configuration, tags } = (0, awsConfigUtils_js_1.parseConfigItem)(resultString); context.putCache(configuration.Arn, 'configuration', { configuration, tags }); return { Arn: configuration.Arn }; }); return { OpenIDConnectProviderList: providers }; } }); /** * Config-based implementation of IAM GetOpenIDConnectProviderCommand */ const AwsConfigGetOpenIDConnectProviderCommand = (0, AwsConfigClientContext_js_1.awsConfigCommand)({ command: client_iam_1.GetOpenIDConnectProviderCommand, execute: async (input, context) => { const { configuration, tags } = context.getCache(input.OpenIDConnectProviderArn, 'configuration'); return { Url: configuration.Url, ClientIDList: configuration.ClientIdList, ThumbprintList: configuration.ThumbprintList, Tags: tags }; } }); /** * Config-based implementation of IAM ListSAMLProvidersCommand */ const AwsConfigListSAMLProvidersCommand = (0, AwsConfigClientContext_js_1.awsConfigCommand)({ command: client_iam_1.ListSAMLProvidersCommand, execute: async (input, context) => { const query = ` SELECT arn, configuration.SamlMetadataDocument, tags WHERE resourceType = 'AWS::IAM::SAMLProvider' AND accountId = '${context.accountId}' AND ${awsConfigUtils_js_1.resourceStatusWhereClause} `; const results = await (0, awsConfigUtils_js_1.executeConfigQuery)(query, context); const providers = results.map((resultString) => { const { configItem, configuration, tags } = (0, awsConfigUtils_js_1.parseConfigItem)(resultString); context.putCache(configItem.arn, 'configuration', { configuration, tags }); return { Arn: configItem.arn }; }); return { SAMLProviderList: providers }; } }); /** * Config-based implementation of IAM GetSAMLProviderCommand */ const AwsConfigGetSAMLProviderCommand = (0, AwsConfigClientContext_js_1.awsConfigCommand)({ command: client_iam_1.GetSAMLProviderCommand, execute: async (input, context) => { const { configuration, tags } = context.getCache(input.SAMLProviderArn, 'configuration'); return { SAMLMetadataDocument: configuration.SamlMetadataDocument, Tags: tags }; } }); /** * Config-based implementation of IAM ListPolicyTagsCommand */ const AwsConfigListPolicyTagsCommand = (0, AwsConfigClientContext_js_1.awsConfigCommand)({ command: client_iam_1.ListPolicyTagsCommand, execute: async (input, context) => { const policyName = input.PolicyArn.split('/').pop(); const query = ` SELECT tags WHERE resourceType = 'AWS::IAM::Policy' AND accountId = '${context.accountId}' AND resourceName = '${policyName}' AND ${awsConfigUtils_js_1.resourceStatusWhereClause} `; const results = await (0, awsConfigUtils_js_1.executeConfigQuery)(query, context); if (results.length === 0) { throw new Error('Policy not found'); } const { tags } = (0, awsConfigUtils_js_1.parseConfigItem)(results[0]); return { Tags: tags, IsTruncated: false }; } }); /** * Given an AWS IAM policy ARN, determine if it is an AWS-managed policy * * @param policyArn the ARN of the IAM policy * @returns true if the policy is AWS-managed, false otherwise */ function isAwsManagedPolicy(policyArn) { return (0, iam_utils_1.splitArnParts)(policyArn).accountId === 'aws'; } function putInArray(item) { if (item === undefined || item === null) { return []; } return Array.isArray(item) ? item : [item]; } //# sourceMappingURL=AwsConfigIamClient.js.map