AWSTemplateFormatVersion: 2010-09-09
Description: Create the Heeler role in member accounts.
Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:
      -
        Label:
          default: Organizational Unit and Account Information
        Parameters:
          - OrganizationUnitList
          - AccountList
          - LimitDeployment
      -
        Label:
          default: Assumed Role Information
        Parameters:
          - MemberAccountRoleName
          - OrganizationRoleExternalId
          - HeelerSecurityRoleArn
    ParameterLabels:
      OrganizationUnitList:
        default: "Organization Unit List (Required)"
      AccountList:
        default: "Account List (Optional)"
      LimitDeployment:
        default: "Scope Change (Optional)"
      MemberAccountRoleName:
        default: "Heeler Role Name (Required)"
      OrganizationRoleExternalId:
        default: "Organization Role External ID (Recommended)"
      HeelerSecurityRoleArn:
        default: "Role ARN provided by Heeler Security"

Parameters:
  OrganizationUnitList:
    Type: String
    AllowedPattern: ^[a-zA-Z0-9\-\,]*$
    Description: Root ID (r-1234) or comma-separated list of Organizational Unit IDs without spaces (ou-1234-abcdefgh,ou-1234-ijklmnop) as stackset target.
  AccountList:
    Type: String
    Default: ""
    Description: Optional list of Accounts to exclude from deployment.
  LimitDeployment:
    Type: String
    AllowedValues:
      - limit
      - exclude
    Default: exclude
    Description: Change to "limit" to limit deployment to provided Accounts.
  MemberAccountRoleName:
    Type: String
    Default: heeler-member
    AllowedPattern: ^[a-zA-Z0-9\-]*$
    Description: The name of the role that Heeler will assume in each member account (limited to alphanumeric and hyphen characters).
  OrganizationRoleExternalId:
    Type: String
    Description: External ID to use when assuming the role in the parent account.
  HeelerSecurityRoleArn:
    Type: String
    Default: arn:aws:iam::168777450829:role/prod-1-role
    Description: The role ARN provided by Heeler Security, which will be included in the Management Role trust policy.

Conditions:
  OUs: !Not [!Equals [!Ref OrganizationUnitList, ""]]
  Accounts: !Not [!Equals [!Ref AccountList, ""]]
  Limit: !Equals [!Ref LimitDeployment, "limit"]

