This guide will walk you through integrating Common Fate with Amazon Web Services (AWS). By the end of this guide, you’ll have a functioning integration with Common Fate, allowing it to read your AWS account inventory and provision access to entitlements.

AWS Setup

By default, two IAM roles are used—one for reading available entitlements and another for assigning access. Although you can combine these into a single role, it is recommended to use two separate roles for better visibility.

You’ll need access to a role with the ability to create IAM roles and policies in your AWS account.

The permissions that the read role uses are:

  • iam:GetRole
  • iam:GetSAMLProvider
  • iam:ListAttachedRolePolicies
  • iam:ListRolePolicies
  • identitystore:ListUsers
  • identitystore:ListGroups
  • identitystore:ListGroupMemberships
  • organizations:DescribeAccount
  • organizations:DescribeOrganization
  • organizations:ListAccounts
  • organizations:ListAccountsForParent
  • organizations:ListOrganizationalUnitsForParent
  • organizations:ListRoots
  • organizations:ListTagsForResource
  • sso:DescribePermissionSet
  • sso:ListAccountAssignments
  • sso:ListPermissionSets
  • sso:ListTagsForResource

The permissions that the provision role uses are:

  • sso:CreateAccountAssignment
  • sso:DeleteAccountAssignment
  • sso:DescribeAccountAssignmentCreationStatus
  • sso:DescribeAccountAssignmentDeletionStatus

Additionally, if you choose to allow Common Fate to manage access to your AWS Organizations Management Account, the following permissions are added:

  • iam:CreateRole
  • iam:AttachRolePolicy

We have developed a reference integration Terraform module which deploys the following resources:

  • A role used by Common Fate to read AWS IDC resources
  • A role used by Common Fate to provision access in AWS IDC

This Terraform module requires sensitive AWS account-level permissions. Deploying this module in a separate Terraform root module is recommended to avoid combining privileged access to both your Common Fate deployment and your AWS account.

To deploy the reference integration, create a new Terraform root module with the following module:

module "common_fate_aws_roles" {
  source                                = "common-fate/common-fate-deployment/aws//modules/aws-idc-integration/iam-roles"
  version                               = "1.22.0"
  common_fate_aws_account_id            = "<Common Fate AWS Account ID>"
}

Configuring Common Fate

In this section, you will register the AWS integration with your Common Fate deployment. At the end of this section, you should have Common Fate reading AWS resources and see them inside the web dashboard. You’ll need to have set up the Common Fate Application Configuration repository using our Terraform provider.

Inside your Application Configuration repository, add the following module:

# config/main.tf (in the Config folder, see: https://github.com/common-fate/byoc-aws-starter-config/tree/main/config)

resource "commonfate_aws_idc_integration" "sandbox" {
  name              = "AWS"
  reader_role_arn   = "<ARN of the idc-reader-role>"
  sso_instance_arn  = "<The AWS SSO Instance ARN>"
  sso_region        = "<The region the AWS SSO instance is deployed to>"
  identity_store_id = "<AWS identity store ID>"
}

Apply the changes. If the apply succeeds, you should see the integration appear on the settings page in the web dashboard, and AWS resources should populate within 10 minutes.

If after 10 minutes you do not see resources appear, check the logs of the common-fate-prod-control-plane service in ECS. You can search for aws_sync to filter for structured logs containing data about the AWS integration.

Provisioning access to AWS

To grant and revoke access to AWS, Common Fate uses a service called the Provisioner. The Provisioner is an internal service and is not user-facing. In default deployments, there is a provisioner builtin to the main stack which can be configured using blocks.

To configure the builtin Provisioner for AWS IDC, add the provisioner_aws_idc_config config block as per the example below:

module "common-fate-deployment" {
  source = "common-fate/common-fate-deployment/aws"
  version = "1.22.0"

  provisioner_aws_idc_config = {
    idc_instance_arn      = "<Your Identity Center Instance ARN>"
    idc_region            = "<Your Identity Center Region>"
    role_arn              = "<ARN of the Common Fate Provision IAM role>"
    idc_identity_store_id = "<Your Identity Store ID>"
  }
}

You’ll additionally need to add the following Provisioner registration inside your Application Configuration repository:

# config/main.tf (in the Config folder, see: https://github.com/common-fate/byoc-aws-starter-config/tree/main/config)

resource "commonfate_webhook_provisioner" "aws" {
  url = "http://common-fate-prod-builtin-provisioner.common-fate-prod-builtin.internal:9999"
  capabilities = [
    {
      target_type = "AWS::Account"
      role_type   = "AWS::IDC::PermissionSet"
      belonging_to = {
        type = "AWS::Organization"
        id   = "<Your AWS Organization ID>"
      }
    },
  ]
}

You can now create an access workflow and availabilities:

# config/aws_idc.tf, see: https://github.com/common-fate/byoc-aws-starter-config/tree/main/config

