avalanche-ops 0.5.1

avalanche-ops spec
---
AWSTemplateFormatVersion: "2010-09-09"
Description: "IAM instance role"

# takes about 3-minute

# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/parameters-section-structure.html
Parameters:
  RoleName:
    Type: String
    Description: Role name.

  RoleProfileName:
    Type: String
    Description: Role profile name.

  Id:
    Type: String
    Description: Unique identifier, prefix for all resources created below.

  KmsKeyArn:
    Type: String
    Description: KMS key ARN that de/encrypts resources.

  S3BucketName:
    Type: String
    Description: S3 bucket name to store.

Mappings:
  ServicePrincipals:
    aws-cn:
      ec2: ec2.amazonaws.com.cn
    aws:
      ec2: ec2.amazonaws.com

Resources:
  # https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iam-role.html
  InstanceRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Ref RoleName
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - Fn::FindInMap:
                    - ServicePrincipals
                    - Ref: AWS::Partition
                    - ec2
            Action:
              - sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/AmazonSSMFullAccess
        - arn:aws:iam::aws:policy/CloudWatchFullAccess
      Path: /
      Policies:
        # restrict this better
        # ref. https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_examples_ec2_ebs-owner.html
        - PolicyName: avalanched-instance-role-policy
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: Allow
                Action:
                  - ec2:DescribeInstances # to fetch tags
                  - ec2:DescribeTags # to find network/resource information
                  - ec2:DescribeVolumes # to wait for volume attachment
                  - ec2:CreateTags
                  - ec2:CreateVolume # to create volume if not exists
                  - ec2:AttachVolume
                  - ec2:DetachVolume # to fail fast in case of spot instance-action
                  - autoscaling:SetInstanceHealth # to fail fast to mark the local instance "Unhealthy"
                  - ec2:TerminateInstances # to fail fast in case of spot instance-action
                Resource: "*"

              - Effect: Allow
                Action:
                  - kms:Encrypt # to generate TLS key and encrypt
                  - kms:GenerateDataKey* # to encrypt TLS key
                  - kms:DescribeKey # to describe the KMS key
                Resource: { Ref: KmsKeyArn }
              - Effect: Allow
                Action:
                  - s3:List*
                Resource: "*"

              - Effect: Allow
                Action:
                  - s3:GetObject # to download artifacts
                  - s3:PutObject # to upload generated TLS keys
                Resource:
                  - !Join [
                      "",
                      [
                        !Sub "arn:${AWS::Partition}:s3:::",
                        !Ref S3BucketName,
                        "/",
                        !Ref Id,
                        "/*",
                      ],
                    ]
                  - !Join [
                      "",
                      [
                        !Sub "arn:${AWS::Partition}:s3:::",
                        !Ref S3BucketName,
                        "/",
                        !Ref Id,
                        "/bootstrap/*",
                      ],
                    ]
                  - !Join [
                      "",
                      [
                        !Sub "arn:${AWS::Partition}:s3:::",
                        !Ref S3BucketName,
                        "/",
                        !Ref Id,
                        "/pki/*",
                      ],
                    ]
                  - !Join [
                      "",
                      [
                        !Sub "arn:${AWS::Partition}:s3:::",
                        !Ref S3BucketName,
                        "/",
                        !Ref Id,
                        "/discover/*",
                      ],
                    ]
                  - !Join [
                      "",
                      [
                        !Sub "arn:${AWS::Partition}:s3:::",
                        !Ref S3BucketName,
                        "/",
                        !Ref Id,
                        "/backups/*",
                      ],
                    ]
                  - !Join [
                      "",
                      [
                        !Sub "arn:${AWS::Partition}:s3:::",
                        !Ref S3BucketName,
                        "/",
                        !Ref Id,
                        "/events/*",
                      ],
                    ]
                  - !Join [
                      "",
                      [
                        !Sub "arn:${AWS::Partition}:s3:::",
                        !Ref S3BucketName,
                        "/",
                        !Ref Id,
                        "/ssm-output-logs/*",
                      ],
                    ]

              - Effect: Allow
                Action:
                  - cloudwatch:PutMetricData
                Resource: "*"

              - Effect: Allow
                Action:
                  - logs:CreateLogGroup
                  - logs:CreateLogStream
                  - logs:PutLogEvents
                  - logs:DescribeLogStreams
                  - logs:PutRetentionPolicy
                Resource:
                  # Ref: http://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html#arn-syntax-cloudwatch-logs
                  - !Sub "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:${Id}"
                  - !Sub "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:${Id}:log-stream:*"

              # for static IP addresses
              - Effect: Allow
                Action:
                  - ec2:AllocateAddress # https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_AllocateAddress.html
                  - ec2:AssociateAddress # https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_AssociateAddress.html
                  - ec2:DescribeAddresses # https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeAddresses.html
                Resource: "*"

  # https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iam-instanceprofile.html
  InstanceProfile:
    Type: AWS::IAM::InstanceProfile
    Properties:
      InstanceProfileName: !Ref RoleProfileName
      Path: "/"
      Roles:
        - !Ref InstanceRole

Outputs:
  InstanceRoleArn:
    Value: !GetAtt InstanceRole.Arn
    Description: Role ARN

  InstanceProfileArn:
    Value: !GetAtt InstanceProfile.Arn
    Description: Instance profile ARN