AWSTemplateFormatVersion: "2010-09-09"
Description: "Template to create an IAM Role with an attached policy that provides all necessary permissions for Artillery.io to run distributed tests on AWS Fargate.
  By default the IAM role is configured to trust your AWS account, meaning it will allow any IAM User, Role or service from your account to assume it. You can restrict the role to allow only by a specific IAM user or role to assume it by filling out the appropriate parameter value below."

Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:
      - Label:
          default: "Restrict to specific IAM User (optional)"
        Parameters:
          - User
      - Label:
          default: "Restrict to specific IAM Role (optional)"
        Parameters:
          - Role
    ParameterLabels:
      User:
        default: "IAM user name or ARN"
      Role:
        default: "IAM role name or ARN"

Parameters:

  User:
    Type: String
    Default: ""
    Description: Use when you want to allow the created role to be assumed only by a specific IAM user (by default any user, role or service from your account will be allowed to assume it). Provide the user name or ARN.

  Role:
    Type: String
    Default: ""
    Description: Use when you want to allow the created role to be assumed only by a specific IAM role (by default any user, role or service from your account will be allowed to assume it). Provide the role name or ARN.

Conditions:
  ShouldTrustAccount:
    !And 
      - !Equals [!Ref User, ""]
      - !Equals [!Ref Role, ""]
  ShouldTrustUser:
      !Not [!Equals [!Ref User, ""]]
  IsUserArn:
      !Equals [!Select [0, !Split [":", !Ref User]], "arn"]
  ShouldTrustRole:
      !Not [!Equals [!Ref Role, ""]]
  IsRoleArn:
      !Equals [!Select [0, !Split [":", !Ref Role]], "arn"]


