Resourcely Documentation
LoginSign Up
  • Get Started
    • 🎱What is Resourcely?
    • 👋Why Resourcely
    • 🏃Quickstart
      • Terraform policies integrated into CI
      • Remediate policy violations in existing infrastructure
      • Templates for generating Terraform
      • Glossary
  • Concepts
    • Foundry
      • Create Blueprints with Foundry
      • Creating Guardrails with Foundry
      • lmport Terraform Modules
    • Guardrails
      • Writing your own Guardrails
      • Editing Guardrails
      • Releasing Guardrails
      • Enabling Inactive Guardrails
      • Guardrails in Action
        • 🐱GitHub Actions
        • 🦊GitLab Pipelines
    • Campaigns
      • Get started with Campaigns
      • Creating Campaigns
      • Remediate Resources
      • Campaign Agent
        • State File Support
          • Amazon Simple Storage Service (S3)
          • Google Cloud Storage (GCS)
          • HCP Terraform
          • Spacelift
        • Running Campaigns with GitHub Actions and a Repo-Hosted State File
        • Running Campaigns Locally
    • Blueprints
      • Authoring Your Own Blueprints
      • Using Built-in Resourcely Blueprints
      • Configuring Global Contexts
      • Deep Linking
    • Resources
      • Provisioning Infrastructure
      • Editing Infrastructure
      • Shopping Cart
      • Config Roots and Environments
    • Other Features and Settings
      • Global Values
      • Global Context
      • Metrics
      • Resourcely-cli
      • Resourcely.yaml
      • VCS Proxy
      • Settings
        • User management
        • Company Information
        • Notification Settings
        • Change Management
          • 🐱Connect to GitHub
          • 🦊Connect to Gitlab
        • Generate API Token
    • ✨Production Setup
      • Single Sign-On (SSO)
        • Auth0
        • AWS Single Sign-On
        • Azure AD
        • Google Workspace
        • JumpCloud
        • Okta
        • Omnissa Workspace ONE (formerly VMware)
        • OneLogin
        • Ping Identity
        • Other SAML / OIDC Providers
      • Source Code Management
        • Page
        • 🐱GitHub
        • 🦊GitLab
        • Atlassian Bitbucket
        • Azure Repos
  • Tutorials and guides
    • Remediation Use Cases
      • Apply tags to resources for automating backups
      • Implement centralized logging
    • Blueprints Use Cases
      • Automate Data Pipeline Creation
      • Encryption for GCP
      • AWS Account Factory
      • Streamline and govern AI
      • IAM Factory
      • Cost optimization for FinOps
      • Guardrails for Terraform Modules
    • Using the Resourcely Terraform Provider
      • Setup Resourcely Provider
      • Blueprints
      • Guardrails
      • Global Context
  • Integrate
    • CI/CD & Terraform Runners
      • Atlantis
      • 🐟AWS CodeBuild
      • Azure Pipelines
      • Buildkite
      • CircleCI
      • CloudBees CI
      • Codefresh
      • Digger
      • Env0
      • 🎏GitHub Actions
        • 🐱Local Plan
          • 🐹AWS with OpenID Connect
        • 🐶Terraform Cloud Integration
      • 🦊GitLab Pipelines
      • Harness
      • 🗻HashiCorp Cloud Platform (formerly Terraform Cloud)
      • Jenkins
      • Octopus Deploy
      • Scalr
      • 🌌Spacelift
      • Terramate
      • 🌎Terrateam
    • Cloud Providers
      • 🌨️Amazon Web Services (AWS)
      • 🤓Google Cloud Platform (GCP)
        • Guardrail Gaunlet at Google Cloud Next 2025
      • 💾Microsoft Azure
      • Alibaba Cloud
      • Huawei Cloud
      • IBM Cloud
      • Oracle Cloud Infrastructure (OCI)
      • Tencent Cloud
      • VMware vSphere
    • Developer Portals
      • Atlassian Compass
      • Backstage
      • Cortex
      • Harness IDP
      • Home grown internal developer portals
      • OpsLevel
      • Port
      • Roadie
    • ITSM
      • Atlassian Jira
      • FreshWorks
      • ServiceNow ITSM
      • ZenDesk
    • CSPM
      • Wiz
    • More Terraform Provider Integrations
      • 🚂ConductorOne Provider
      • Databricks Provider
      • Kubernetes Provider
      • 🐕Datadog Provider
      • ❄️Snowflake Provider
