Building Secure AWS S3 Access for Departments at DataSafe Solutions

Hassan HussainHassan Hussain
4 min read

Here’s the situation: I needed to set up secure access to S3 buckets for different departments, HR, Finance, and Marketing, without creating a permissions spaghetti mess.

I used Terraform and AWS IAM to do it.

But instead of pretending it all went smoothly, I’m going to show you the real bumps I hit, how I untangled them, and why this setup works.

GitHub repo: github.com/hassanathussain/datasafe-solutions


Core Concepts Used

We’re using IAM users + roles + policies to create a clean RBAC flow:

  • Users don’t get direct permissions to S3.

  • They assume a role instead.

  • The role has actual access.

This way, we separate identity from permissions. Cleaner. Safer. Easier to audit.

1. IAM User

  • Represents a person or system.

  • Created in users.tf

2. IAM Role

  • A set of permissions that can be assumed.

  • Created in roles.tf

3. IAM Policy

  • Defines allowed actions and resources.

  • Defined in *_policy.tf and assume_policies.tf

4. Trust Policy

  • Set in assume_role_policydefines who can assume the role.

5. Policy Attachment

  • Binds users or roles to the defined permissions.

How the Files Talk to Each Other

users.tf

  • Defines users like hr_user, finance_user, etc.

  • Attaches an assume-role policy to each user, enabling them to assume their department-specific role.

roles.tf

  • Defines roles like HRAccessRole, FinanceAccessRole.

  • Sets the trust policy to allow assumption by IAM users in the same account.

  • Attaches permissions via policies defined elsewhere.

assume_policies.tf

  • Creates policies that allow sts:AssumeRole for each user.

*_policy.tf

  • Defines actual access levels:

    • HR: full access to S3://bucket/HR/

    • Finance: full access to Finance/, read-only to HR/

    • Marketing: full access to Marketing/


🔐 RBAC in Action

IAM UserAssumed RolePermissions
hr_userHRAccessRoleFull access to HR/
finance_userFinanceAccessRoleFull Finance/, read-only HR/
marketing_userMarketingAccessRoleFull access to Marketing/

Users don’t get direct access to S3. Instead:

  • They assume roles with sts:AssumeRole.

  • Roles carry the actual permissions.

This is the core of RBAC.


Real Mistakes I Made (So You Don’t Have To)

❌ Trust Policy Too Open

What I did:
Set "Principal": {"AWS": "*"} In my trust policy.

Why that’s bad:
That lets any AWS identity assume your role. It’s like leaving your house key under the doormat... and tweeting about it.

Fix:
I locked it down to a specific user:

"Principal": {
  "AWS": "arn:aws:iam::${account_id}:user/finance_user"
}

❌ Forgot s3:ListBucket

Symptom:
Users could upload/download, but not list files.

Fix:
Added this:

{
  "Effect": "Allow",
  "Action": "s3:ListBucket",
  "Resource": "arn:aws:s3:::bucket-name",
  "Condition": {
    "StringLike": {
      "s3:prefix": ["Finance/*"]
    }
  }
}

Now they can see what’s actually in the bucket.


❌ Bad Resource ARNs

What I did:
Used arn:aws:s3:::bucket-name/Finance instead of Finance/*

Result:
Access denied errors even though everything looked fine.

Fix:
Be explicit: arn:aws:s3:::bucket-name/Finance/*


❌ Gave Users Too Much Power

Early mistake:
Gave users direct S3 permissions and gave roles S3 permissions. Total overkill. And confusing.

Cleaned it up:
Users only get permission to assume roles.
Roles hold all the access power.


✅ Why This Works

  • Least privilege: No one gets more than they need.

  • Clear separation: Identity ≠ permissions.

  • Easy to audit: Who assumed what role, and when? CloudTrail knows.

  • Scalable: Adding a new team is just a new role + policy + user.


Walkthrough: How finance_user Gets Access

  1. Logs in with IAM credentials

  2. Uses sts:AssumeRole to grab FinanceAccessRole

  3. AWS checks:

    • Does this user have permission to assume the role? ✅

    • Does the role trust the user? ✅

  4. AWS returns a temporary access token

  5. The user uses that token to access S3 like a boss


Final Thoughts

This isn’t just about S3 permissions.

It’s about learning to think clearly about who should have access to what, and building that clarity into your infrastructure. Tools like Terraform make it repeatable, but you’ve got to know where the traps are.

Now you do.

If you want a walkthrough of the Terraform code structure next, let me know happy to break that down too.


0
Subscribe to my newsletter

Read articles from Hassan Hussain directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Hassan Hussain
Hassan Hussain

Cloud Architect | AWS Certified | DevOps Enthusiast I help developers and teams navigate cloud migrations and efficient CI/CD pipelines. Currently designing scalable infrastructure, automation, and monitoring on AWS (Terraform, GitHub Actions, ECS, Prometheus/Grafana) Documenting weekly learning and projects publicly