Resources:
  ArtilleryDistributedTestingFargateRole:
    Type: "AWS::IAM::Role"
    Properties:
      RoleName: "ArtilleryDistributedTestingFargateRole"
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: "Allow"
            Principal:
              AWS: [
                !If [ShouldTrustAccount, !Ref "AWS::AccountId", !Ref "AWS::NoValue"],
                !If [ShouldTrustUser, !If [IsUserArn, !Ref User, !Sub "arn:aws:iam::${AWS::AccountId}:user/${User}"], !Ref "AWS::NoValue"],
                !If [ShouldTrustRole, !If [IsRoleArn, !Ref Role, !Sub "arn:aws:iam::${AWS::AccountId}:role/${Role}"], !Ref "AWS::NoValue"]
              ]
            Action: [
              "sts:AssumeRole"
            ]
            
      Path: "/"
      Policies:
        - PolicyName: "ArtilleryDistributedTestingFargatePolicy"
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Sid: "CreateOrGetECSRole"
                Effect: "Allow"
                Action:
                  - "iam:CreateRole"
                  - "iam:GetRole"
                  - "iam:AttachRolePolicy"
                  - "iam:PassRole"
                Resource:
                  Fn::Sub: "arn:aws:iam::${AWS::AccountId}:role/artilleryio-ecs-worker-role"
              - Sid: "CreateECSPolicy"
                Effect: "Allow"
                Action:
                  - "iam:CreatePolicy"
                Resource:
                  Fn::Sub: "arn:aws:iam::${AWS::AccountId}:policy/artilleryio-ecs-worker-policy"
              - Effect: "Allow"
                Action:
                  - "iam:CreateServiceLinkedRole"
                Resource:
                  - "arn:aws:iam::*:role/aws-service-role/ecs.amazonaws.com/AWSServiceRoleForECS*"
                Condition:
                  StringLike:
                    iam:AWSServiceName: "ecs.amazonaws.com"
              - Effect: "Allow"
                Action:
                  - "iam:PassRole"
                Resource:
                  - Fn::Sub: "arn:aws:iam::${AWS::AccountId}:role/artilleryio-ecs-worker-role"
              - Sid: "SQSPermissions"
                Effect: "Allow"
                Action:
                  - "sqs:*"
                Resource:
                  Fn::Sub: "arn:aws:sqs:*:${AWS::AccountId}:artilleryio*"
              - Sid: "SQSListQueues"
                Effect: "Allow"
                Action:
                  - "sqs:ListQueues"
                Resource: "*"
              - Sid: "ECSPermissionsGeneral"
                Effect: "Allow"
                Action:
                  - "ecs:ListClusters"
                  - "ecs:CreateCluster"
                  - "ecs:RegisterTaskDefinition"
                  - "ecs:DeregisterTaskDefinition"
                Resource: "*"
              - Sid: "ECSPermissionsScopedToCluster"
                Effect: "Allow"
                Action:
                  - "ecs:DescribeClusters"
                  - "ecs:ListContainerInstances"
                Resource:
                  Fn::Sub: "arn:aws:ecs:*:${AWS::AccountId}:cluster/*"
              - Sid: "ECSPermissionsScopedWithCondition"
                Effect: "Allow"
                Action:
                  - "ecs:SubmitTaskStateChange"
                  - "ecs:DescribeTasks"
                  - "ecs:ListTasks"
                  - "ecs:ListTaskDefinitions"
                  - "ecs:DescribeTaskDefinition"
                  - "ecs:StartTask"
                  - "ecs:StopTask"
                  - "ecs:RunTask"
                Condition:
                  ArnEquals:
                    ecs:cluster:
                      Fn::Sub: "arn:aws:ecs:*:${AWS::AccountId}:cluster/*"
                Resource: "*"
              - Sid: "ECSTagResourceOnRunTask"
                Effect: "Allow"
                Action:
                  - "ecs:TagResource"
                Condition:
                  StringEquals:
                    ecs:CreateAction: "RunTask"
                Resource:
                  Fn::Sub: "arn:aws:ecs:*:${AWS::AccountId}:task/*"
              - Sid: "S3Permissions"
                Effect: "Allow"
                Action:
                  - "s3:CreateBucket"
                  - "s3:DeleteObject"
                  - "s3:GetObject"
                  - "s3:GetObjectAcl"
                  - "s3:GetObjectTagging"
                  - "s3:GetObjectVersion"
                  - "s3:PutObject"
                  - "s3:PutObjectAcl"
                  - "s3:ListBucket"
                  - "s3:GetBucketLocation"
                  - "s3:GetBucketLogging"
                  - "s3:GetBucketPolicy"
                  - "s3:GetBucketTagging"
                  - "s3:PutBucketPolicy"
                  - "s3:PutBucketTagging"
                  - "s3:PutMetricsConfiguration"
                  - "s3:GetLifecycleConfiguration"
                  - "s3:PutLifecycleConfiguration"
                Resource:
                  - "arn:aws:s3:::artilleryio-test-data-*"
                  - "arn:aws:s3:::artilleryio-test-data-*/*"
              - Sid: "LogsPermissions"
                Effect: "Allow"
                Action:
                  - "logs:PutRetentionPolicy"
                Resource:
                  - Fn::Sub: "arn:aws:logs:*:${AWS::AccountId}:log-group:artilleryio-log-group/*"
              - Effect: "Allow"
                Action:
                  - "secretsmanager:GetSecretValue"
                Resource:
                  - Fn::Sub: "arn:aws:secretsmanager:*:${AWS::AccountId}:secret:artilleryio/*"
              - Effect: "Allow"
                Action:
                  - "ssm:PutParameter"
                  - "ssm:GetParameter"
                  - "ssm:GetParameters"
                  - "ssm:DeleteParameter"
                  - "ssm:DescribeParameters"
                  - "ssm:GetParametersByPath"
                Resource:
                  - Fn::Sub: "arn:aws:ssm:us-east-1:${AWS::AccountId}:parameter/artilleryio/*"
                  - Fn::Sub: "arn:aws:ssm:us-east-2:${AWS::AccountId}:parameter/artilleryio/*"
                  - Fn::Sub: "arn:aws:ssm:us-west-1:${AWS::AccountId}:parameter/artilleryio/*"
                  - Fn::Sub: "arn:aws:ssm:us-west-2:${AWS::AccountId}:parameter/artilleryio/*"
                  - Fn::Sub: "arn:aws:ssm:ca-central-1:${AWS::AccountId}:parameter/artilleryio/*"
                  - Fn::Sub: "arn:aws:ssm:eu-west-1:${AWS::AccountId}:parameter/artilleryio/*"
                  - Fn::Sub: "arn:aws:ssm:eu-west-2:${AWS::AccountId}:parameter/artilleryio/*"
                  - Fn::Sub: "arn:aws:ssm:eu-west-3:${AWS::AccountId}:parameter/artilleryio/*"
                  - Fn::Sub: "arn:aws:ssm:eu-central-1:${AWS::AccountId}:parameter/artilleryio/*"
                  - Fn::Sub: "arn:aws:ssm:eu-north-1:${AWS::AccountId}:parameter/artilleryio/*"
                  - Fn::Sub: "arn:aws:ssm:ap-south-1:${AWS::AccountId}:parameter/artilleryio/*"
                  - Fn::Sub: "arn:aws:ssm:ap-east-1:${AWS::AccountId}:parameter/artilleryio/*"
                  - Fn::Sub: "arn:aws:ssm:ap-northeast-1:${AWS::AccountId}:parameter/artilleryio/*"
                  - Fn::Sub: "arn:aws:ssm:ap-northeast-2:${AWS::AccountId}:parameter/artilleryio/*"
                  - Fn::Sub: "arn:aws:ssm:ap-southeast-1:${AWS::AccountId}:parameter/artilleryio/*"
                  - Fn::Sub: "arn:aws:ssm:ap-southeast-2:${AWS::AccountId}:parameter/artilleryio/*"
                  - Fn::Sub: "arn:aws:ssm:me-south-1:${AWS::AccountId}:parameter/artilleryio/*"
                  - Fn::Sub: "arn:aws:ssm:sa-east-1:${AWS::AccountId}:parameter/artilleryio/*"
              - Effect: "Allow"
                Action:
                  - "ec2:DescribeRouteTables"
                  - "ec2:DescribeVpcs"
                  - "ec2:DescribeSubnets"
                Resource: "*"

Outputs:
  RoleArn:
    Description: "ARN of the created IAM Role"
    Value:
      Fn::GetAtt:
        - "ArtilleryDistributedTestingFargateRole"
        - "Arn"
