IAM Policies

Important: These docs are for the outdated Jets 5 versions and below. For the latest Jets docs: docs.rubyonjets.com

Jets provides several ways to finely control the IAM policies associated with your Lambda functions. Here are the ways and their precedence:

  1. Function-specific IAM policy: highest precedence. Applies for the distinct Lambda function.
  2. Class-wide IAM policy: Applies for all Lambda functions for the class.
  3. Application-wide IAM policy: lowest precedence. Applies for all Lambda functions of the Jets application.

CloudFormation Controllers Build Setting

Important: In Jets v5, a single Lambda function handles all controller requests. You can only define IAM policies for all controllers in ApplicationController or in config/application.rb.

For jobs, you have the ability to control IAM Policies at the individual Lambda function level because Jets always deploys an distinct Lambda functions for each job method.

Function specific IAM policy

class PostsController < ApplicationController
  # ...
  iam_policy("s3", "sns")
  def show
    render json: {action: "show", id: params[:id]}
  end
end

Class-wide IAM policy

class PostsController < ApplicationController
  class_iam_policy(
    "dynamodb",
    {
      action: ["kinesis:*"],
      effect: "Allow",
      resource: "*",
    }
  )
end

Application-wide IAM policy

Jets.application.configure do |config|
  config.iam_policy = ["logs"]
end

Controllers vs Jobs

You only have fine-grain control of function IAM Policies for Jobs, not Controllers. This is because each Job method is deployed as a separate discrete Lambda Function. Whereas, a single Lambda Function is deployed for all controllers.

IAM Policies Inheritance

IAM policies defined at lower levels of precedence inherit and include the policies from the higher levels of precedence. This is done so you do not have to duplicate your IAM policies when you only need to add simple additional permissions. For example, the default application-wide IAM policy looks something like this:

[{
  action: ["logs:*"],
  effect: "Allow",
  resource: "arn:aws:logs:REGION:123456789:log-group:/aws/lambda/demo-dev-*",
}]

When you add a function specific IAM policy to a method:

class PostsController < ApplicationController
  # ...
  iam_policy("s3")
  def show
    render json: {action: "show", id: params[:id]}
  end
end

The resulting policy for the method will look something like this:

[{
  action: ["logs:*"],
  effect: "Allow",
  resource: "arn:aws:logs:REGION:123456789:log-group:/aws/lambda/demo-dev-*",
},{
  action: ["s3:*"],
  effect: "Allow",
  resource: "*",
}]

So the IAM policies are additive.

Application-wide: Override but Keep Default

If you would like to override the default Application-wide IAM policy in config/application.rb and still use the Jets default application IAM policy, then set config.iam_policy:

Jets.application.configure do |config|
  config.iam_policy = [
    {
      action: ["dynamodb:*"],
      effect: "Allow",
      resource: "arn:aws:dynamodb:#{Jets.aws.region}:#{Jets.aws.account}:table/#{Jets.project_namespace}*",
    }
  ]
end

This adds to the Jets default application IAM policy. This is useful because the default application IAM policy is dynamically calculated depending on what the resources are being provisioned. For example, using Shared Resources will add CloudFormation read permissions.

Application-wide: Override Entirely

If you wish to override the Jets default application IAM policy entirely, then set config.default_iam_policy:

Jets.application.configure do |config|
  config.default_iam_policy = [
    {
      action: ["logs:*", "s3:*"],
      effect: "Allow",
      resource: "*",
    }
  ]
end

Note, be careful using this advanced technique because it will override the IAM default policy entirely. It could prevent the Jets application from working right because it doesn’t have enough permissions. You must make sure that the policy has the right permissions.

IAM DSL Multiple Calls

When you call iam_policy multiple times, it appends permissions for that specific function. Example:

iam_policy("s3")
iam_policy("sns")

The same as:

iam_policy("s3", "sns")

IAM Policy Definition Styles

You might have noticed that the above iam_policy examples take a variety of different parameter styles. Jets allows for different IAM Policy Definition styles for your convenience. The iam_policy takes a single parameter or list of parameters. Jets expands each parameter in the list to Policy Statements in an IAM Policy Document.

Summary of the different expansion styles:

  1. Simple Statement: simplest
  2. Statement Hash
  3. Full Policy Hash: most complex

It is suggested that you start off with the simplest iam_policy definition style and use to the more complex styles when needed. Here are examples of each style with their expansion:

IAM Policy Simple Statement

iam_policy("s3", "sns")

Expands to:

Version: '2012-10-17'
Statement:
- Action:
  - s3:*
  Effect: Allow
  Resource: "*"
- Action:
  - sns:*
  Effect: Allow
  Resource: "*"

The notation with :* also works: iam_policy("s3:*", "sns:*").

IAM Policy Statement Hash

class_iam_policy(
  "dynamodb",
  {
    action: ["kinesis"],
    effect: "Allow",
    resource: "arn:aws:kinesis:#{Jets.aws.region}:#{Jets.aws.account}:stream/name*",
  }
)

Expands to:

Version: '2012-10-17'
Statement:
- Action:
  - dynamodb:*
  Effect: Allow
  Resource: "*"
- Action:
  - kinesis:*
  Effect: Allow
  Resource: "arn:aws:kinesis:us-west-2:1234567890:stream/name*"

Note, the resource values are examples.

IAM Policy Full Policy Hash

iam_policy(
  version: "2012-10-17",
  statement: [{
    action: ["lambda:*"],
    effect: "Allow",
    resource: "*"
  }]
)

Expands to:

Version: '2012-10-17'
Statement:
- Action:
  - lambda:*
  Effect: Allow
  Resource: "*"

IAM Policy Definition Expansion

What’s important to understand is that ultimately, the iam_policy definition expands to include an IAM Policy document that looks something like this:

PolicyDocument:
  Version: '2012-10-17'
  Statement:
  - Action:
    - s3:*
    Effect: Allow
    Resource: "*"

The expanded IAM Policy documents gets included into the CloudFormation template and get associated with the desired Lambda functions. More details on what a raw IAM Policy document looks like can be found at:

One Lambda For All Conrollers

For controllers, Jets builds and uses only a single Lambda Function. Hence the iam_policy definitions cannot be applied at the individual function level. There is only a single Lambda Function.

In this case, Jets will print and warning and tell you to either use the Application-wide IAM policy in config/application.rb. You also define your IAM policy at the ApplicationController.

Lambda Function vs User Deploy IAM Policies

The IAM Policies docs on this page refer to the IAM policy associated with your Lambda Execution Role. These permissions control what AWS resources your Lambda functions have access to. This is different from the IAM role you use to deploy a Jets application, which is typically your IAM User permissions. If you are looking for the minimal IAM Policy to deploy a Jets application for your IAM user, check out Minimal Deploy IAM Policy.