Learn More

How Judoscale Uses AWS IAM Roles

Like any other service that interfaces or interacts with your Amazon ECS clusters and services, Judoscale adheres to AWS' role-based authorization and access-management system in IAM. This document serves as a quick brief on why and how we scaffold up the IAM roles that we do when you install Judoscale into your application. Zooming out a bit, recall that installing Judoscale requires an application-level change (installing a gem), and a platform integration so that we can actually scale your application!

Read Access

When you first sign up for an account on Judoscale and begin to configure an AWS team, we need read access to your ECS services (across all clusters) — this is simply so that we know which ones to present to you in the UI when we ask, "Which service do you want to setup autoscaling for?" We request this read access as a CloudFormation template that you can run automatically, if you so desire. In its entirety, and for explicit transparency, this is that CloudFormation script we prompt you to run:

{
  "AWSTemplateFormatVersion": "2010-09-09",
  "Description": "Create an IAM role for Judoscale. Grants read-only access to all ECS resources and some account metadata.",
  "Parameters": {
    "RoleName": {
      "Type": "String",
      "Description": "IAM Role Name"
    },
    "JudoscaleAccountId": {
      "Type": "String",
      "Description": "Judoscale Account ID"
    },
    "ExternalId": {
      "Type": "String",
      "Description": "The unique ID for your team provided by Judoscale"
    }
  },
  "Resources": {
    "JudoscaleIAMRole": {
      "Type": "AWS::IAM::Role",
      "Properties": {
        "RoleName": { "Ref": "RoleName" },
        "AssumeRolePolicyDocument": {
          "Version": "2012-10-17",
          "Statement": [
            {
              "Effect": "Allow",
              "Principal": {
                "AWS": {
                  "Fn::Sub": "arn:aws:iam::${JudoscaleAccountId}:root"
                }
              },
              "Action": ["sts:AssumeRole"],
              "Condition": {
                "StringEquals": {
                  "sts:ExternalId": { "Ref": "ExternalId" }
                }
              }
            }
          ]
        },
        "ManagedPolicyArns": [
          {
            "Ref": "JudoscaleIAMPolicy"
          }
        ]
      }
    },
    "JudoscaleIAMPolicy": {
      "Type": "AWS::IAM::ManagedPolicy",
      "Properties": {
        "Description": "ECS Read-only permissions policy for Judoscale role",
        "PolicyDocument": {
          "Version": "2012-10-17",
          "Statement": [
            {
              "Effect": "Allow",
              "Action": [
                "ecs:Describe*",
                "ecs:List*",
                "iam:ListAccountAliases",
                "account:ListRegions"
              ],
              "Resource": "*"
            }
          ]
        }
      }
    }
  },
  "Outputs": {
    "RoleARN": {
      "Description": "ARN of the Judoscale IAM role",
      "Value": {
        "Fn::GetAtt": ["JudoscaleIAMRole", "Arn"]
      }
    }
  }
}

This script sets up two, and exactly two, constructs on your AWS account.

First, an AWS::IAM::Role. This is a role on your account that is assumable by our Judoscale service AWS account. Additionally, we use an AssumeRolePolicy to add another layer of protection: even if the account-level access management is correct, the correct external key must match before the role can be assumed. We store this key separately from our account credentials and each is unique per Judoscale client. This is standard practice for cross-account role assumption. Finally, we apply a managed IAM policy to this role. This is the JudoscaleIAMPolicy we’re about to create!

Second, we create the aforementioned managed IAM policy, labeled JudoscaleIAMPolicy. Here we grant permission to four actions:

  • ecs:List* allows us to see all of the clusters and services running in your account’s Amazon ECS setup
  • ecs:Describe* allows us to see the details for any of the clusters / services (including their current scale)
  • account:ListRegions allows us to figure out if you have any clusters or services in regions outside of your default (Judoscale supports all regions concurrently!)
  • iam:ListAccountAliases simply allows us to give your team a more friendly default name in Judoscale when you sign up 🙂

And that’s it! These four actions, powered by the Judoscale role, allow us to get an initial sense of your services and clusters to simply get your Judoscale account setup properly. This screen, in particular, would not be possible without those permissions... and that would make Judoscale pretty unhelpful!

Screenshot of the Judoscale UI prompting the user to select a Service from their ECS account to link into Judoscale

If your team doesn’t use CloudFormation and/or prefers Terraform instead, let us know! We’d be happy to help you figure out the right solution for your ECS setup. We’re always available via email and our office hours!

Once your clusters and services are imported, linked, and you set some initial autoscaling settings, we’ll need more permissions. We setup read permissions above, but to actually execute scaling for your application, we’ll need write permissions. Luckily, these are simpler!

Write Access

Once you have an account and an ECS team setup in Judoscale, you’ll want to activate autoscaling. That’s the whole point, right? Right! Alas, to actually execute a scale change for your service(s), we’ll need to setup some basic write permissions. The good news is that these are simpler and much more restricted than our read permissions.

Once again in its entirety, and for explicit transparency, this is the CloudFormation script we prompt that configures the write permissions:

{
  "AWSTemplateFormatVersion": "2010-09-09",
  "Description": "Grant update access to Judoscale for autocaling selected ECS services.",
  "Parameters": {
    "RoleNames": {
      "Type": "CommaDelimitedList",
      "Description": "Roles that will be granted access"
    },
    "Services": {
      "Type": "CommaDelimitedList",
      "Description": "The list of ECS service ARNs to allow in the policy"
    }
  },
  "Resources": {
    "JudoscaleUpdatePolicy": {
      "Type": "AWS::IAM::ManagedPolicy",
      "Properties": {
        "Description": "Policy for updating specific ECS services",
        "Roles": { "Ref": "RoleNames" },
        "PolicyDocument": {
          "Version": "2012-10-17",
          "Statement": [
            {
              "Effect": "Allow",
              "Action": "ecs:UpdateService",
              "Resource": { "Ref": "Services" }
            }
          ]
        }
      }
    }
  }
}

This script sets up a single new managed IAM policy and binds it to the same IAM role we created above. Ultimately that means the JudoscaleIAMRole we created initially to read ECS information will have two policies: one for reading and this one for writing.

The key note here is that the action we’re enabling is ecs:UpdateService. We do wish there was a more fine-grained permission we could call, but unfortunately this is the finest-grain action we can grant that allows us to update the task count on a service.

The other key note is that we’re passing in a specific list of resources that the permission applies to. This list will only be the services you’ve opted in to autoscaling with Judoscale. We don’t need (and won’t ask for) update permissions to services you aren’t actively attempting to use Judoscale for. There’s no need!

If your team doesn’t use CloudFormation and/or prefers Terraform instead, let us know! We’d be happy to help you figure out the right solution for your ECS setup. We’re always available via email and our office hours!

With this permission set, we’ll be fully able to autoscale your service.

We hope this explainer gives you a little more context into the specific permissions we request on AWS accounts! We look forward to scaling your next ECS Service 🙂

Previous
Schedules and Pre-Scaling