resource "commonfate_access_workflow" "aws" {
  name                     = "aws"
  access_duration_seconds  = 60 * 60 * 2
  try_extend_after_seconds = 60 * 60
  priority                 = 1
}

resource "commonfate_aws_account_selector" "select_all" {
  id                  = "select_all_aws"
  name                = "Select All AWS"
  aws_organization_id = "<Your AWS Organization ID>"
  when                = "true"
}

resource "commonfate_aws_idc_account_availabilities" "aws" {
  workflow_id             = commonfate_access_workflow.aws.id
  aws_permission_set_arn  = <The permission set ARN to grant access to>
  aws_account_selector_id = commonfate_aws_account_selector.select_all.id
  aws_identity_store_id   = "<Your Identity Store ID>"

}

AWS account selectors

To make AWS accounts available for Just-In-Time (JIT) access you can add a commonfate_aws_account_selector Selector resource to your Common Fate application Terraform code. As shown below, the when clause in the resource is a Cedar expression. You can use any Cedar operator in the when clause, such as && and || to combine conditions.

You’ll need to use the commonfate_aws_account_selector in conjunction with a commonfate_aws_idc_account_availabilities and commonfate_access_workflow resources, as shown above.

We’ve included some examples below.

Select an account by ID

resource "commonfate_aws_account_selector" "example" {
  id                  = "example"
  name                = "Example"
  aws_organization_id = "<Your AWS Organization ID>"
  when                = <<EOT
  resource == AWS::Account::"123456789012"
  EOT
}

Select multiple accounts by ID

resource "commonfate_aws_account_selector" "example" {
  id                  = "example"
  name                = "Example"
  aws_organization_id = "<Your AWS Organization ID>"
  when                = <<EOT
  resource == AWS::Account::"123456789012"  || resource == AWS::Account::"222333444555"
  EOT
}

Select an account based on a naming pattern

Select accounts with a name ending in -prod:

resource "commonfate_aws_account_selector" "example" {
  id                  = "example"
  name                = "Example"
  aws_organization_id = "<Your AWS Organization ID>"
  when                = <<EOT
  resource.name like "*-prod"
  EOT
}

Select accounts with a name beginning with Develop:

resource "commonfate_aws_account_selector" "example" {
  id                  = "example"
  name                = "Example"
  aws_organization_id = "<Your AWS Organization ID>"
  when                = <<EOT
  resource.name like "Develop*"
  EOT
}

Select accounts belonging to a particular Organizational Unit (OU)

resource "commonfate_aws_account_selector" "example" {
  id                  = "example"
  name                = "Example"
  aws_organization_id = "<Your AWS Organization ID>"
  when                = <<EOT
  resource in AWS::OrgUnit::"ou-123abc"
  EOT
}

Select an account containing a tag key

Selects AWS accounts which have a tag with a key of production:

resource "commonfate_aws_account_selector" "example" {
  id                  = "example"
  name                = "Example"
  aws_organization_id = "<Your AWS Organization ID>"
  when                = <<EOT
  resource.tag_keys.contains("production")
  EOT
}

Select an account containing a tag key and value

Selects AWS accounts which have a tag with key department and value engineering (department=engineering):

resource "commonfate_aws_account_selector" "example" {
  id                  = "example"
  name                = "Example"
  aws_organization_id = "<Your AWS Organization ID>"
  when                = <<EOT
  resource.tags.contains({key: "department", value: "engineering"})
  EOT
}

Minimum IAM permissions to provision the AWS Integration roles

Rather than using an administrative access role to provision the AWS integration roles, you can use the following policy which will permit deploying and updating the integration roles:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowPolicyCreateAndUpdate",
      "Effect": "Allow",
      "Action": [
        "iam:CreatePolicy",
        "iam:GetPolicyVersion",
        "iam:GetPolicy",
        "iam:ListPolicyVersions",
        "iam:CreatePolicyVersion"
      ],
      "Resource": "*"
    },
    {
      "Sid": "AllowRoleUpdate",
      "Effect": "Allow",
      "Action": [
        "iam:ListRolePolicies",
        "iam:CreateRole",
        "iam:ListAttachedRolePolicies",
        "iam:TagRole",
        "iam:GetRole",
        "iam:ListInstanceProfilesForRole",
        "iam:AttachRolePolicy",
        "iam:UpdateRole",
        "iam:PutRolePolicy",
        "iam:GetRolePolicy",
        "iam:UpdateRoleDescription"
      ],
      "Resource": [
        "arn:aws:iam::<YOUR MANAGEMENT ACCOUNT ID>:role/common-fate-prod-idc-reader-role",
        "arn:aws:iam::<YOUR MANAGEMENT ACCOUNT ID>:role/common-fate-prod-idc-provisioner-role"
      ]
    }
  ]
}