- Published on
An IAM Split-Privilege Model with Terraform for Robust Cybersecurity
- Authors
- Name
- Ted
- @supasaf
In our last chat, we talked about managing the keys to your cloud castle. But what about the person who makes the keys? In a typical setup, it's easy to accidentally create a user with "God Mode" privileges—someone who can not only open any door but can also forge new keys for themselves to open doors that don't even exist yet. This is an attacker's dream and a security admin's nightmare.
If a single person can create a new, all-powerful permission policy and then attach that policy to their own account, it's game over. They can go from a lowly castle guard to the king of the world in about five minutes. This is a massive security vulnerability just waiting to happen.
Thankfully, some very clever people have thought about this problem. In his excellent book, "AWS Security," Dylan Shields lays out a brilliant solution: The Split-Privilege Model. The idea is simple but powerful, like the rule for launching a nuclear missile: it should take two people with two different keys.
The Two-Key Solution: The Master and The Manager
The model splits the "God Mode" power into two separate roles. Neither role on its own is all-powerful.
The Master (The Key Maker): This role is like your master blacksmith. They can forge new keys (create IAM policies), build new keychains (create roles), and issue new ID cards (create users). But here's the catch: they can't give the keys to anyone. They just make them and put them on the wall.
The Manager (The Key Giver): This role is the castle chamberlain. They can't make new keys. But they can take the keys the Master has already made and assign them to the right people (attach policies to users and roles, add users to groups).
See the genius here? To take over the castle, an attacker would need to steal the keys from both the blacksmith and the chamberlain. You've just doubled the difficulty of a privilege escalation attack.
Let's Build It with Terraform
Talk is cheap, so let's get to the code. Using Terraform, we can build these two separate, limited roles. This is the blueprint for our "two-key" system. Notice the Allow
and Deny
statements—they are explicitly designed to stop one role from doing the other's job.
resource "aws_iam_user" "supasaf_master" {
name = "supasaf_master"
}
resource "aws_iam_role" "master_role" {
name = "master_role"
assume_role_policy = jsonencode({
Version = "2012-10-17",
Statement = [
{
Action = "sts:AssumeRole",
Principal = {
AWS = aws_iam_user.supasaf_master.arn
},
Effect = "Allow",
},
],
})
}
resource "aws_iam_role_policy" "master_policy" {
name = "master_policy"
role = aws_iam_role.master_role.id
policy = jsonencode({
Version = "2012-10-17",
Statement = [
{
Action = [
"iam:AttachRolePolicy",
"iam:CreateGroup",
"iam:CreatePolicy",
"iam:CreatePolicyVersion",
"iam:CreateRole",
"iam:CreateUser",
"iam:DeleteGroup",
"iam:DeletePolicy",
"iam:DeletePolicyVersion",
"iam:DeleteRole",
"iam:DeleteRolePolicy",
"iam:DeleteUser",
"iam:PutRolePolicy",
"iam:GetPolicy",
"iam:ListPolicies",
"iam:GetPolicyVersion",
"iam:ListPoliciesGrantingServiceAccess",
"iam:GetRole",
"iam:ListPolicyVersions",
"iam:GetRolePolicy",
"iam:ListRolePolicies",
"iam:GetUser",
"iam:ListAttachedGroupPolicies",
"iam:GetUserPolicy",
"iam:ListAttachedRolePolicies",
"iam:ListEntitiesForPolicy",
"iam:ListAttachedUserPolicies",
"iam:ListGroupPolicies",
"iam:ListRoles",
"iam:ListGroups",
"iam:ListUsers",
"iam:ListGroupsForUser"
],
Effect = "Allow",
Resource = "*"
},
{
Action = [
"iam:AttachRolePolicy",
"iam:CreateGroup",
"iam:CreatePolicy",
"iam:CreatePolicyVersion",
"iam:CreateRole",
"iam:CreateUser",
"iam:DeleteGroup",
"iam:DeletePolicy",
"iam:DeletePolicyVersion",
"iam:DeleteRole",
"iam:DeleteRolePolicy",
"iam:DeleteUser",
"iam:PutRolePolicy"
],
Effect = "Deny",
Resource = "*"
},
],
})
}
# Manager Role
resource "aws_iam_user" "supasaf_manager" {
name = "supasaf_manager"
}
resource "aws_iam_role" "manager_role" {
name = "manager_role"
assume_role_policy = jsonencode({
Version = "2012-10-17",
Statement = [
{
Action = "sts:AssumeRole",
Principal = {
AWS = aws_iam_user.supasaf_manager.arn
},
Effect = "Allow",
},
],
})
}
resource "aws_iam_role_policy" "manager_policy" {
name = "manager_policy"
role = aws_iam_role.manager_role.id
policy = jsonencode({
Version = "2012-10-17",
Statement = [
{
Action = [
"iam:AddUserToGroup",
"iam:AttachGroupPolicy",
"iam:DeleteGroupPolicy",
"iam:DeleteUserPolicy",
"iam:DetachGroupPolicy",
"iam:DetachRolePolicy",
"iam:DetachUserPolicy",
"iam:PutGroupPolicy",
"iam:PutUserPolicy",
"iam:RemoveUserFromGroup",
"iam:UpdateGroup",
"iam:DeleteRolePolicy",
"iam:UpdateAssumeRolePolicy",
"iam:UpdateUser"
],
Effect = "Allow",
Resource = "*"
},
{
Action = [
"iam:AttachRolePolicy",
"iam:CreateGroup",
"iam:CreatePolicy",
"iam:CreatePolicyVersion",
"iam:CreateRole",
"iam:CreateUser",
"iam:DeleteGroup",
"iam:DeletePolicy",
"iam:DeletePolicyVersion",
"iam:DeleteRole",
"iam:DeleteUser",
"iam:PutRolePolicy"
],
Effect = "Deny",
Resource = "*"
},
],
})
}
Once you apply this, you can check out your new roles in the AWS console, feeling much safer.

Where Do We Go From Here? Leveling Up Your IAM Security
Okay, so you’ve implemented the two-key rule. Awesome! That already puts you way ahead of the game. But what's next on the path to becoming an IAM security guru?
Permissions Boundaries: This is the ultimate safety net. A permissions boundary is a policy you attach to a user or role that sets the maximum permissions they can ever have. Even if the Manager role tries to attach a God Mode policy to a user, the permissions boundary can stop it, saying, "Nope, you can't grant powers that you don't have yourself." It's like putting a ceiling on what's possible, preventing even the most creative privilege escalation.
Automated Code Scanning: Before you even
terraform apply
, you can have tools automatically check your code for security mistakes. Tools like Open Policy Agent (OPA) act like a building inspector for your code. They'll scan your Terraform files and scream at you if you accidentally write a policy that's too permissive, likeAction: "*", Resource: "*"
. This is a lifesaver for catching mistakes early.Active Monitoring and Auditing: Having great locks on your doors is fantastic, but you still need security cameras. Make sure you have AWS CloudTrail enabled to log every single IAM action. Who assumed the Master role? When? What policies did they create? You should also use Amazon GuardDuty, which uses machine learning to detect suspicious IAM activity, like a role being used from a strange location.
The Takeaway: Be the Smart Warden, Not the Panicked Guard
In the world of cloud security, thinking like an attacker is half the battle. They will always look for the single point of failure—the one user, the one key that can unlock everything. The split-privilege model is a simple, powerful way to eliminate that single point of failure.
By combining this model with other IAM best practices and the power of Terraform, you move from being the panicked guard with a messy key ring to the wise, calm castle warden who has a clear, enforceable plan. You'll sleep better at night, I promise.