Tutorials
Tutorials
July 19, 2022

How To Manage Scalr RBAC At Scale Using The Scalr Provider

By
Ryan Fee

As mentioned in the last blog on granular RBAC, the way Scalr does IAM is a key differentiator from the competition. Scalr allows for custom roles and access policies at every scope in the product making the RBAC model extremely flexible. More flexibility and options is a good thing, but you want to make sure you're managing RBAC in a scalable way, which is done by automating it in the Scalr provider.

Automating RBAC is one step in a larger automation effort to “vend” Scalr environments, which is a best practice when onboarding new applications/teams. Typically, this involves creating a Scalr module(s) which will create teams and associate a role with the team. Then creating an environment and linking provider credentials, policies, teams, variables, and modules to the environment. This allows the teams to log into a pre configured environment and solely focus on their Terraform deployments. (If you already have a vending machine for you cloud accounts/subscriptions, you can extend that to Scalr as well) We’re going to go into some technical detail on how to automate Scalr RBAC.

There are a few objects that need to be managed:

  • Roles - The permissions a team or user has.
  • Users/Teams -  The users or teams who have access to Scalr.
  • Access Policies - The object that links a team to a role and defines the scope at which the linking happens.

In this tutorial, we’ll assign a team with a custom role to an environment using the Scalr provider. We are going to go through each piece of code first to help you understand how it is used and closer to the bottom of the blog you will see the end result which is a main.tf with all of the code in it.

First, let’s set up the provider in a providers.tf. Get the provider information from https://registry.scalr.io/:

terraform {
  required_providers {
      scalr = {
          source = "registry.scalr.io/scalr/scalr"
          version= "1.0.0-rc32"
      }
  }
}

Let's start constructing our main.tf. First we’ll add a local to define the account:

##Define your account and environment ID##
locals {
  account_id  = "<your-account-id>"
}

Next add some data sources to pull IDs:

data "scalr_environment" "example" {
  name = "Research-Development"
  account_id = local.account_id
}

data "scalr_role" "example" {
  name = "account-min-access"
}

Now, let's create a predefined set of roles that all teams will be assigned. The majority of the time, roles are created upfront and then modified as needed, this is not something that is updated frequently. For these purposes, we’ll create a role, which is used for the applications teams who need to create Terraform runs only.

resource "scalr_role" "planner" {
  name         = "Plan Creation"
  account_id = local.account_id
  description = "Ability to create Terraform runs, but not approve"

  permissions = [
    "accounts:read",
    "environments:read",
    "runs:cancel",
    "runs:create",
    "workspaces:read",
    "workspaces:set-schedule",
    "workspaces:update"
  ]
}

Next, let’s add a team. Normally, you would likely want to use Terraform variables to reuse the code, but for these purposes we’ll hardcode and use locals for the account/environment ID. Further down in the blog you will see all of the code with the locals defined:

resource "scalr_iam_team" "example" {
  name        = "myfirstscalrteam"
  description = "Scalr Example Team"
  account_id  = local.account_id
}

Lastly, we need to create an access policy to attach the role to a team and link the team to a scope in Scalr. All teams must have at least the ‘account-min-access’ role at the account scope to have access to Scalr, and then they can be added to the environment in which they will create runs:

##Link to the account first with minimal access##
resource "scalr_access_policy" "team_min_access_to_acc_scope" {
  subject {
    type = "team"
    id = scalr_iam_team.example.id
  }
  scope {
    type = "account"
    id = local.account_id
  }

  role_ids = [
    data.scalr_role.example.id
  ]
}

##Link to an environment with role created earlier##
resource "scalr_access_policy" "team_access_to_env_scope" {
   subject {
     type = "team"
     id = scalr_iam_team.example.id
   }
   scope {
     type = "environment"
     id = data.scalr_environment.example.id
   }

   role_ids = [
     scalr_role.planner.id
   ]
}

All together, this looks like:

##Define your account ID##
locals {
  account_id  = "acc-sscctbisjkl35b8"
}

data "scalr_environment" "example" {
  name = "Research-Development"
  account_id = local.account_id
}

data "scalr_role" "example" {
  name = "account-min-access"
}

resource "scalr_role" "planner" {
  name         = "Plan Creation"
  account_id = local.account_id
  description = "Ability to create Terraform runs, but not approve"

  permissions = [
    "accounts:read",
    "environments:read",
    "runs:cancel",
    "runs:create",
    "workspaces:read",
    "workspaces:set-schedule",
    "workspaces:update"
  ]
}

resource "scalr_iam_team" "example" {
  name        = "myfirstscalrteam"
  description = "Scalr Example Team"
  ##Add your own account ID##
  account_id  = local.account_id
}

##Link to the account first with minimal access##
resource "scalr_access_policy" "team_min_access_to_acc_scope" {
  subject {
    type = "team"
    id = scalr_iam_team.example.id
  }
  scope {
    type = "account"
    id = local.account_id
  }

  role_ids = [
    data.scalr_role.example.id
  ]
}

##Link to an environment with role created earlier##
resource "scalr_access_policy" "team_access_to_env_scope" {
  subject {
    type = "team"
    id = scalr_iam_team.example.id
  }
  scope {
    type = "environment"
    id = data.scalr_environment.example.id
  }

  role_ids = [
    scalr_role.planner.id
  ]
}

To execute this, you have a couple of options:

I’m choosing to use a VCS backed workspace:

Upon execution we see that the four resources have been created:

And can verify this by looking at each object in the account scope:

Team creation:

Role creation:

Account scope access policy:

Environment scope access policy:

What's next

Now that you have learned the basics of how to use the Scalr provider to manage IAM, you can take it to the next level with these few suggestions:

  • Separate out the role resource into its own module. You most likely won’t create a new role every time a team onboards, instead reuse common roles. We kept it in the same main.tf code for example purposes in this blog.
  • Use this code as a starting point for a module and use variable files to make the code reusable.
  • Incorporate this into a vending machine. As teams onboard into Scalr they usually get their own environment. Add more code which will create the environments, link objects (teams, policies, variables, and more) to fully automate your onboarding process.

Look for more examples on other concepts coming soon.

Note: While this blog references Terraform, everything mentioned in here also applies to OpenTofu. New to OpenTofu? It is a fork of Terraform 1.5.7 as a result of the license change from MPL to BUSL by HashiCorp. OpenTofu is an open-source alternative to Terraform that is governed by the Linux Foundation. All features available in Terraform 1.5.7 or earlier are also available in OpenTofu. Find out the history of OpenTofu here.

Start using the Terraform platform of the future.

A screenshot of the modules page in the Scalr Platform