Powered by GitBook
On this page
  • Overview
  • Creating an IAM Blueprint
  • Terraform vs Blueprint
  • Frontmatter
  • IAM Group
  • IAM Group Membership
  • IAM Policy and Group Policy Attachment
  • Using Global Values
  • Handling multiple IAM policy.statement.resources with nested section tags
  • Restricting behavior with Guardrails
  • Modular Blueprints
  • Conclusion
  1. Tutorials and guides
  2. Blueprints Use Cases

IAM Factory

Streamlining and standardizing IAM creation

PreviousStreamline and govern AINextCost optimization for FinOps

Last updated 6 months ago

Overview

Identity Access Management (IAM) is extensively utilized and may look simple at first glance, yet it is surprisingly complex. IAM users, roles, groups, and policies form the backbone of restricting and allowing access between people, applications, databases, servers, and more.

Developers who lack experience with Identity Access Management (IAM) may spend countless hours trying to create effective policies. Even those with IAM experience can unintentionally misconfigure settings without realizing it. These accidental misconfigurations can lead to serious consequences, such as compromised data, malfunctioning applications, or malicious actions by unauthorized individuals.

Resourcely’s configuration platform is a great way to make deploying IAM simple, while ensuring that IAM resources that are created meet your company’s requirements. Below, we’ll create an IAM factory that gives developers a simple UI for creating IAM users, roles, policies, and groups. Your company can customize these Blueprints and Guardrails based on your access standards by signing up for Resourcely for free:

Creating an IAM Blueprint

We'll focus on creating modular IAM Blueprints in this tutorial.

There are 4 primary resources needed to create an IAM group, associate users, and attach a policy:

Terraform vs Blueprint

Terraform

A Terraform example might look like this:


# Define an IAM policy
resource "aws_iam_policy" "example_policy" {
  name        = "example-policy"
  description = "Example policy for demo purposes"
  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Action   = "s3:ListBucket"
        Effect   = "Allow"
        Resource = "arn:aws:s3:::example-bucket"
      },
      {
        Action   = "s3:GetObject"
        Effect   = "Allow"
        Resource = "arn:aws:s3:::example-bucket/*"
      }
    ]
  })
}

# Attach the policy to the group
resource "aws_iam_group_policy_attachment" "example_attachment" {
  group      = aws_iam_group.example_group.name
  policy_arn = aws_iam_policy.example_policy.arn
}

# Create IAM users
resource "aws_iam_user" "example_user1" {
  name = "example-user1"
}

resource "aws_iam_user" "example_user2" {
  name = "example-user2"
}

# Assign users to the group
resource "aws_iam_group_membership" "example_group_membership" {
  name  = "example-group-membership"
  group = aws_iam_group.example_group.name
  users = [
    aws_iam_user.example_user1.name,
    aws_iam_user.example_user2.name,
  ]
}

Blueprint
---
constants:
  __group_name: "{{ group_name }}_{{ __guid }}"
variables:
  group_name:
    desc: "Name for the IAM group. This name should be unique within the AWS account."
    required: true
    group: IAM Group Settings
  users:
    desc: "List of IAM user names to add to this IAM group."
    required: false
    group: IAM Group Membership
    links_to: resource.aws_iam_user.name
  iam_policies.iam_action_list:
    global_value: aws_iam_actions_collection
    group: IAM Group Policy
    desc: "Select what permission sets you'd like this policy to have"
  iam_policies.iam_effect:
    global_value: iam_effect
    group: IAM Group Policy
    desc: "Effect to apply to the actions"
  iam_policies.policy_name:
    group: IAM Group Policy
    desc: "Name of the policy to create."
  iam_policies.resources.resource:
    group: IAM Group Policy
    desc: "Select the resources you'd like to associate with this policy. Link to a resource you are creating from the dropdown, or specify an arn in the format aws:servicename:::your-specific-resource."
  iam_policies:
    group: IAM Group Policy
groups:
  IAM Group Settings:
    order: 1
    desc: "Settings for defining the IAM group, including its name."
  IAM Group Policy:
    order: 2
    desc: "Policy to create to attach to the IAM group. Choose the various effects "
  IAM Group Membership:
    order: 3
    desc: "Membership for the IAM group. List any IAM users that should belong to this group."
---

resource "aws_iam_group" "{{__group_name}}" {
  name = {{ group_name }}
}

