

# Managing permissions and access for a multi-account architecture
<a name="managing-permissions"></a>

**Topics**
+ [Engineering cultural considerations](engineering-cultural-considerations.md)
+ [Creating permission sets](creating-permission-sets.md)
+ [Creating a permissions boundary](creating-a-permissions-boundary.md)
+ [Managing permissions for individuals](managing-permissions-for-individuals.md)

# Engineering cultural considerations
<a name="engineering-cultural-considerations"></a>

One of the pillars of the AWS Well-Architected Framework is Operational Excellence. Teams must understand the [operating model](https://docs.aws.amazon.com/wellarchitected/latest/operational-excellence-pillar/operating-model.html) and their part in achieving your business outcomes. Teams can focus on achieving shared goals when they understand their responsibilities, can take ownership, and know how decisions are made.

With early stage companies that are building quickly, everyone on the team performs multiple roles. It isn't uncommon for these users to have highly privileged access to the entire AWS account. As companies grow, they often want to follow the principle of *least privilege* and only grant permissions that are required for the user to do their job. To help you limit scope, you can use [AWS Identity and Access Management Access Analyzer](https://docs.aws.amazon.com/IAM/latest/UserGuide/what-is-access-analyzer.html) to see what permissions a user or IAM role is actually using, allowing you to remove any excess permissions.

It can be challenging to decide who in your company has permissions to create IAM roles. This is commonly a vector for escalating privileges. Escalating privileges is when a user can expand their own permissions or scope of access. For example, if a user has limited permissions but can create new IAM roles, that user could escalate their privileges by creating and assuming a new IAM role that has the `AdministratorAccess` managed policy applied.

Some companies limit IAM role provisioning to a centralized team of trusted individuals. The downside of this approach is that this team can quickly become a bottleneck because almost all AWS services require an IAM role to operate. As an alternative, you can use [permissions boundaries](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_boundaries.html) to delegate IAM access to only users who are developing, testing, launching, and managing your cloud infrastructure. For example policies, see [Example Permission Boundaries](https://github.com/aws-samples/example-permissions-boundary) (GitHub).

Development operations (DevOps) teams, also known as *platform* teams, often need to balance self-service capabilities for multiple internal development teams against application operational stability. Fostering an engineering culture that embraces autonomy, mastery, and purpose in the workplace can help motivate teams. Engineers want to do their work in a self-directed manner, without relying on others to do things for them. If DevOps teams can implement self-service solutions, this also reduces the amount of time others depend on them to get things done.

# Creating permission sets
<a name="creating-permission-sets"></a>

You can manage AWS account access by using [permission sets](https://docs.aws.amazon.com/singlesignon/latest/userguide/permissionsetsconcept.html) in AWS IAM Identity Center. A *permission set* is a template that helps you deploy one or more IAM policies to multiple AWS accounts. When you assign a permission set to an AWS account, IAM Identity Center creates an IAM role and attaches your IAM policies to that role. For more information, see [Create and manage permission sets](https://docs.aws.amazon.com/singlesignon/latest/userguide/permissionsets.html) (IAM Identity Center documentation).

AWS recommends creating permission sets that map to the different personas in your business.

**Topics**
+ [Billing permission set](#billing-permission-set)
+ [Developer permission set](#developer-permission-set)
+ [Production permission set](#production-permission-set)

The following permissions sets are snippets from an AWS CloudFormation template. You should use this code as a starting point and customize it for your business. For more information about CloudFormation templates, see [Learn template basics](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/gettingstarted.templatebasics.html) (CloudFormation documentation).

## Billing permission set
<a name="billing-permission-set"></a>

The finance team uses **BillingAccessPermissionSet** to view the AWS Billing console dashboard and AWS Cost Explorer in each account.

```
BillingAccessPermissionSet:
  Type: "AWS::SSO::PermissionSet"
  Properties:
    Description: Access to Billing and Cost Explorer
    InstanceArn: !Sub "arn:${AWS::Partition}:sso:::instance/ssoins-instanceId"
    ManagedPolicies:
      - !Sub "arn:${AWS::Partition}:iam::aws:policy/job-function/Billing"
    Name: BillingAccess
    SessionDuration: PT8H
    RelayStateType: https://console.aws.amazon.com/billing/home
```

## Developer permission set
<a name="developer-permission-set"></a>

The engineering team uses **DeveloperAccessPermissionSet** to access non-production accounts.

```
DeveloperAccessPermissionSet:
  Type: "AWS::SSO::PermissionSet"
  Properties:
    Description: Access to provision resources through CloudFormation
    InlinePolicy: !Sub |-
      {
        "Version": "2012-10-17", 		 	 	 		 	 	 
        "Statement": [
          {
            "Effect": "Allow",
            "Action": "iam:PassRole",
            "Resource": "arn:${AWS::Partition}:iam::*:role/CloudFormationRole",
            "Condition": {
              "StringEquals": {
                "aws:ResourceAccount": "${!aws:PrincipalAccount}",
                "iam:PassedToService": "cloudformation.${AWS::URLSuffix}"
              }
            }
          },
          {
            "Effect": "Allow",
            "Action": [
              "cloudformation:ContinueUpdateRollback",
              "cloudformation:CreateChangeSet",
              "cloudformation:CreateStack",
              "cloudformation:DeleteStack",
              "cloudformation:RollbackStack",
              "cloudformation:UpdateStack"
            ],
            "Resource": "arn:${AWS::Partition}:cloudformation:*:*:stack/app-*",
            "Condition": {
              "ArnLike": {
                "cloudformation:RoleArn": "arn:${AWS::Partition}:iam::${!aws:PrincipalAccount}:role/CloudFormationRole"
              },
              "Null": {
                "cloudformation:ImportResourceTypes": true
              }
            }
          },
          {
            "Effect": "Allow",
            "Action": [
              "cloudformation:CancelUpdateStack",
              "cloudformation:DeleteChangeSet",
              "cloudformation:DetectStackDrift",
              "cloudformation:DetectStackResourceDrift",
              "cloudformation:ExecuteChangeSet",
              "cloudformation:TagResource",
              "cloudformation:UntagResource",
              "cloudformation:UpdateTerminationProtection"
            ],
            "Resource": "arn:${AWS::Partition}:cloudformation:*:*:stack/app-*"
          },
          {
           "Effect": "Allow",
            "Action": [
              "cloudformation:CreateUploadBucket",
              "cloudformation:ValidateTemplate",
              "cloudformation:EstimateTemplateCost"
            ],
            "Resource": "*"
          }
        ]
      }
    InstanceArn: !Sub "arn:${AWS::Partition}:sso:::instance/ssoins-instanceId"
    ManagedPolicies:
      - !Sub "arn:${AWS::Partition}:iam::aws:policy/AWSServiceCatalogEndUserFullAccess"
      - !Sub "arn:${AWS::Partition}:iam::aws:policy/AWSBillingReadOnlyAccess"
      - !Sub "arn:${AWS::Partition}:iam::aws:policy/AWSSupportAccess"
      - !Sub "arn:${AWS::Partition}:iam::aws:policy/ReadOnlyAccess"
    Name: DeveloperAccess
    SessionDuration: PT8H
```

## Production permission set
<a name="production-permission-set"></a>

The engineering team uses **ProductionPermissionSet** to access production accounts. This permission set has limited, view-only access.

```
ProductionPermissionSet:
  Type: "AWS::SSO::PermissionSet"
  Properties:
    Description: Access to production accounts
    InlinePolicy: !Sub |-
      {
        "Version": "2012-10-17", 		 	 	 		 	 	 
        "Statement": [
          {
            "Effect": "Allow",
            "Action": "iam:PassRole",
            "Resource": "arn:${AWS::Partition}:iam::*:role/CloudFormationRole",
            "Condition": {
              "StringEquals": {
                "aws:ResourceAccount": "${!aws:PrincipalAccount}",
                "iam:PassedToService": "cloudformation.${AWS::URLSuffix}"
              }
            }
          },
          {
            "Effect": "Allow",
            "Action": "cloudformation:ContinueUpdateRollback",
            "Resource": "arn:${AWS::Partition}:cloudformation:*:*:stack/app-*",
            "Condition": {
              "ArnLike": {
                "cloudformation:RoleArn": "arn:${AWS::Partition}:iam::${!aws:PrincipalAccount}:role/CloudFormationRole"
              }
            }
          },
          {
            "Effect": "Allow",
            "Action": "cloudformation:CancelUpdateStack",
            "Resource": "arn:${AWS::Partition}:cloudformation:*:*:stack/app-*"
          }
        ]
      }
    InstanceArn: !Sub "arn:${AWS::Partition}:sso:::instance/ssoins-instanceId"
    ManagedPolicies:
      - !Sub "arn:${AWS::Partition}:iam::aws:policy/AWSBillingReadOnlyAccess"
      - !Sub "arn:${AWS::Partition}:iam::aws:policy/AWSSupportAccess"
      - !Sub "arn:${AWS::Partition}:iam::aws:policy/job-function/ViewOnlyAccess"
    Name: ProductionAccess
    SessionDuration: PT2H
```

# Creating a permissions boundary
<a name="creating-a-permissions-boundary"></a>

After you deploy the permission sets, you establish a permissions boundary. This *permissions boundary* is a mechanism to delegate IAM access to only users who are developing, testing, launching, and managing your cloud infrastructure. Those users can perform only the actions that are permitted by the policy and the permissions boundary.

You can define the permissions boundary in an AWS CloudFormation template and then use CloudFormation StackSets to deploy the template into multiple accounts. This helps you establish and maintain standardized policies across your organization with a single operation. For more information and instructions, see [Working with AWS CloudFormation StackSets](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/what-is-cfnstacksets.html) (CloudFormation documentation).

The following CloudFormation template provisions an IAM role and creates an IAM policy that acts as a permission boundary. Using a stack set, you can deploy this template to all of the member accounts in your organization.

```
CloudFormationRole:
  Type: "AWS::IAM::Role"
  Properties:
    AssumeRolePolicyDocument:
      Version: "2012-10-17"
      Statement:
        Effect: Allow
        Principal:
          Service: !Sub "cloudformation.${AWS::URLSuffix}"
        Action: "sts:AssumeRole"
        Condition:
          StringEquals:
            "aws:SourceAccount": !Ref "AWS::AccountId"
    Description: !Sub "DO NOT DELETE - Used by CloudFormation. Created by CloudFormation ${AWS::StackId}"
    ManagedPolicyArns:
      - !Sub "arn:${AWS::Partition}:iam::aws:policy/AdministratorAccess"
    PermissionsBoundary: !Ref DeveloperBoundary
    RoleName: CloudFormationRole

DeveloperBoundary:
  Type: "AWS::IAM::ManagedPolicy"
  Properties:
    Description: Permission boundary for developers
    ManagedPolicyName: PermissionsBoundary
    PolicyDocument:
      Version: "2012-10-17"
      Statement:
        - Sid: AllowModifyIamRolesWithBoundary
          Effect: Allow
          Action:
            - "iam:AttachRolePolicy"
            - "iam:CreateRole"
            - "iam:DeleteRolePolicy"
            - "iam:DetachRolePolicy"
            - "iam:PutRolePermissionsBoundary"
            - "iam:PutRolePolicy"
          Resource: !Sub "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/app/*"
          Condition:
            ArnEquals:
              "iam:PermissionsBoundary": !Sub "arn:${AWS::Partition}:iam::${AWS::AccountId}:policy/PermissionsBoundary"
        - Sid: AllowModifyIamRoles
          Effect: Allow
          Action:
            - "iam:DeleteRole"
            - "iam:TagRole"
            - "iam:UntagRole"
            - "iam:UpdateAssumeRolePolicy"
            - "iam:UpdateRole"
            - "iam:UpdateRoleDescription"
          Resource: !Sub "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/app/*"
        - Sid: OverlyPermissiveAllowedServices
          Effect: Allow
          Action:
            - "lambda:*"
            - "apigateway:*"
            - "events:*"
            - "s3:*"
            - "logs:*"
          Resource: "*"
```

The **CloudFormationRole** role, **PermissionsBoundary** policy, and the **DeveloperAccess** permission set work together to grant the following permissions:
+ Users have read-only access to most AWS services, through the **ReadOnlyAccess** AWS managed policy.
+ Users have access to open support cases, through the **AWSSupportAccess** AWS managed policy.
+ Users have read-only access to the AWS Billing console dashboard, through the **AWSBillingReadOnlyAccess** AWS managed policy.
+ Users are able to provision products from Service Catalog, through the **AWSServiceCatalogEndUserFullAccess** AWS managed policy.
+ Users are able to validate and estimate the cost of any CloudFormation template, through the inline policy.
+ By using the **CloudFormationRole** IAM role, users are able to create, update, or delete any CloudFormation stack that starts with **app/**.
+ Users are able to use CloudFormation to create, update, or delete IAM roles that start with **app/**. The **PermissionsBoundary** IAM policy prevents users from escalating their privileges.
+ Users can provision AWS Lambda, Amazon EventBridge, Amazon CloudWatch, Amazon Simple Storage Service (Amazon S3), and Amazon API Gateway resources only by using CloudFormation.

The following image shows how an authorized user, such as a developer, can create a new IAM role in a member account by using the permissions sets, IAM roles, and permissions boundaries described in this guide:

1. The user authenticates in IAM Identity Center and assumes the **DeveloperAccess** IAM role.

1. The user initiates the `cloudformation:CreateStack` action and assumes the **CloudFormationRole** IAM role.

1. The user initiates the `iam:CreateRole` action and uses CloudFormation to create a new IAM role.

1. The **PermissionsBoundary** IAM policy is applied to the new IAM role.



![\[User creating an IAM role that is subject to the permissions boundary in the member account\]](http://docs.aws.amazon.com/prescriptive-guidance/latest/transitioning-to-multiple-aws-accounts/images/2_create-iam-role.png)


The **CloudFormationRole** role has the [AdministratorAccess](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_job-functions.html#jf_administrator) managed policy attached, but due to the **PermissionsBoundary** IAM policy, the **CloudFormationRole** role's effective permissions become equal to the **PermissionsBoundary** policy. The **PermissionsBoundary** policy references itself when allowing the `iam:CreateRole` action, which ensures that roles can be created only if the permissions boundary is applied.

# Managing permissions for individuals
<a name="managing-permissions-for-individuals"></a>

By using permissions sets, the permissions boundary, and the **CloudFormationRole** IAM role, you can limit the amount of permissions that you need to assign directly to individual principals. This helps you manage access as your company grows and helps you apply the security best practice of granting least privilege.

You can also use *service-linked roles*, which grant permissions to an AWS service to provision resources on your behalf. Instead of granting permissions to the IAM principal (user, user group, or role), you can grant the permissions to the service. For example, the service-linked role for [AWS Service Catalog](https://docs.aws.amazon.com/servicecatalog/latest/adminguide/introduction.html) allows you to provision your own templates, resources, and environments, without assigning permissions to the IAM principal. For more information, see [AWS services that work with IAM](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_aws-services-that-work-with-iam.html) and [Using service-linked roles](https://docs.aws.amazon.com/IAM/latest/UserGuide/using-service-linked-roles.html) (IAM documentation).

Another best practice is to limit the amount of access individuals have to the AWS Management Console. By limiting access to the console, you can require individuals to provision resources by using infrastructure as code (IaC) technologies, such as [AWS CloudFormation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/Welcome.html), [HashiCorp Terraform](https://www.terraform.io/), or [Pulumi](https://www.pulumi.com/). Managing infrastructure through IaC you to track changes to resources over time and introduce mechanisms for approving changes, such as GitHub pull requests.