How to monitor and query IAM resources at scale – Part 1
In this two-part blog post, we’ll provide recommendations for using AWS Identity and Access Management (IAM) APIs, and we’ll share useful details on how IAM works so that you can use it more effectively. For example, you might be creating new IAM resources such as roles and policies through automation and notice a delay for resource propagations. Or you might be building a custom cloud security monitoring solution that uses IAM APIs to evaluate the security and compliance of your AWS accounts, and you want to know how to do that without exceeding limits. Although these are just a few example use cases, the insights described in this post are intended to help you avoid anti-patterns when building scalable cloud services that use IAM APIs.
<p>In this post, we describe how to create IAM resources and use them soon after for authorization decisions. We also describe options for monitoring and responding to IAM resource changes for entire accounts. In part 2, we’ll cover the API throttling behavior of IAM and <a href="https://docs.aws.amazon.com/STS/latest/APIReference/welcome.html" target="_blank" rel="noopener">AWS Security Token Service (AWS STS)</a> and how you can effectively plan your usage of these APIs. Let’s dive in!</p>
<h2>Use case 1: Create IAM resources and attempt to use them immediately</h2>
<p>If you’re a cloud developer, you create and use IAM resources when you develop applications on AWS. For your application to interact with AWS services, you need to grant IAM permissions to your application. Your application—whether it runs on <a href="https://aws.amazon.com/lambda/" target="_blank" rel="noopener">AWS Lambda</a>, <a href="https://aws.amazon.com/ec2/" target="_blank" rel="noopener">Amazon Elastic Compute Cloud (Amazon EC2)</a>, or another service—will need an associated <a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles.html" target="_blank" rel="noopener">IAM role</a> and <a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies.html" target="_blank" rel="noopener">policy</a> that provide the necessary permissions.</p>
<p>Imagine that you want to create <a href="https://aws.amazon.com/blogs/security/techniques-for-writing-least-privilege-iam-policies/" target="_blank" rel="noopener"> least privilege policies</a> for your application. You begin by deploying new or updated IAM resources, such as roles and policies, along with your application updates, and you automate this process to speed up testing and development.</p>
<p>During development, you begin removing unnecessary policy permissions, with your automation testing the updated permissions. However, you notice that some of your updates do not immediately take effect. The following sections address why this occurs and provide insights to help you architect for other scenarios.</p>
<h3>Understand the IAM control plane and data plane</h3>
<p>Let’s first learn more about the control plane and data plane in IAM. The <em>control plane</em> involves operations to create, read, update, and delete IAM resources, and it’s how you get the current state of IAM. When you invoke IAM APIs, you interact with the control plane. This includes any API that falls under the <code>iam:*</code> namespace. The <em>data plane</em>, in contrast, consists of the authorization system that is used at scale to grant access to the broader set of AWS services and resources. This includes the <a href="https://docs.aws.amazon.com/STS/latest/APIReference/welcome.html" target="_blank" rel="noopener">AWS STS APIs</a>, which have their own <code>sts:*</code> namespace.</p>
<p>When you call the IAM control plane APIs to create, update, or delete resources, you can expect a read-after-write consistent response. This means that you can retrieve (read) the resource and its latest updates immediately after it’s written. In contrast, the IAM data plane, where authorizations occur, is <a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/troubleshoot_general.html#troubleshoot_general_eventual-consistency" target="_blank" rel="noopener">eventually consistent</a>. This means that there will be a delay for IAM resource changes, such as updates to roles and policies, to propagate and reflect in the authorizations that follow. The delay can be several seconds or longer. Because of this, you need to allow for propagation time when you test changes to IAM resources. To learn more about the control plane and data plane of IAM, see <a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/disaster-recovery-resiliency.html" target="_blank" rel="noopener">Resilience in AWS Identity and Access Management</a>.</p>
<blockquote>
<p><strong>Note</strong>: Because calls to AWS APIs rely on IAM to check permissions, the availability and scalability of the data plane are paramount. In 2011, the “<em>can the caller do this?</em>” function handled a couple of thousand requests per second. Today, as new services continue to launch and the number of AWS customers increases, AWS Identity handles over half a billion API calls per second worldwide, and the number is growing. Eventually consistent design enables the IAM data plane to maintain the high availability and low latency needed to evaluate permissions on AWS.</p>
</blockquote>
<p>This is why when architecting your application, we recommend that you don’t depend on control plane actions such as resource updates for critical parts of your application’s workflow. Instead, you should architect to take advantage of the data plane, which includes STS and the authorization system of IAM. In the next section, we describe how you can do this.</p>
<h3>Test permissions with STS scope-down policies</h3>
<p>IAM role sessions have a feature called a <a href="https://aws.amazon.com/blogs/security/create-fine-grained-session-permissions-using-iam-managed-policies/" target="_blank" rel="noopener">session policy</a>, which takes effect immediately when a role is assumed. This is an optional policy that you can provide to scope down the role’s existing identity policies, with the permissions <a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_evaluation-logic.html#policy-eval-basics" target="_blank" rel="noopener">being the intersection</a> of the role’s identity-based policies and the session policy. By using session policies, you get specific, scoped-down credentials from a single pre-existing role without having to create new roles or identity policies for each particular session’s use case. You can use session policies for your application or when you test which least privilege policies are best for your application.</p>
<p>Let’s walk through an example of when to use session policies for permissions testing. Imagine that you need permissions that require very specific, fine-grained conditions to attain your ideal least privilege policy. You might iterate on the policy several times, making updates and testing the changes over and over again. If you update a policy attached to a role, you need to wait for these changes to propagate to the IAM data plane. But if you instead specify a scope-down policy when assuming the pre-existing role prior to testing, you can immediately test and observe the effects of your permissions changes. Immediate testing is possible because your role and its original policy have already propagated to the data plane, enabling you to iterate over various scoped-down session policies that operate against the IAM data plane.</p>
<h3>Use STS session policies to assume a role with the AWS CLI</h3>
<p>There are two ways to provide a session policy during the <a href="https://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRole.html" target="_blank" rel="noopener">AssumeRole</a> process: you can provide an inline policy document or the Amazon Resource Names (ARNs) of managed session policies. The following example shows how to do this through the <a href="https://aws.amazon.com/cli/" target="_blank" rel="noopener">AWS Command Line Interface (AWS CLI)</a>, by passing in a policy document along with the <span>AssumeRole</span> call. If you use this example policy, make sure to replace <span></span> and <span></span> with your own information.</p>
<div class="hide-language">
<pre><code class="lang-html">$ aws sts assume-role
–role-arn arn:aws:iam:::role/s3-full-access
–role-session-name getobject-only-exco
–policy ‘{ “Version”: “2012-10-17”, “Statement”: [ { “Action”: [ “s3:GetObject” ], “Effect”: “Allow”, “Resource”: “arn:aws:s3::: /*” } ] }’
In this example, we provide a previously created role ARN named <code>s3-full-access</code>, which provides full access to <a href="https://aws.amazon.com/s3/" target="_blank" rel="noopener">Amazon Simple Storage Service (Amazon S3)</a>. We can further restrict the role’s permissions by supplying a policy with the optional <code>--policy</code> option. The inline policy document only allows the <code>GetObject</code> request against the S3 bucket named <code></code>. The effective permissions for the returned session are the intersection of the role’s identity-based policies and our provided session policy. Therefore, the role session’s permissions are limited to only performing the <code>GetObject</code> request against the <code></code>.</p>
<blockquote>
<p><strong>Note</strong>: The combined size of the passed inline policy document and all passed managed policy ARN characters cannot exceed 2,048 characters. You can reduce the size of the JSON policy document by removing unnecessary whitespace and shortening or removing tags associated with your session.</p>
</blockquote>
<p>To learn more about session permissions, see <a href="https://aws.amazon.com/blogs/security/create-fine-grained-session-permissions-using-iam-managed-policies/" target="_blank" rel="noopener">Create fine-grained session permissions using IAM managed policies</a>. In part two of this post, we will describe how you can use role sessions when you need to provide credentials at a high rate.</p>
<h2>Use case 2: Monitor and respond to IAM resources for entire accounts</h2>
<p>You might need to periodically audit the state of your IAM resources, such as roles and policies, including whether these IAM resources have changed, in a single account or across your entire organization. For example, you might want to check whether roles have overly broad access to actions and resources. Or you might want to monitor IAM resource creation and updates to respond to security-relevant permission changes. In this section, you will learn how to choose the right tool for auditing and monitoring IAM resources across accounts. You will learn about the AWS services that support this use case, the benefits of polling compared to event-based architectures, and powerful APIs that aggregate common information.</p>
<h3>Respond to configuration changes with an event-driven approach</h3>
<p>Sometimes you might need to perform actions relatively quickly based on IAM changes. For example, you might need to check if a trust policy for a newly created or updated role allows cross-account access. In cases like this, you can use <a href="https://docs.aws.amazon.com/config/latest/developerguide/evaluate-config.html" target="_blank" rel="noopener">AWS Config rules</a>, <a href="https://aws.amazon.com/cloudtrail/" target="_blank" rel="noopener">AWS CloudTrail</a>, or <a href="https://aws.amazon.com/eventbridge/" target="_blank" rel="noopener">Amazon EventBridge</a> to detect state changes and perform actions based on these state changes. You can use AWS Config rules to evaluate whether a resource complies with the conditions that you specify. If it doesn’t comply, you can provide a workflow to <a href="https://docs.aws.amazon.com/config/latest/developerguide/remediation.html" target="_blank" rel="noopener">remediate the non-compliance</a>. With CloudTrail, you can monitor your account’s API calls, and log API calls for your accounts with <a href="https://docs.aws.amazon.com/awscloudtrail/latest/userguide/creating-trail-organization.html" target="_blank" rel="noopener">AWS Organizations integration</a>. EventBridge works closely with CloudTrail and <a href="https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-rules.html" target="_blank" rel="noopener">helps you create rules</a> that match incoming events and send them to targets, such as Lambda, where your code can perform analysis or automated remediation. You can even filter out events from your accounts and send them to a central account’s <a href="https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-event-bus.html" target="_blank" rel="noopener">event bus</a> for processing. For an example of how to use EventBridge with <a href="https://aws.amazon.com/iam/features/analyze-access/" target="_blank" rel="noopener">IAM Access Analyzer</a> to remediate cross-account access in a role’s trust policy, see <a href="https://aws.amazon.com/blogs/security/automate-resolution-for-iam-access-analyzer-cross-account-access-findings-on-iam-roles/" target="_blank" rel="noopener">Automate resolution for IAM Access Analyzer cross-account access findings on IAM roles</a>. Which feature you choose depends on whether you need to monitor one account or all accounts in your organization, as well as which solution you are more comfortable building with.</p>
<p>One caveat to an event-driven approach is that if many events occur over a short period and your application responds to each event with an IAM API call of its own, you could eventually be throttled by IAM. To address this, you can queue up your responding API calls, distribute them over a longer period, or aggregate them to reduce API call volume. For example, if some of your calls are write APIs (such as <a href="https://docs.aws.amazon.com/IAM/latest/APIReference/API_UpdateAssumeRolePolicy.html" target="_blank" rel="noopener">UpdateAssumeRolePolicy</a> or <a href="https://docs.aws.amazon.com/IAM/latest/APIReference/API_CreatePolicyVersion.html" target="_blank" rel="noopener">CreatePolicyVersion</a>) or read APIs (such as <a href="https://docs.aws.amazon.com/IAM/latest/APIReference/API_GetRole.html" target="_blank" rel="noopener">GetRole</a> or <a href="https://docs.aws.amazon.com/IAM/latest/APIReference/API_GetRolePolicy.html" target="_blank" rel="noopener">GetRolePolicy</a>), you can call them serially with a delay between calls. If you need the latest status on a large number of principals and policies, you can call IAM bulk APIs such as <a href="https://docs.aws.amazon.com/IAM/latest/APIReference/API_GetAccountAuthorizationDetails.html" target="_blank" rel="noopener">GetAccountAuthorizationDetails</a>, which will return data to you for principals and policies and their relationships in your organization. This approach helps you avoid throttling and querying the IAM control plane with unnecessary and redundant API calls. You will learn more about throttling and how to address it in part two of this post.</p>
<h3>Retrieve point-in-time resource information with AWS Config</h3>
<p><a href="https://aws.amazon.com/config/" target="_blank" rel="noopener">AWS Config</a> helps you assess, audit, and evaluate the configuration of your AWS resources. It also offers multi-account, multi-Region data aggregation and is <a href="https://docs.aws.amazon.com/organizations/latest/userguide/services-that-can-integrate-config.html" target="_blank" rel="noopener">integrated with AWS Organizations</a>. With AWS Config, you can create rules that detect and respond to changes. AWS Config also keeps an inventory of AWS resource configurations that you can query through its API, so that you don’t need to make direct API calls to each resource’s service. AWS Config also offers the ability to <a href="https://docs.aws.amazon.com/config/latest/developerguide/looking-up-discovered-resources.html" target="_blank" rel="noopener">return the status of resources</a> from multiple accounts and <a href="https://aws.amazon.com/about-aws/global-infrastructure/regions_az/" target="_blank" rel="noopener">AWS Regions</a>. As shown in Figure 1, you can use the AWS Config console to run a simple <a href="https://docs.aws.amazon.com/config/latest/developerguide/example-query.html" target="_blank" rel="noopener">SQL-like statement</a> for details on the IAM roles in your entire organization.</p>
<div id="attachment_28554" class="wp-caption aligncenter">
<img aria-describedby="caption-attachment-28554" src="https://www.infracom.com.sg/wp-content/uploads/2023/02/img1-2-1024x569-1.png" alt="Figure 1: Run a query on IAM roles in AWS Config" width="760" class="size-large wp-image-28554" />
<p id="caption-attachment-28554" class="wp-caption-text">Figure 1: Run a query on IAM roles in AWS Config</p>
</div>
<p>The preceding results also show associated resources, such as the inline and attached policies for the IAM roles. Alternatively, you can obtain these results from the <a href="https://docs.aws.amazon.com/config/?id=docs_gateway" target="_blank" rel="noopener">SDK or CLI</a>. The following query that uses the CLI is equivalent to the preceding query that uses the console. If you use this query, make sure to replace <span>DOC-EXAMPLE-CONFIG-AGGREGATOR></span> with your <a href="https://docs.aws.amazon.com/config/latest/developerguide/aggregate-data.html" target="_blank" rel="noopener">AWS Config aggregator</a>.</p>
<div class="hide-language">
<pre><code class="lang-text">aws configservice select-aggregate-resource-config
–configuration-aggregator-name
–expression “SELECT accountId, resourceId, resourceName, resourceType, tags, configuration.attachedManagedPolicies, configuration.rolePolicyList WHERE resourceType = ‘AWS::IAM::Role'”
Here is the response (note that we’ve adjusted the formatting to make it more readable):
The preceding command returns the details of roles in your organization’s accounts, including the full policy document for the associated inline policy. It also returns the customer-managed policy names and their ARNs, for which you can view the policy documents and versions by using the BatchGetResourceConfig API. Note that AWS Config doesn’t provide the AWS-managed policy documents. However, these are common across accounts, and we will show you how to query that data later in this section.
To query the status of roles in your organization, you need to have AWS Config enabled in each account. You also need an aggregator to monitor your accounts with your organization’s management account or a delegated administrator account. For more details on how to set up AWS Config, see the AWS Config developer guide. After you set up AWS Config, you can periodically call the AWS Config APIs to get a snapshot of the current or prior state of your resources. Furthermore, you can periodically pull the snapshot records and evaluate this information in other tools outside of AWS Config. So before you directly use the IAM APIs to get IAM information, consider using AWS Config—this is what it’s for!
Retrieve IAM resource information directly from IAM
As previously noted, AWS Config can give you a bulk view of your AWS and IAM resources. Additionally, CloudTrail and EventBridge can detect AWS and IAM resource changes and help you act on them. If you need data from IAM beyond what these services offer, you can query the IAM APIs directly to get the latest information on your resources.
A few key APIs can help you audit IAM resources more efficiently, especially in bulk. The first is GetAccountAuthorizationDetails, which enables you to retrieve the principals in your account, their associated inline policy documents (if any), attached managed policies, and their relationships to each other. This API reduces the need to individually call ListRolePolicies and ListAttachedRolePolicies for each role in an account. GetAccountAuthorizationDetails also returns the role trust policy document for roles in the results. Finally, GetAccountAuthorizationDetails allows you to filter the result set. For example, if you don’t need information relating to groups or AWS managed policies, you can exclude these from the API response. You can do this by using the filter parameter to only include the details that you need at the time.
Another useful API is GenerateServiceLastAccessedDetails. This API gives you details about when an IAM resource (user, group, role, or policy) was last used in an attempt to access AWS services. You can use this API to identify roles that are unused and remove them if you don’t need them. IAM Access Analyzer, which you will learn about later in this post, also uses the same information.
The following table summarizes the key APIs that you can use, rather than building your own code that loops for this information individually.
Type of information | API | How to use the API | Frequency of use |
User list and user detail | GetAccountAuthorizationDetails | Pass User to the filter parameter | When needed, per account |
User’s inline policy | User’s inline policy GetAccountAuthorizationDetails | Pass User to the filter parameter | When needed, per account |
User’s attached managed policies | GetAccountAuthorizationDetails | Pass User to the filter parameter | When needed, per account |
Role list and role detail | GetAccountAuthorizationDetails | Pass Role to the filter parameter | When needed, per account |
Role trust policy | GetAccountAuthorizationDetails | Pass Role to the filter parameter | When needed, per account |
Role’s inline policy | GetAccountAuthorizationDetails | Pass Role to the filter parameter | When needed, per account |
Role’s attached managed policies | GetAccountAuthorizationDetails | Pass Role to the filter parameter | When needed, per account |
Role last used | GetAccountAuthorizationDetails | Pass Role to the filter parameter | When needed, per account |
Group list and group detail | GetAccountAuthorizationDetails | Pass Group to the filter parameter | When needed, per account |
Group’s inline policy | GetAccountAuthorizationDetails | Pass Group to the filter parameter | When needed, per account |
Group’s attached managed policies | GetAccountAuthorizationDetails | Pass Group to the filter parameter | When needed, per account |
AWS customer managed policies | GetAccountAuthorizationDetails | Pass LocalManagedPolicy to the filter parameter | When needed, per account |
AWS managed policies | GetAccountAuthorizationDetails | Pass LocalManagedPolicy to the filter parameter | 24 hours recommended, globally (once for all accounts within an AWS partition) |
Policy versions | GetAccountAuthorizationDetails | Pass either LocalManagedPolicy or WSManagedPolicy to the filter parameter | 24 hours recommended, per account |
Services access attempts by an IAM resource | GetServiceLastAccessedDetails | Submit a job through the GenerateServiceLastAccessedDetails API, which returns a JobId; then retrieve the results after the job completes. | Spread total number of requests evenly across 24 hours |
Actions access attempts by an IAM resource | GetServiceLastAccessedDetails | Submit a job through the GenerateServiceLastAccessedDetails API which returns a JobId; then retrieve the results after the job completes. Pass ACTION_LEVEL as the required Granularity parameter. | Spread total number of requests evenly across 24 hours |
Note: In the table, we suggest that you perform some of these API requests once every 24 hours as a starting point. You might prefer to perform your own analysis at a longer time interval, such as every 48 hours, but we don’t recommend requesting it more often than every 24 hours because these resources (and therefore the details in the responses) don’t change often. These APIs are suitable for periodic, point-in-time collection of information. If you need faster detection of information from
GetAccountAuthorizationDetails
, consider whether AWS Config rules or EventBridge will fit your needs. ForGetServiceLastAccessedDetails
, recent activity usually appears within four hours, so more frequent requests are unlikely to provide much value.
Use of these APIs can help you avoid writing code that loops through results to make individual read API calls for each principal, policy, and policy version in an account, which could result in tens of thousands of API requests and call throttling. Instead of iterating over each resource, you should use solutions that return bulk data, such as GetAccountAuthorizationDetails
, AWS Config, or an AWS Partner Network solution. However, if you’re experiencing throttling, you will learn some practical considerations on how to handle that later in this post.
Inspect IAM resources across multiple accounts and organizations
Your use case might require that you inspect IAM resources across multiple accounts in your organization. Or perhaps you are an independent software vendor and need to build a software-as-a-service tool to evaluate IAM resources across many organizations. The following considerations can help you address use cases like these.
AWS Organizations integration
Previously, you learned of the benefits of the “service last accessed data” that the GenerateServiceLastAccessedDetails
and GetServiceLastAccessedDetails
APIs provide. But what if you want to pull this data for multiple accounts in your organization? IAM has bulk APIs that support querying this data across your entire organization, so you don’t need to assume a role in each account to generate the request. To generate a report for entities (organization root, organizational unit, or account) or policies in your organization, use the GenerateOrganizationsAccessReport operation, which returns a JobId
that is passed as a parameter to the GetOrganizationsAccessReport operation to check if the report has been generated. When the job status is marked complete, you can retrieve the report.
AWS managed policies
Many customers use AWS managed policies because they align to common job functions. AWS creates and administers these policies, which have their own ARNs, such as arn:aws:iam::aws:policy/AWSCodeCommitPowerUser
. AWS managed policies are available for every account, and they are the same for every account. AWS updates them when new services and API operations are introduced. Updated policies are recorded and visible as a new version, so you only need to query for the current AWS managed policies once per evaluation cycle, rather than once per account. Therefore, if you’re evaluating hundreds or thousands of accounts, you shouldn’t include the AWS managed policies and their policy versions in your query. Doing so would result in thousands of redundant API requests and could cause throttling. Instead, you can query the AWS managed policies once and then reuse the results across your analysis and evaluation by caching the results for a period of time (for example, every 24 hours) in your application before requesting them again to check for updates. Because AWS managed policies are available through the GetAccountAuthorizationDetails
API, you don’t need to query for the AWS managed policies or their versions as a separate action.
Multi-account limits
The preceding table lists the frequency of API requests as “per account” in many places. If you’re calling IAM APIs by assuming a role in other accounts from a central account, some IAM APIs have rate-limiting criteria that apply to API requests performed from the assuming account (the central account). To query data from multiple accounts, we recommend that you serially iterate over the accounts one-by-one to avoid throttling. You’ll learn more about this strategy, as well as throttling, in part 2 of this blog post.
Conclusion
In this post, you learned about different aspects of IAM and best practices to test and query IAM efficiently. With STS session policies, you can test different policies to help achieve least privilege access. With AWS Config, EventBridge, CloudTrail, and CloudTrail Lake, you can audit your IAM resources and respond to changes while reducing the number of IAM API calls that you make. If you need to call IAM directly, you can use IAM bulk APIs for more efficient retrieval of your resource state. You can learn more about IAM and best practices in part 2 of blog post: How to monitor and query IAM resources at scale – Part 2. Stay tuned!
If you have feedback about this post, submit comments in the Comments section below. If you have questions about this post, start a new thread on the Security, Identity, & Compliance re:Post or contact AWS Support.
Want more AWS Security news? Follow us on Twitter.
<!-- '"` -->