resource "aws_iam_group_membership" "{{__group_name}}_group_membership" {
  name = "{{ group_name }}_membership"
  group = aws_iam_group.{{__group_name}}.name
  users = [
    {{# users }}
      "{{ users }}",
    {{/ users }}
  ]
}

{{# iam_policies }}

resource "aws_iam_group_policy_attachment" "{{__group_name}}_policy_attachment_{{ iam_policies.__index }}" {
  group      = aws_iam_group.{{__group_name}}.name
  policy_arn = resource.aws_iam_policy.{{ iam_policies.policy_name }}_{{iam_policies.__index}}.arn
}

resource "aws_iam_policy" "{{iam_policies.policy_name}}_{{ iam_policies.__index }}" {
  name        = "{{ iam_policies.policy_name }}"
  description = "Custom IAM policy for {{ group_name }} group."
  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Action = [
          {{ iam_policies.iam_action_list }}
        ]
        Effect   = [
            {{ iam_policies.iam_effect }}
        ]

        Resource = [
          {{# iam_policies.resources }}
          {{ iam_policies.resources.resource }},
          {{/ iam_policies.resources }}
        ]
      },
    ]
  })
}
{{/ iam_policies }}

With our Blueprint, we get the following form for developers:

Frontmatter

Let’s break this Blueprint down into chunks. First, the frontmatter:

---
constants:
  __group_name: "{{ group_name }}_{{ __guid }}"
variables:
  group_name:
    desc: "Name for the IAM group. This name should be unique within the AWS account."
    required: true
    group: IAM Group Settings
  users:
    desc: "List of IAM user names to add to this IAM group."
    required: false
    group: IAM Group Membership
    links_to: resource.aws_iam_user.name
  iam_policies.iam_action_list:
    global_value: aws_iam_actions_collection
    group: IAM Group Policy
    desc: "Select what permission sets you'd like this policy to have"
  iam_policies.iam_effect:
    global_value: iam_effect
    group: IAM Group Policy
    desc: "Effect to apply to the actions"
  iam_policies.policy_name:
    group: IAM Group Policy
    desc: "Name of the policy to create."
  iam_policies.resources.resource:
    group: IAM Group Policy
    desc: "Select the resources you'd like to associate with this policy. Link to a resource you are creating from the dropdown, or specify an arn in the format aws:servicename:::your-specific-resource."
  iam_policies:
    group: IAM Group Policy
groups:
  IAM Group Settings:
    order: 1
    desc: "Settings for defining the IAM group, including its name."
  IAM Group Policy:
    order: 2
    desc: "Policy to create to attach to the IAM group. Choose the various effects "
  IAM Group Membership:
    order: 3
    desc: "Membership for the IAM group. List any IAM users that should belong to this group."
---

We have three sections in the frontmatter: constants, variables, and groups.

Constants are used to define static values that you want to reference. Variables create input fields in our developer form, and groups allow us to organize our developer form.

The variables section is easy to create: in this example, we actually did it by highlighting various fields and clicking “Generate Tags”.

We define metadata such as description, which group to organize the input in, and even links_to for linking to existing resources.

IAM Group

Next up, we start creating templated Terraform utilizing our variables:

resource "aws_iam_group" "{{__group_name}}" {
  name = {{ group_name }}
}

Here, we created our first resource: the aws_iam_group. We will associate users and policies with this group, giving users that are members the ability to access certain services or resources.

The name = {{ group_name }} line references the group_name variable we defined in our frontmatter:

  group_name:
    desc: "Name for the IAM group. This name should be unique within the AWS account."
    required: true
    group: IAM Group Settings

Defining variables automatically creates a form for developers to interact with. Resourcely automatically populates the variable with choices that make sense, based on the associated field. If the variable is related to a region, relevant region options will be populated. If the variable is related to a boolean, true/false will be populated.

IAM Group Membership

Further inspecting our Blueprint, let’s check out our next resource: IAM group membership.

resource "aws_iam_group_membership" "{{__group_name}}_group_membership" {
  name = "{{ group_name }}_membership"
  group = aws_iam_group.{{__group_name}}.name
  users = [
    {{# users }}
      "{{ users }}",
    {{/ users }}
  ]
}

Here, we utilize the same group_name variable and the __group_name constant that we set in the frontmatter to set the name our aws_iam_group_membership without requiring more inputs. We append “_membership” to the variable to make the name unique. This resource will associate users with the IAM group that we created before.

The line group = aws_iam_group.{{__group_name}}.name lets us reference the name attribute of the group that we created above.

Finally, we associate usernames with the IAM group. Here we utilize section tags {{# }} and {{/ }}. Section tags allow us to take 1 or many inputs in an indexed array that can be referenced with the special __index variable. These inputs are automatically handled in our form like the below:

Section tags can be nested inside each other, which we’ll explore in depth later.

IAM Policy and Group Policy Attachment

{{# iam_policies }}

resource "aws_iam_group_policy_attachment" "{{__group_name}}_policy_attachment_{{ iam_policies.__index }}" {
  group      = aws_iam_group.{{__group_name}}.name
  policy_arn = resource.aws_iam_policy.{{ iam_policies.polcy_name }}_{{iam_policies.__index}}.arn
}

resource "aws_iam_policy" "{{ iam_policies.policy_name }}_{{ iam_policies.__index }}" {
  name        = "{{ iam_policies.policy_name }}"
  description = "Custom IAM policy for {{ group_name }} group."
  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Action = [
          {{ iam_policies.iam_action_list }}
        ]
        Effect   = [
            {{ iam_policies.iam_effect }}
        ]

        Resource = [
          {{# iam_policies.resources }}
          {{ iam_policies.resources.resource }},
          {{/ iam_policies.resources }}
        ]
      },
    ]
  })
}
{{/ iam_policies }}

The policy attachment resource is straightforward - we reference the single IAM group that we created before, and then we reference the IAM policy that we create below it.

Using Global Values

Recall our frontmatter: for the Action and Effect variables, we referenced global values:

  iam_policies.iam_action_list:
    **global_value: aws_iam_actions_collection**
    group: IAM Group Policy
    desc: "Select what permission sets you'd like this policy to have"
  iam_policies.iam_effect:
    **global_value: iam_effect**
    group: IAM Group Policy
    desc: "Effect to apply to the actions"

These reference two global value collections that we have created. These collections are used in this case to simplify selections for developers.

For IAM actions, developers would select DynamoDB Read, and the collection value (["dynamodb:GetItem","dynamodb:Query","dynamodb:Scan","dynamodb:BatchGetItem"]) would be inserted into the Terraform generated for them when they fill out the form. Collections are a great way to simplify and restrict inputs, and they’re also customizable: you could define any profile of permissions that users could choose from and add rules around that using Guardrails.

Handling multiple IAM policy.statement.resources with nested section tags

For resources, we utilize a nested section tag:

        Resource = [
          {{# iam_policies.resources }}
          {{ iam_policies.resources.resource }},
          {{/ iam_policies.resources }}
        ]

Nested section tags allow us to associate multiple resources with the policy. Resources expect an arn with the format arn:aws:service_name:::object_name. One common (although unsafe) pattern that users will often use is to simply insert a wildcard (*). This would allow the policy to interact with any AWS object in your account.

Restricting behavior with Guardrails

Let’s say you want to restrict this behavior. You would apply the following Resourcely Guardrail to your Blueprint:

GUARDRAIL "[IAM] Disallow IAM policies with a wildcard resource public"
  WHEN aws_iam_policy OR aws_iam_role_policy
    REQUIRE NO policy.statement.resource = "*"
  OVERRIDE WITH APPROVAL @security

If a user were to insert a wildcard into the Resources input, the resulting Terraform change request would be blocked from merging until the security team (or whichever team you specify) approves.

Modular Blueprints

Blueprints can also be modular: broken up into many small pieces, and linked together. Here are 4 modular Blueprints that can be linked together.

Conclusion

You can copy this Blueprint into your Resourcely account. For them to work properly, you need to add Global Values Collections (available ).

In our last section of code, we create IAM policies and attach them to our IAM group using two resources: aws_iam_policy and aws_iam_group_policy_attachment. We wrap both of these resources in section tags, so that we can create 1 or more sets of policies and policy attachments. For more information about creating safe IAM policies, check out on IAM policies using IaC.

The aws_iam_policy resource can be a complex one for developers to understand. IAM actions are not immediately obvious, and it is easy to screw up allowed resources. As a result, we have put strict controls around these inputs: giving guidance while preventing potentially harmful actions. How do we do that? and .

These Global Values Collection are available .

With these 4 Blueprints, your organization has a paved road to creating IAM that meets your expectations. Users can create users and attach policies to them, or create an assumable role and attach a policy, or create a group + users + policies. The sky is the limit! To try them out, sign up for your free account at , and create the Blueprints you want based on the GitHub links above.

IAM is complex, hard to scale, and dangerous when improperly implemented. Platform, identity, and security teams need ways to streamline IAM configuration that isn’t manual. With Resourcely’s configuration platform, give developers self-service IAM configuration that is safe, fast, and efficient. Get your own IAM factory by signing up for Resourcely for free:

get your account here.
aws_iam_group
aws_iam_group_membership
aws_iam_policy
aws_iam_group_policy_attachment
here
this good Hashi documentation
Global values
Guardrails
here
IAM users
IAM policies
IAM roles
IAM groups
https://portal.resourcely.io
get your account here.