Published on

ABAC and RBAC: A Journey with Terraform and AWS Tagged Resources

Authors

Alright, let's talk about granting permissions in your cloud kingdom. If you just give everyone the master key, you're going to have a bad time. So, we need a system. For decades, we've had two main schools of thought on this, kind of like two competing martial arts dojos: the Way of the Role (RBAC) and the Way of the Attribute (ABAC).

So what's the difference? Let's go back to our trusty cloud castle analogy.

  • RBAC (Role-Based Access Control): This is the classic, straightforward approach. You get permissions based on your job title. We create a "Guard" role, and all guards get a key that opens the armory. We create a "Chef" role, and all chefs get a key to the pantry. It's simple, predictable, and easy to explain.

  • ABAC (Attribute-Based Access Control): This is the fancy, modern, some might say magical approach. Permissions are based on attributes (or "tags" in AWS). It’s like a smart-lock system. The rule isn't "Only Chefs can open the pantry." It's "The pantry door will only open for a person whose ID card has the Department=Kitchen attribute, and only if the time is between 6 AM and 8 PM, and only if they are wearing the official Chef's Hat." It's way more flexible and powerful.

Let's see how these two styles play out in the real world with AWS and Terraform.

The RBAC Way: Simple and Structured

RBAC is the bread and butter of access control. You define a role, attach permissions to it, and then assign users to that role. It's perfect for stable organizations where everyone's job is clearly defined.

With Terraform, creating a role is no big deal.

resource "aws_iam_role" "test_role" {
  name = "test_role"

  assume_role_policy = data.aws_iam_policy_document.assume_role.json
}

resource "aws_iam_role_policy_attachment" "test_role_policy" {
  role       = aws_iam_role.test_role.name
  policy_arn = aws_iam_policy.test_policy.arn
}

Anyone assigned to test_role gets the powers defined in test_policy. Easy peasy. The downside? If you have lots of slightly different teams, you can end up with hundreds of roles ("Test-Developers", "Prod-Developers", "Super-Prod-Developers-Who-Can-Also-Order-Pizza"). This is called "role explosion," and it's not fun to manage.

The ABAC Way: Flexible and Scalable with Tags

This is where things get interesting. ABAC uses tags (which are just simple labels) to make smarter decisions. Instead of creating a new role for every project, you just create a new tag!

First, let's use Terraform to create an S3 bucket and slap a tag on it. We're labeling this bucket as part of the "Test" environment.

resource "aws_s3_bucket" "bucket" {
  bucket = "supasaf-test-bucket"
  acl    = "private"

  tags = {
    Environment = "Test"
  }
}

Now for the magic. We'll create a policy that doesn't care who you are or what your job title is. It only cares about attributes. This policy says, "You can put an object in this bucket, but only if you also tag that new object with Environment=Test."

data "aws_iam_policy_document" "s3_limited" {
  statement {
    actions = [
      "s3:ListBucket",
    ]

    resources = [
      aws_s3_bucket.bucket.arn,
    ]
  }

  statement {
    actions = [
      "s3:GetObject",
      "s3:PutObject",
      "s3:PutObjectTagging"
    ]

    resources = [
      "${aws_s3_bucket.bucket.arn}/*",
    ]

    condition {
      test     = "StringEquals"
      variable = "s3:RequestObjectTag/Environment"
      values = [
        "Test",
      ]
    }
  }
}

Let's create a user and give them this shiny new policy.

resource "aws_iam_policy" "s3_limited_policy" {
  name        = "s3_limited"
  description = "S3 limited policy"
  policy      = data.aws_iam_policy_document.s3_limited.json
}

resource "aws_iam_user" "user" {
  name = "s3_limited_user"
}

resource "aws_iam_user_policy_attachment" "user_s3_limited" {
  user       = aws_iam_user.user.name
  policy_arn = aws_iam_policy.s3_limited_policy.arn
}

Now, let's try to use our new user's credentials to upload a file. Notice we have to provide the --tagging 'Environment=Test' part. We're presenting the correct "attribute" to the magical lock.

❯ aws s3api put-object --bucket supasaf-test-bucket --key test.txt --body test.txt --tagging 'Environment=Test' --profile s3_limited_user

{
    "ETag": "\"bc98d84673286ce1447eca1766f28504\"",
    "ServerSideEncryption": "AES256"
}

Success! The smart-lock saw our tag and let us in. If we tried this without the tag, AWS would have slammed the door in our face. We can see our correctly tagged file sitting happily in the bucket.

Building a Policy Machine

The real power of ABAC with Terraform is that you can build dynamic policies. Look at this example where we use a variable to define the environment.

variable "environment" {
  description = "The environment for which to grant access"
  type        = string
}

data "aws_iam_policy_document" "s3_limited" {
  statement {
    effect = "Allow"

    actions = [
      "s3:*",
    ]

    resources = [
      "${aws_s3_bucket.bucket.arn}/*",
    ]

    condition {
      test     = "StringEquals"
      variable = "s3:ExistingObjectTag/Environment"
      values   = [var.environment]
    }
  }
}

With this, you can apply the same Terraform code for your "Test", "Staging", and "Prod" environments just by changing one variable. You've built a policy-making machine instead of hand-crafting policies one by one.

So, Which Kung Fu Style is Best?

Here’s the secret: you don’t have to choose! The best dojos teach a mix of styles.

  • Use RBAC for broad, company-wide permissions that don't change much. For example, a "Billing-Admins" role or a "ReadOnly-Auditors" role.

  • Use ABAC for your dynamic, project-based work. It's perfect for development teams working across multiple projects and environments because you can grant access to resources based on a project:X or team:Y tag without creating a million roles.

The key is to understand that security isn't a one-size-fits-all problem. By having both RBAC and ABAC in your toolkit, you have the flexibility to build a security model that is both powerful and manageable. You get the best of both worlds—the simplicity of roles and the fine-grained, scalable power of attributes. Now go forth and secure your castle!