AWSTemplateFormatVersion: "2010-09-09"
Description: "Creates a secure, tag-enforced IAM role for Dragonfly BYOC"

Parameters:
  RoleName:
    Type: String
    Description: "The name of the role to be created."
    Default: "DragonflyBYOCRole"
  PolicyName:
    Type: String
    Description: "The name of the policy to be created."
    Default: "DragonflyBYOCPolicy"
  ExternalId:
    Type: String
    Description: "The unique External Id provided by Dragonfly to prevent the confused deputy problem."
    NoEcho: true

Resources:
  DragonflyBYOCInstanceRole:
    Type: "AWS::IAM::Role"
    Properties:
      RoleName: "dfcloud-prodv2-data-plane-instance-role"
      Tags:
        - Key: "owner"
          Value: "dragonfly-cloud"
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: "Allow"
            Principal:
              Service: "ec2.amazonaws.com"
            Action: "sts:AssumeRole"
      Policies:
        - PolicyName: "artifacts-bucket-access-policy"
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: "Allow"
                Action: "sts:AssumeRole"
                Resource: "arn:aws:iam::962364259018:role/byoc-prodv2-artifacts-access-role-*"
        - PolicyName: "backups-bucket-access-policy"
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: "Allow"
                Action:
                  - "s3:GetObject"
                  - "s3:GetObjectAcl"
                  - "s3:PutObject"
                  - "s3:PutObjectAcl"
                  - "s3:ListBucket"
                Resource:
                  - "arn:aws:s3:::dfcloud-prodv2-data-plane-backups-*"
                  - "arn:aws:s3:::dfcloud-prodv2-data-plane-backups-*/*"
        - PolicyName: "operations-bucket-access-policy"
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: "Allow"
                Action:
                  - "s3:GetObject"
                  - "s3:GetObjectAcl"
                  - "s3:PutObject"
                  - "s3:PutObjectAcl"
                  - "s3:ListBucket"
                Resource:
                  - "arn:aws:s3:::dfcloud-prodv2-data-plane-operations-*"
                  - "arn:aws:s3:::dfcloud-prodv2-data-plane-operations-*/*"

  DragonflyBYOCRole:
    Type: "AWS::IAM::Role"
    Properties:
      RoleName: !Ref RoleName
      Tags:
        - Key: "owner"
          Value: "dragonfly-cloud"
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: "Allow"
            Principal:
              AWS: "arn:aws:iam::962364259018:role/prodv2-control-plane-controller-worker"
            Action: "sts:AssumeRole"
            Condition:
              StringEquals:
                'sts:ExternalId': !Ref ExternalId
          - Effect: "Allow"
            Principal:
              AWS: "arn:aws:iam::962364259018:role/prodv2-control-plane-api-server"
            Action: "sts:AssumeRole"
            Condition:
              StringEquals:
                'sts:ExternalId': !Ref ExternalId
      Policies:
        - PolicyName: !Ref PolicyName
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              # Allow creation of resources and the tagging of those resources in the same call.
              - Sid: "AllowCreationAndTaggingOfResources"
                Effect: "Allow"
                Action:
                  - "ec2:AuthorizeSecurityGroupIngress"
                  - "ec2:CreateVpc"
                  - "ec2:CreateInternetGateway"
                  - "ec2:CreateSecurityGroup"
                  - "ec2:CreateVpcEndpoint"
                  - "ec2:CreateVpcPeeringConnection"
                  - "ec2:CreateSubnet"
                  - "ec2:CreateTags"
                  - "ec2:RunInstances"
                  - "iam:CreateInstanceProfile"
                  - "iam:TagInstanceProfile"
                Resource: "*"
                Condition:
                  StringEquals:
                    "aws:RequestTag/owner": "dragonfly-cloud"

              # Allow read-only and discovery actions.
              - Sid: "AllowReadOnlyActions"
                Effect: "Allow"
                Action:
                  - "ec2:DescribeInstanceTypes"
                  - "ec2:DescribeInstances"
                  - "ec2:DescribeImages"
                  - "ec2:DescribeVpcs"
                  - "ec2:DescribeVpcEndpoints"
                  - "ec2:DescribeVpcPeeringConnections"
                  - "ec2:DescribeSubnets"
                  - "ec2:DescribeSecurityGroups"
                  - "ec2:DescribeSecurityGroupRules"
                  - "ec2:DescribeInternetGateways"
                  - "ec2:DescribeRouteTables"
                Resource: "*"

              # Allow modification and deletion of resources ONLY IF they are already tagged correctly.
              - Sid: "AllowModificationOfTaggedResources"
                Effect: "Allow"
                Action:
                  - "ec2:AttachInternetGateway"
                  - "ec2:AuthorizeSecurityGroupIngress"
                  - "ec2:CreateSubnet"
                  - "ec2:CreateVpcEndpoint"
                  - "ec2:CreateVpcPeeringConnection"
                  - "ec2:CreateSecurityGroup"
                  - "ec2:CreateRoute"
                  - "ec2:DeleteVpc"
                  - "ec2:DeleteRoute"
                  - "ec2:DeleteSubnet"
                  - "ec2:DeleteSecurityGroup"
                  - "ec2:DeleteVpcEndpoints"
                  - "ec2:DeleteVpcPeeringConnection"
                  - "ec2:DeleteInternetGateway"
                  - "ec2:DetachInternetGateway"
                  - "ec2:ModifyVpcAttribute"
                  - "ec2:ModifyVpcEndpoint"
                  - "ec2:RebootInstances"
                  - "ec2:RunInstances"
                  - "ec2:RevokeSecurityGroupIngress"
                  - "ec2:StopInstances"
                  - "ec2:TerminateInstances"
                  - "iam:AddRoleToInstanceProfile"
                  - "iam:DeleteInstanceProfile"
                  - "iam:RemoveRoleFromInstanceProfile"
                Resource: "*"
                Condition:
                  StringEquals:
                    "aws:ResourceTag/owner": "dragonfly-cloud"

              # Allow reading resources ONLY IF they are already tagged correctly.
              - Sid: "AllowReadOnlyActionsOfTaggedResources"
                Effect: "Allow"
                Action:
                  - "iam:GetInstanceProfile"
                Resource: "*"
                Condition:
                  StringEquals:
                    "aws:ResourceTag/owner": "dragonfly-cloud"

              - Sid: "AllowS3Actions"
                Effect: "Allow"
                Action:
                  - "s3:AbortMultipartUpload"
                  - "s3:CreateBucket"
                  - "s3:PutBucketTagging"
                  - "s3:PutObject"
                  - "s3:GetObject"
                  - "s3:DeleteBucket"
                  - "s3:DeleteObject"
                  - "s3:DeleteObjectVersion"
                  - "s3:ListBucket"
                  - "s3:ListBucketVersions"
                  - "s3:ListBucketMultipartUploads"
                  - "s3:GetBucketPolicy"
                  - "s3:PutBucketPolicy"
                Resource: "arn:aws:s3:::dfcloud-*"

              - Sid: "AllowPassRoleToEC2"
                Effect: "Allow"
                Action:
                  - "iam:PassRole"
                Resource:
                  - !Sub "arn:aws:iam::${AWS::AccountId}:role/dfcloud-prodv2-data-plane-instance-role"
                Condition:
                  StringEquals:
                    "iam:PassedToService": "ec2.amazonaws.com"

Outputs:
  DragonflyBYOCRoleArn:
    Description: "The ARN of the role to be assumed by Dragonfly Cloud worker."
    Value: !GetAtt DragonflyBYOCRole.Arn
  ExternalId:
    Description: "The External Id provided by Dragonfly to prevent the confused deputy problem."
    Value: !Ref ExternalId
  AWSAccountId:
    Description: "The AWS Account ID where this stack is deployed."
    Value: !Ref AWS::AccountId