Resources:
  HeelerReadOnly:
    Type: "AWS::IAM::Role"
    Properties:
      RoleName: heeler-management-ro
      Description: The read-only management role that is allowed to fetch resource information from member accounts.
      Policies:
        - PolicyName: OrgVisibility
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: Allow
                Resource: "*"
                Action:
                  - "organizations:Describe*"
                  - "organizations:List*"
                  - "sts:AssumeRole"
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              AWS: !Ref HeelerSecurityRoleArn
            Action:
              - "sts:AssumeRole"
            Condition:
              StringEquals:
                sts:ExternalId: !Ref OrganizationRoleExternalId
            Sid: AssumeRolewithCondition
          - Effect: Allow
            Principal:
              AWS: "arn:aws:iam::168777450829:role/prod-1-role"
            Action: "sts:TagSession"
            Sid: TagSession

  HeelerMemberReadOnlyPolicy:
    Type: AWS::IAM::ManagedPolicy
    Properties:
      ManagedPolicyName: Heeler
      Description: Heeler policy that denies access to certain actions and allows Lambda GetFunction and SSM SendCommand
      PolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Deny
            Resource: "*"
            Action:
              - "cloudformation:GetTemplate"
              - "dynamodb:GetItem"
              - "dynamodb:BatchGetItem"
              - "dynamodb:Query"
              - "dynamodb:Scan"
              - "ec2:GetConsoleOutput"
              - "ec2:GetConsoleScreenshot"
              - "kinesis:Get*"
              - "logs:GetLogEvents"
              - "s3:GetObject"
              - "sdb:Select*"
              - "sqs:ReceiveMessage"
          - Effect: Allow
            Resource: "*"
            Action:
              - "lambda:GetFunction"
              - "ssm:SendCommand"

  HeelerMemberEKSPolicy:
    Type: AWS::IAM::ManagedPolicy
    Properties:
      Description: Heeler policy that allows configuring API access to EKS clusters
      PolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Action:
              - "eks:TagResource"
            Resource: "*"
          - Effect: Allow
            Action:
              - "eks:CreateAccessEntry"
            Resource: "*"
            Condition:
              ArnEquals:
                "eks:principalArn": !GetAtt HeelerMember.Arn
          - Effect: "Allow"
            Action:
              - "eks:AssociateAccessPolicy"
            Resource: "*"
            Condition:
              ArnEquals:
                "eks:policyArn": "arn:aws:eks::aws:cluster-access-policy/AmazonEKSAdminViewPolicy"
      ManagedPolicyName: HeelerEKS
      Roles:
        - !Ref HeelerMember

  HeelerMember:
    Type: "AWS::IAM::Role"
    Properties:
      RoleName: !Ref MemberAccountRoleName
      Description: Role that the read-only management role will assume to fetch resources from member accounts and allow API access to EKS clusters
      MaxSessionDuration: 28800
      ManagedPolicyArns:
        - "arn:aws:iam::aws:policy/ReadOnlyAccess"
        - !GetAtt HeelerMemberReadOnlyPolicy.PolicyArn
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              AWS: !GetAtt HeelerReadOnly.Arn
            Action:
              - "sts:AssumeRole"
              - "sts:TagSession"

  HeelerMemberRoles:
    Type: "AWS::CloudFormation::StackSet"
    Properties:
      StackSetName: HeelerRoles
      Description: Heeler Member Account Setup
      Capabilities:
        - CAPABILITY_NAMED_IAM
      AutoDeployment:
        Enabled: true
        RetainStacksOnAccountRemoval: false
      PermissionModel: SERVICE_MANAGED
      StackInstancesGroup:
        - DeploymentTargets:
            AccountFilterType:
              !If [Accounts, !If [Limit, INTERSECTION, DIFFERENCE], !Ref AWS::NoValue]
            Accounts:
              !If [Accounts, !Split [",", !Ref AccountList], !Ref AWS::NoValue]
            OrganizationalUnitIds:
              !If [
                OUs,
                !Split [",", !Ref OrganizationUnitList],
                !Ref AWS::NoValue,
              ]
          Regions:
            - !Ref AWS::Region
      ManagedExecution:
        Active: true
      Parameters:
        - ParameterKey: OrganizationRoleArn
          ParameterValue: !GetAtt HeelerReadOnly.Arn
        - ParameterKey: MemberAccountRoleName
          ParameterValue: !Ref MemberAccountRoleName
      TemplateBody: |
        {
            "AWSTemplateFormatVersion": "2010-09-09",
            "Description": "Create the Heeler role in member accounts.",
            "Parameters": {
                "OrganizationRoleArn": {
                    "Type": "String",
                    "Description": "The read-only management role that is allowed to assume the member role."
                },
                "MemberAccountRoleName": {
                    "Type": "String",
                    "Description": "The name of the role that the read-only management role will assume in each member account."
                },
            },
            "Resources": {
                "Heeler": {
                    "Type": "AWS::IAM::ManagedPolicy",
                    "Properties": {
                        "ManagedPolicyName": "Heeler",
                        "Description": "Heeler policy that denies access to certain actions and allows Lambda GetFunction and SSM SendCommand",
                        "PolicyDocument": {
                            "Version": "2012-10-17",
                            "Statement": [
                                {
                                    "Effect": "Deny",
                                    "Resource": "*",
                                    "Action": [
                                        "cloudformation:GetTemplate",
                                        "dynamodb:GetItem",
                                        "dynamodb:BatchGetItem",
                                        "dynamodb:Query",
                                        "dynamodb:Scan",
                                        "ec2:GetConsoleOutput",
                                        "ec2:GetConsoleScreenshot",
                                        "kinesis:Get*",
                                        "logs:GetLogEvents",
                                        "s3:GetObject",
                                        "sdb:Select*",
                                        "sqs:ReceiveMessage"
                                    ]
                                },
                                {
                                    "Effect": "Allow",
                                    "Resource": "*",
                                    "Action": [
                                        "lambda:GetFunction",
                                        "ssm:SendCommand"
                                    ]
                                }
                            ]
                        }
                    }
                },
                "HeelerEKS": {
                    "Type": "AWS::IAM::ManagedPolicy",
                    "Properties": {
                        "ManagedPolicyName": "HeelerEKS",
                        "Description": "Heeler policy that allows configuring API access to EKS clusters",
                        "PolicyDocument": {
                            "Version": "2012-10-17",
                            "Statement": [
                                {
                                    "Action": [
                                        "eks:TagResource"
                                    ],
                                    "Resource": "*",
                                    "Effect": "Allow"
                                },
                                {
                                    "Condition": {
                                        "ArnEquals": {
                                            "eks:principalArn": {
                                                "Fn::GetAtt": [
                                                    "HeelerMember",
                                                    "Arn"
                                                ]
                                            }
                                        }
                                    },
                                    "Action": [
                                        "eks:CreateAccessEntry"
                                    ],
                                    "Resource": "*",
                                    "Effect": "Allow"
                                },
                                {
                                    "Condition": {
                                        "ArnEquals": {
                                            "eks:policyArn": "arn:aws:eks::aws:cluster-access-policy/AmazonEKSAdminViewPolicy"
                                        }
                                    },
                                    "Action": [
                                        "eks:AssociateAccessPolicy"
                                    ],
                                    "Resource": "*",
                                    "Effect": "Allow"
                                }
                            ]
                        },
                        "Roles": [
                            {
                                "Ref": "HeelerMember"
                            }
                        ]
                    }
                },
                "HeelerMember": {
                    "Type": "AWS::IAM::Role",
                    "Properties": {
                        "RoleName": {
                            "Ref": "MemberAccountRoleName"
                        },
                        "Description": "Role that the read-only management role will assume to fetch resources from member accounts and allow API access to EKS clusters",
                        "ManagedPolicyArns": [
                            {
                                "Ref": "Heeler"
                            },
                            "arn:aws:iam::aws:policy/ReadOnlyAccess"
                        ],
                        "MaxSessionDuration": 28800,
                        "AssumeRolePolicyDocument": {
                            "Version": "2012-10-17",
                            "Statement": [
                                {
                                    "Effect": "Allow",
                                    "Principal": {
                                        "AWS": {
                                            "Fn::Sub": "${OrganizationRoleArn}"
                                        }
                                    },
                                    "Action": [
                                        "sts:AssumeRole",
                                        "sts:TagSession"
                                    ]
                                }
                            ]
                        }
                    }
                }
            }
        }

Outputs:
  OrganizationRoleArn:
    Value: !GetAtt HeelerReadOnly.Arn
    Description: The ARN of the Heeler management read-only role.
