3.4 IAM Conditions: aws:RequestedRegion, aws:MultiFactorAuthPresent, and More
Right, let’s talk about IAM Conditions. This is where you stop just handing out skeleton keys and start building a proper security system with laser tripwires and “authorized personnel only” signs. Without conditions, an IAM policy is a blunt instrument. With them, you can craft something beautifully precise. We’re going to dive into a couple of the most useful (and occasionally baffling) global condition keys, the ones that start with aws:.
Think of these aws: keys as the system’s way of telling you about the context of a request. It’s not just who is asking to do something, but how, when, and from where they’re asking. This is your primary tool for implementing the principle of least privilege. You don’t just grant s3:GetObject; you grant s3:GetObject only if the request is for a bucket in the us-east-1 region, only if the user has MFA, and only if it’s a Tuesday (okay, maybe not Tuesday, but you get the point).
The Almighty aws:RequestedRegion
This is arguably one of the most important condition keys for locking down your multi-region footprint. It checks which region the API call is being made against. This is different from aws:SourceIp, which checks where the request came from. This checks where it’s going.
Why should you care? Because if you give a principal ec2:* permissions without a region condition, they can spin up a hundred expensive p4d.24xlarge instances in every single region you operate in, and you’ll be writing a very awkward email to your CFO. Let’s prevent that.
Here’s a policy that lets a user describe instances, but only in us-east-1 and eu-west-1.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "ec2:DescribeInstances",
"Resource": "*",
"Condition": {
"StringEquals": {
"aws:RequestedRegion": ["us-east-1", "eu-west-1"]
}
}
}
]
}
The pitfall here? Not all services are regional. IAM, CloudFront, and Route53 are global services. A request to iam:ListUsers doesn’t have a region context. If you use aws:RequestedRegion in a policy that also allows global service actions, you must explicitly allow the special us-east-1 region for those global calls or, more correctly, use a condition operator that accounts for a null value. Often, it’s cleaner to have separate statements for regional and global services.
The MFA Gatekeeper: aws:MultiFactorAuthPresent
This one does exactly what it says on the tin. It checks if the user authenticated with a second factor (like a hardware key or an authenticator app) at the beginning of their session. This is your best friend for protecting the crown jewels.
Crucial, non-negotiable detail: This key evaluates to true only if MFA was used at session start. It does not magically become true if you enable MFA after logging in. This trips everyone up. If you log into the AWS console with just a password, your entire session is aws:MultiFactorAuthPresent: false, even if you later enroll an MFA device. You have to log out and back in.
Use this to create a powerful break-glass statement. Let’s say you have a super-admin policy. You don’t want it used for everyday browsing. You only want it used when someone has explicitly proven they are who they say are with MFA.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "*",
"Resource": "*",
"Condition": {
"Bool": {
"aws:MultiFactorAuthPresent": "true"
}
}
}
]
}
Now, a user can have this policy attached, but it’s inert until they log in with MFA. It’s a fantastic safety mechanism. The rough edge? API access. When using access keys (like for the CLI), this condition will always be false. Access keys are a single factor. To use MFA with the CLI, you need to use sts get-session-token with MFA serial and token, which gives you temporary keys where this condition is, correctly, true.
Other Key Players in the aws: Namespace
While RequestedRegion and MultiFactorAuthPresent are the headliners, the band has other talented members.
aws:SourceIp: The classic. Restrict access based on the public IP of the requester. Essential for corporate network access, but nearly useless for anyone working from a coffee shop with a dynamic IP. Remember, this is the IP hitting AWS’s front door, which might be different from your NAT gateway’s IP if traffic is routed weirdly.aws:PrincipalArn: This is incredibly powerful for writing policies that are tied to a specific role or user ARN, not just a generic name. Great for programmatic access where you need to be specific.aws:RequestTag/${TagKey}andaws:ResourceTag/${TagKey}: These are the workhorses of tag-based access control (ABAC).RequestTagchecks if the API call itself includes a specific tag (e.g., on a new EC2 instance), whileResourceTagchecks if the resource being acted upon has a specific tag. This is how you let teams manage their own resources by mandating they tag them withTeam=Finance.
The golden rule with all conditions is to test them relentlessly. Use the IAM Policy Simulator. It’s clunky, but it’s your best friend. Assume your first policy has a logic error that either locks everyone out or, worse, lets someone do something they shouldn’t. Conditions are your sharpest tool; just don’t accidentally point them at your own foot.