AWS offers various services, ranging from low-level EC2 bare metal instances to fully-fledged application frameworks like Amplify. This range of different services makes it impossible for AWS to provide out-of-the-box permissions that would sufficiently cover all its customers’ needs.
AWS has a central service to facilitate policies and access control called Identity and Access Management (IAM). It’s one of the core services that interact with all other AWS services to ensure every user and service can only access what’s required to do their work and nothing more.
Back in the day when engineers could simply install everything on their local machine for development, they had access to all resources. But in the cloud, the default is to allow for no permission to do anything. This protects cloud users from accidentally opening something up to a malicious actor, but it also makes the developer's work much harder because they have to think about what permissions are required to do their work.
This article seeks to explain key concepts in AWS IAM and explain what to do if things don’t go as expected.
How IAM policies are structured
IAM uses policies to define permissions, which you can then assign to IAM users or roles.
A policy is a JSON object that follows this base structure:
"Effect" can have the values "allow" or "deny", so it’s the most simple and most crucial part of the policy.
"Action" defines the API actions this policy provides access to and can include wildcards.
"Resource" defines the ARN that points to the infrastructure item that the policy gives access to and also allows for wildcards.
"Condition" is optional and defines additional conditions that must be true for the policy to take effect.
In the example above, the policy gives access to all EC2 API actions that start with “Describe” for all instances of one specific account. The condition restricts the policy to EC2 instances located in a particular VPC.
You can find an unofficial JSON schema for IAM policies on GitHub.
Differences between the two most used policy types: identity-based and resource-based
IAM lets you attach policies to IAM users, groups, and roles and directly to resources like an S3 bucket or a Lambda function.
An identity-based policy is attached to a specific user, role, or group and defines what they are allowed to do with one or more resources.
A resource-based policy is attached to a specific resource and defines what one or more users, roles, groups, or services are allowed to do with the resource in question.
Depending on your use case, one might be easier to define than the other, but keep in mind that not all AWS services support resource-based policies.
A resource-based policy differs in its JSON schema by requiring a "Principal". The principal can either be a service or an AWS account. Some AWS services, like SNS, will interact with other services from outside your AWS account, so you must define them correctly.
The following resource-based policy is attached to a specific Lambda function and defines the permissions the S3 service has for that function:
Explicit deny in policies
Using an explicit "Deny" effect in your policy will always override an “Allow”. Check out this flow diagram by AWS:
Debugging IAM policies
With computing resources like EC2, EKS, or Lambda, it’s easy to debug because you can write logging code directly to get insights, but you can't do that with a more implicit service like IAM. This is where CloudTrail comes into play.
CloudTrail is AWS’ logging service dedicated to access logs. It’s tightly integrated with IAM, and by default, it will capture the most recent logs in its Event History category.
To ensure you get all the information you need, you can set up a so-called “trail,” which will dump all the logs into an S3 bucket for you to examine later.
$ aws cloudtrailcreate-subscription --name MyTrail --s3-new-bucket my-logging-bucket
After you create a trail, you rerun your service or deployment. All your access errors will end up in that S3 bucket as JSON files for you to browse for mistakes.
If your company uses AWS extensively, chances are you have multiple AWS accounts and even organizations set up. With AWS Single Sign-On (SSO), you can manage cross-account access within AWS.
SSO comes with a feature called permission sets, which you can use to assign accounts specific policies from another account. They’re essentially a way of grouping permissions from multiple accounts into one set to make assigning them easier.
For example, you can assign your database admin permissions to all databases in your organization's accounts or allow your finance team access to the billing statements of all of your AWS accounts.
This way, your team members don’t have to wrestle with multiple accounts and instead simply use one account to access all the resources required for their work.
The policies of AWS IAM are a core component of all systems built on AWS. If you want to work effectively with AWS, you need to understand IAM; otherwise, sooner or later, you’ll run into issues that can’t simply be debugged using the console output of the programming language of your choice.
IAM policies are simple JSON objects that all follow the same schema. They’re typically split into identity-based and resource-based policies. If you read and understood a few of them, you should be ready to deploy your first application to AWS.
By default, IAM locks down all of your services to ensure malicious users can’t compromise them. Services without any access at all aren’t very usable, so you should take some time to learn about IAM.
And when your application doesn’t work and you have no idea where the problem lies, checking out AWS CloudTrail can help you debug any access control issues.