AWSTemplateFormatVersion: "2010-09-09"
Description: >-
  Groundcrew access. Tier=observe (default) creates one IAM role that only the Groundcrew
  principal can assume, and only with this workspace's ExternalId. The role reads configuration
  and metadata; it cannot read secret values, decrypt data, or read objects. Also turns on a
  local Resource Explorer index in this region. Tier=deploy also creates the groundcrew-deploys
  ECS cluster, the groundcrew-task-execution role, and the groundcrew-deploy role, which may
  only create and change groundcrew-* resources tagged project=groundcrew (and write, never
  read, groundcrew/* secrets). EnableOperate=true (independent of Tier) creates the
  groundcrew-operate role for approved emergency fixes: roll an ECS service back to an existing
  revision, restart it, or move a secret's AWSCURRENT stage back to the previous version, only
  on the services and secrets listed in OperateServiceArns / OperateSecretArns and never
  reading a secret value (rollbacks may pass OperatePassRoleArns to ECS). Delete this stack to
  remove all access.

Parameters:
  GroundcrewPrincipalArn:
    Type: String
    Description: >-
      Groundcrew's platform identity, the only principal allowed to assume the role: the
      groundcrew-platform role of the Groundcrew service at groundkrew.com (the default). A
      self-hosted Groundcrew shows its own on the Connections page.
    Default: arn:aws:iam::881050425883:role/groundcrew-platform
    AllowedPattern: "^arn:aws:iam::[0-9]{12}:(root|user/.+|role/.+)$"
  ExternalId:
    Type: String
    Description: >-
      This workspace's ExternalId (prefilled by the Connect AWS link). Do not reuse it elsewhere.
    MinLength: 16
    MaxLength: 128
    AllowedPattern: "^[A-Za-z0-9_+=,.@:/-]+$"
  RoleName:
    Type: String
    Default: groundcrew-read-only
    Description: >-
      Name of the role to create. Must start with groundcrew- (Groundcrew's platform identity
      may only assume roles named groundcrew-*).
    AllowedPattern: "^groundcrew-[A-Za-z0-9+=,.@_-]{1,53}$"
  CreateResourceExplorerIndex:
    Type: String
    Default: "true"
    AllowedValues: ["true", "false"]
    Description: >-
      Create a local Resource Explorer index and default view in this region. Set false if this
      region already has an index.

  Tier:
    Type: String
    Default: observe
    AllowedValues: ["observe", "deploy"]
    Description: >-
      observe: read-only discovery (the groundcrew-read-only role). deploy: also lets
      Groundcrew deploy apps into a groundcrew-deploys ECS cluster, using only resources named
      groundcrew-* and tagged project=groundcrew. Switch by updating this stack.

  EnableOperate:
    Type: String
    Default: "false"
    AllowedValues: ["true", "false"]
    Description: >-
      true: also create the groundcrew-operate role, so Groundcrew can run approved emergency
      fixes (ECS rollback or restart, secret version rollback, Lambda alias rollback, EC2
      reboot) on the resources listed below.
      Independent of Tier. Every action still needs a person's approval in Groundcrew.
  OperateServiceArns:
    Type: CommaDelimitedList
    Default: "arn:aws:ecs:*:*:service/none/none"
    Description: >-
      ECS services the operate role may update, as ARNs; a wildcard per cluster is allowed
      (arn:aws:ecs:REGION:ACCOUNT:service/CLUSTER/*). Used only with EnableOperate=true.
  OperateSecretArns:
    Type: CommaDelimitedList
    Default: "arn:aws:secretsmanager:*:*:secret:none"
    Description: >-
      Secrets Manager secrets whose version stages the operate role may move, as ARNs; a prefix
      wildcard is allowed (arn:aws:secretsmanager:REGION:ACCOUNT:secret:checkout/*). Used only
      with EnableOperate=true.
  OperatePassRoleArns:
    Type: CommaDelimitedList
    Default: "arn:aws:iam::*:role/none"
    Description: >-
      IAM roles a rollback may hand back to ECS: the execution and task roles of the task
      definition revisions the services above run (ECS needs iam:PassRole on them to change a
      service's revision), as ARNs; a name wildcard is allowed
      (arn:aws:iam::ACCOUNT:role/checkout-*). Passed to ecs-tasks.amazonaws.com only, never a
      groundcrew-* role. The default matches nothing: rollbacks of services whose revisions have
      roles then stop at the plan. Used only with EnableOperate=true.
  OperateFunctionArns:
    Type: CommaDelimitedList
    Default: "arn:aws:lambda:*:*:function:none"
    Description: >-
      Lambda functions whose aliases the operate role may point back at an earlier published
      version, as ARNs ending in * so their aliases match
      (arn:aws:lambda:REGION:ACCOUNT:function:checkout-*). Used only with EnableOperate=true.
  OperateInstanceArns:
    Type: CommaDelimitedList
    Default: "arn:aws:ec2:*:*:instance/none"
    Description: >-
      EC2 instances the operate role may reboot when their status checks fail, as ARNs
      (arn:aws:ec2:REGION:ACCOUNT:instance/i-0abc..., or instance/* for all). Used only with
      EnableOperate=true.

Rules:
  # The defaults above match nothing; turning operate on needs real ARNs in at least one list
  # (an ECS-only, Lambda-only, or EC2-only app lists only what it has).
  # Rules only allow Ref and the Fn::Equals/Not/And/Or/Contains/EachMember*/RefAll/ValueOf*
  # functions (test_rules_use_only_supported_functions).
  OperateNeedsSomethingToActOn:
    RuleCondition: !Equals [!Ref EnableOperate, "true"]
    Assertions:
      - Assert: !Or
          - !Not [!Contains [!Ref OperateServiceArns, "arn:aws:ecs:*:*:service/none/none"]]
          - !Not [!Contains [!Ref OperateSecretArns, "arn:aws:secretsmanager:*:*:secret:none"]]
          - !Not [!Contains [!Ref OperateFunctionArns, "arn:aws:lambda:*:*:function:none"]]
          - !Not [!Contains [!Ref OperateInstanceArns, "arn:aws:ec2:*:*:instance/none"]]
        AssertDescription: With EnableOperate=true, list what Groundcrew may act on in OperateServiceArns, OperateSecretArns, OperateFunctionArns, or OperateInstanceArns.

Conditions:
  CreateIndex: !Equals [!Ref CreateResourceExplorerIndex, "true"]
  IsDeploy: !Equals [!Ref Tier, "deploy"]
  IsOperate: !Equals [!Ref EnableOperate, "true"]

Resources:
  GroundcrewRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Ref RoleName
      Description: Groundcrew read-only discovery (metadata only, no secret values)
      MaxSessionDuration: 3600
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Sid: GroundcrewWithExternalId
            Effect: Allow
            Principal:
              AWS: !Ref GroundcrewPrincipalArn
            Action: sts:AssumeRole
            Condition:
              StringEquals:
                sts:ExternalId: !Ref ExternalId
      Policies:
        - PolicyName: groundcrew-read-only
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Sid: Compute
                Effect: Allow
                Action:
                  - ecs:ListClusters
                  - ecs:DescribeClusters
                  - ecs:ListServices
                  - ecs:DescribeServices
                  - ecs:ListTaskDefinitions
                  - ecs:DescribeTaskDefinition
                  - ecs:ListTasks
                  - ecs:DescribeTasks
                  - lambda:ListFunctions
                  - lambda:GetFunctionConfiguration
                  - lambda:ListTags
                  - lambda:ListVersionsByFunction
                  - lambda:ListAliases
                  - lambda:GetAlias
                  - ec2:DescribeInstances
                  - ec2:DescribeInstanceStatus
                Resource: "*"
              - Sid: ImagesAndDatabases
                Effect: Allow
                Action:
                  - ecr:DescribeRepositories
                  - ecr:DescribeImages
                  - ecr:ListTagsForResource
                  - rds:DescribeDBInstances
                  - rds:DescribeDBClusters
                  - rds:ListTagsForResource
                Resource: "*"
              - Sid: Network
                Effect: Allow
                Action:
                  - ec2:DescribeVpcs
                  - ec2:DescribeSubnets
                  - ec2:DescribeSecurityGroups
                  - ec2:DescribeSecurityGroupRules
                  - ec2:DescribeNetworkInterfaces
                  - elasticloadbalancing:DescribeLoadBalancers
                  - elasticloadbalancing:DescribeTargetGroups
                  - elasticloadbalancing:DescribeTargetHealth
                Resource: "*"
              - Sid: Telemetry
                Effect: Allow
                Action:
                  - cloudwatch:GetMetricData
                  - cloudwatch:GetMetricStatistics
                  - cloudwatch:ListMetrics
                  - cloudwatch:DescribeAlarms
                  - logs:DescribeLogGroups
                  - logs:FilterLogEvents
                Resource: "*"
              - Sid: SecretMetadataOnly
                Effect: Allow
                Action:
                  - secretsmanager:ListSecrets
                  - secretsmanager:DescribeSecret
                Resource: "*"
              - Sid: IamMetadata
                Effect: Allow
                Action:
                  - iam:GetRole
                  - iam:ListRoles
                  - iam:ListRolePolicies
                  - iam:GetRolePolicy
                  - iam:ListAttachedRolePolicies
                  - iam:GetPolicy
                  - iam:GetPolicyVersion
                Resource: "*"
              - Sid: Inventory
                Effect: Allow
                Action:
                  - resource-explorer-2:Search
                  - resource-explorer-2:GetView
                  - resource-explorer-2:GetDefaultView
                  - resource-explorer-2:ListViews
                  - resource-explorer-2:GetIndex
                  - resource-explorer-2:ListIndexes
                  - cloudformation:GetResource
                  - cloudformation:ListResources
                  - tag:GetResources
                Resource: "*"
              # Explicit deny wins over any allow, including ones added to this role later.
              - Sid: NeverReadSecretValuesOrData
                Effect: Deny
                Action:
                  - secretsmanager:GetSecretValue
                  - ssm:GetParameter*
                  - kms:Decrypt
                  - s3:GetObject
                  - s3:GetObjectVersion
                Resource: "*"

  ResourceExplorerIndex:
    Type: AWS::ResourceExplorer2::Index
    Condition: CreateIndex
    Properties:
      Type: LOCAL

  ResourceExplorerView:
    Type: AWS::ResourceExplorer2::View
    Condition: CreateIndex
    DependsOn: ResourceExplorerIndex
    Properties:
      ViewName: groundcrew-default
      IncludedProperties:
        - Name: tags

  ResourceExplorerDefaultView:
    Type: AWS::ResourceExplorer2::DefaultViewAssociation
    Condition: CreateIndex
    Properties:
      ViewArn: !Ref ResourceExplorerView

  # ------------------------------------------------------------------ Tier=deploy only
  # Everything below exists only with Tier=deploy. Names are fixed: the deploy role's policy,
  # Groundcrew's settings (docs/phase4-infra.md), and cleanup rely on them.

  DeployCluster:
    Type: AWS::ECS::Cluster
    Condition: IsDeploy
    Properties:
      ClusterName: groundcrew-deploys
      CapacityProviders: ["FARGATE", "FARGATE_SPOT"]
      DefaultCapacityProviderStrategy:
        - CapacityProvider: FARGATE
          Weight: 1
      ClusterSettings:
        - Name: containerInsights
          Value: disabled
      Tags:
        - Key: project
          Value: groundcrew

  # Used by ECS (not by Groundcrew) to pull a deploy's image and write its logs.
  TaskExecutionRole:
    Type: AWS::IAM::Role
    Condition: IsDeploy
    Properties:
      RoleName: groundcrew-task-execution
      Description: ECS task execution for Groundcrew deploys (pull groundcrew-* images, write /groundcrew/* logs, inject groundcrew/* secrets)
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Sid: EcsTasksInThisAccount
            Effect: Allow
            Principal:
              Service: ecs-tasks.amazonaws.com
            Action: sts:AssumeRole
            Condition:
              StringEquals:
                aws:SourceAccount: !Ref AWS::AccountId
              ArnLike:
                aws:SourceArn: !Sub "arn:${AWS::Partition}:ecs:${AWS::Region}:${AWS::AccountId}:*"
      Policies:
        - PolicyName: groundcrew-task-execution
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Sid: RegistryLogin
                Effect: Allow
                Action: ecr:GetAuthorizationToken
                Resource: "*"
              - Sid: PullGroundcrewImages
                Effect: Allow
                Action:
                  - ecr:BatchCheckLayerAvailability
                  - ecr:BatchGetImage
                  - ecr:GetDownloadUrlForLayer
                Resource: !Sub "arn:${AWS::Partition}:ecr:${AWS::Region}:${AWS::AccountId}:repository/groundcrew-*"
              - Sid: WriteGroundcrewLogs
                Effect: Allow
                Action:
                  - logs:CreateLogStream
                  - logs:PutLogEvents
                Resource: !Sub "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/groundcrew/*"
              # ECS reads a deploy's env secrets (groundcrew/<app>/<VAR>) when a task starts.
              # Only ECS uses this role; Groundcrew itself can never read a secret value.
              - Sid: InjectGroundcrewSecrets
                Effect: Allow
                Action: secretsmanager:GetSecretValue
                Resource: !Sub "arn:${AWS::Partition}:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:groundcrew/*"
      Tags:
        - Key: project
          Value: groundcrew

  DeployRole:
    Type: AWS::IAM::Role
    Condition: IsDeploy
    Properties:
      RoleName: groundcrew-deploy
      Description: Groundcrew deploys (groundcrew-* resources tagged project=groundcrew only, no secret values)
      MaxSessionDuration: 3600
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Sid: GroundcrewWithExternalId
            Effect: Allow
            Principal:
              AWS: !Ref GroundcrewPrincipalArn
            Action: sts:AssumeRole
            Condition:
              StringEquals:
                sts:ExternalId: !Ref ExternalId
      Policies:
        - PolicyName: groundcrew-deploy
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              # --- ECS: only the groundcrew-deploys cluster
              - Sid: EcsServicesInDeployCluster
                Effect: Allow
                Action:
                  - ecs:CreateService
                  - ecs:UpdateService
                  - ecs:DeleteService
                  - ecs:DescribeServices
                Resource: !Sub "arn:${AWS::Partition}:ecs:${AWS::Region}:${AWS::AccountId}:service/groundcrew-deploys/*"
              - Sid: EcsDescribeDeployCluster
                Effect: Allow
                Action: ecs:DescribeClusters
                Resource: !GetAtt DeployCluster.Arn
              - Sid: EcsListInDeployCluster
                Effect: Allow
                Action:
                  - ecs:ListServices
                  - ecs:ListTasks
                Resource: "*"
                Condition:
                  ArnEquals:
                    ecs:cluster: !GetAtt DeployCluster.Arn
              - Sid: EcsRunGroundcrewTasksInDeployCluster
                Effect: Allow
                Action: ecs:RunTask
                Resource: !Sub "arn:${AWS::Partition}:ecs:${AWS::Region}:${AWS::AccountId}:task-definition/groundcrew-*:*"
                Condition:
                  ArnEquals:
                    ecs:cluster: !GetAtt DeployCluster.Arn
              - Sid: EcsTasksInDeployCluster
                Effect: Allow
                Action:
                  - ecs:StopTask
                  - ecs:DescribeTasks
                Resource: !Sub "arn:${AWS::Partition}:ecs:${AWS::Region}:${AWS::AccountId}:task/groundcrew-deploys/*"
              # RegisterTaskDefinition has no resource-level permissions; it must carry the tag.
              - Sid: EcsRegisterTaggedTaskDefinitions
                Effect: Allow
                Action: ecs:RegisterTaskDefinition
                Resource: "*"
                Condition:
                  StringEquals:
                    aws:RequestTag/project: groundcrew
              # Read-only; no resource-level permissions exist for these. Deregistering is not
              # granted at all: it cannot be scoped, task definitions cost nothing, and a
              # rollback uses the previous revision.
              - Sid: EcsReadTaskDefinitions
                Effect: Allow
                Action:
                  - ecs:DescribeTaskDefinition
                  - ecs:ListTaskDefinitions
                Resource: "*"
              - Sid: EcsTagOnlyWhileCreating
                Effect: Allow
                Action: ecs:TagResource
                Resource: "*"
                Condition:
                  StringEquals:
                    ecs:CreateAction:
                      - CreateService
                      - RegisterTaskDefinition
                      - RunTask
                    aws:RequestTag/project: groundcrew
              # --- ECR: only repositories named groundcrew-*
              - Sid: EcrLogin
                Effect: Allow
                Action: ecr:GetAuthorizationToken
                Resource: "*"
              - Sid: EcrCreateTaggedGroundcrewRepositories
                Effect: Allow
                Action:
                  - ecr:CreateRepository
                  - ecr:TagResource
                Resource: !Sub "arn:${AWS::Partition}:ecr:${AWS::Region}:${AWS::AccountId}:repository/groundcrew-*"
                Condition:
                  StringEquals:
                    aws:RequestTag/project: groundcrew
              - Sid: EcrUseGroundcrewRepositories
                Effect: Allow
                Action:
                  - ecr:BatchCheckLayerAvailability
                  - ecr:InitiateLayerUpload
                  - ecr:UploadLayerPart
                  - ecr:CompleteLayerUpload
                  - ecr:PutImage
                  - ecr:BatchGetImage
                  - ecr:GetDownloadUrlForLayer
                  - ecr:DescribeRepositories
                  - ecr:DescribeImages
                  - ecr:ListImages
                  - ecr:BatchDeleteImage
                  - ecr:DeleteRepository
                Resource: !Sub "arn:${AWS::Partition}:ecr:${AWS::Region}:${AWS::AccountId}:repository/groundcrew-*"
              # --- EC2: read the network; security groups only when tagged project=groundcrew
              - Sid: Ec2DescribeNetwork
                Effect: Allow
                Action:
                  - ec2:DescribeVpcs
                  - ec2:DescribeSubnets
                  - ec2:DescribeSecurityGroups
                  - ec2:DescribeSecurityGroupRules
                  - ec2:DescribeNetworkInterfaces
                Resource: "*"
              - Sid: Ec2CreateTaggedSecurityGroups
                Effect: Allow
                Action: ec2:CreateSecurityGroup
                Resource: !Sub "arn:${AWS::Partition}:ec2:${AWS::Region}:${AWS::AccountId}:security-group/*"
                Condition:
                  StringEquals:
                    aws:RequestTag/project: groundcrew
              # CreateSecurityGroup is also authorized against the VPC the group goes in.
              - Sid: Ec2SecurityGroupsInThisRegionsVpcs
                Effect: Allow
                Action: ec2:CreateSecurityGroup
                Resource: !Sub "arn:${AWS::Partition}:ec2:${AWS::Region}:${AWS::AccountId}:vpc/*"
              - Sid: Ec2TagOnlyWhileCreatingSecurityGroups
                Effect: Allow
                Action: ec2:CreateTags
                Resource: !Sub "arn:${AWS::Partition}:ec2:${AWS::Region}:${AWS::AccountId}:security-group/*"
                Condition:
                  StringEquals:
                    ec2:CreateAction: CreateSecurityGroup
              - Sid: Ec2ChangeOnlyGroundcrewSecurityGroups
                Effect: Allow
                Action:
                  - ec2:AuthorizeSecurityGroupIngress
                  - ec2:RevokeSecurityGroupIngress
                  - ec2:DeleteSecurityGroup
                Resource: !Sub "arn:${AWS::Partition}:ec2:${AWS::Region}:${AWS::AccountId}:security-group/*"
                Condition:
                  StringEquals:
                    aws:ResourceTag/project: groundcrew
              # --- CloudWatch Logs: only /groundcrew/*
              - Sid: LogsGroundcrewLogGroups
                Effect: Allow
                Action:
                  - logs:CreateLogGroup
                  - logs:DeleteLogGroup
                  - logs:PutRetentionPolicy
                  - logs:TagResource
                  - logs:TagLogGroup
                  - logs:FilterLogEvents
                Resource: !Sub "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/groundcrew/*"
              - Sid: LogsDescribe
                Effect: Allow
                Action: logs:DescribeLogGroups
                Resource: "*"
              # --- IAM: hand ECS only the task execution role
              - Sid: PassOnlyTheTaskExecutionRoleToEcs
                Effect: Allow
                Action: iam:PassRole
                Resource: !GetAtt TaskExecutionRole.Arn
                Condition:
                  StringEquals:
                    iam:PassedToService: ecs-tasks.amazonaws.com
              # --- Secrets Manager: a deploy's env secrets, groundcrew/<app>/<VAR> only. Write and
              # describe, never read the value (the Deny below; ECS injects it at task start).
              - Sid: SecretsCreateTaggedGroundcrewSecrets
                Effect: Allow
                Action: secretsmanager:CreateSecret
                Resource: !Sub "arn:${AWS::Partition}:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:groundcrew/*"
                Condition:
                  StringEquals:
                    aws:RequestTag/project: groundcrew
              - Sid: SecretsManageGroundcrewSecrets
                Effect: Allow
                Action:
                  - secretsmanager:PutSecretValue
                  - secretsmanager:TagResource
                  - secretsmanager:DeleteSecret
                  - secretsmanager:DescribeSecret
                Resource: !Sub "arn:${AWS::Partition}:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:groundcrew/*"
              # --- Cleanup finds deploys by tag (make cleanup-deploys)
              - Sid: FindTaggedResources
                Effect: Allow
                Action: tag:GetResources
                Resource: "*"
              # Explicit deny wins over any allow, including ones added to this role later.
              - Sid: NeverReadSecretValuesOrData
                Effect: Deny
                Action:
                  - secretsmanager:GetSecretValue
                  - ssm:GetParameter*
                  - kms:Decrypt
                  - s3:GetObject
                  - s3:GetObjectVersion
                Resource: "*"
      Tags:
        - Key: project
          Value: groundcrew

  # ------------------------------------------------------------------ EnableOperate=true only
  # Approved emergency fixes on the customer's own resources. No create or delete; the only IAM
  # action is iam:PassRole on OperatePassRoleArns to ECS tasks, which UpdateService needs to
  # point a service at a revision that has an execution or task role.
  OperateRole:
    Type: AWS::IAM::Role
    Condition: IsOperate
    Properties:
      RoleName: groundcrew-operate
      Description: Groundcrew approved emergency fixes on the listed services, secrets, functions, and instances (no secret values)
      MaxSessionDuration: 3600
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Sid: GroundcrewWithExternalId
            Effect: Allow
            Principal:
              AWS: !Ref GroundcrewPrincipalArn
            Action: sts:AssumeRole
            Condition:
              StringEquals:
                sts:ExternalId: !Ref ExternalId
      Policies:
        - PolicyName: groundcrew-operate
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Sid: EcsRollbackOrRestartListedServices
                Effect: Allow
                Action:
                  - ecs:UpdateService
                  - ecs:DescribeServices
                Resource: !Ref OperateServiceArns
              # No resource-level permissions exist for these; they only read.
              - Sid: EcsReadTaskDefinitions
                Effect: Allow
                Action:
                  - ecs:ListTaskDefinitions
                  - ecs:DescribeTaskDefinition
                Resource: "*"
              - Sid: SecretsMoveVersionStagesOfListedSecrets
                Effect: Allow
                Action:
                  - secretsmanager:UpdateSecretVersionStage
                  - secretsmanager:DescribeSecret
                Resource: !Ref OperateSecretArns
              - Sid: LambdaRollBackAliasesOfListedFunctions
                Effect: Allow
                Action:
                  - lambda:GetAlias
                  - lambda:ListVersionsByFunction
                  - lambda:UpdateAlias
                Resource: !Ref OperateFunctionArns
              - Sid: Ec2RebootListedInstances
                Effect: Allow
                Action: ec2:RebootInstances
                Resource: !Ref OperateInstanceArns
              # Read only, and with no resource-level permissions.
              - Sid: Ec2ReadInstanceStatus
                Effect: Allow
                Action: ec2:DescribeInstanceStatus
                Resource: "*"
              - Sid: PassListedRolesToEcsTasksForRollback
                Effect: Allow
                Action: iam:PassRole
                Resource: !Ref OperatePassRoleArns
                Condition:
                  StringEquals:
                    iam:PassedToService: ecs-tasks.amazonaws.com
              # Even if OperatePassRoleArns is written too wide, Groundcrew's own roles stay out.
              - Sid: NeverPassGroundcrewRoles
                Effect: Deny
                Action: iam:PassRole
                Resource: !Sub "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/groundcrew-*"
              # Explicit deny wins over any allow, including ones added to this role later.
              - Sid: NeverReadSecretValuesOrData
                Effect: Deny
                Action:
                  - secretsmanager:GetSecretValue
                  - ssm:GetParameter*
                  - kms:Decrypt
                  - s3:GetObject
                  - s3:GetObjectVersion
                Resource: "*"
      Tags:
        - Key: project
          Value: groundcrew

Outputs:
  RoleArn:
    Description: Paste this into Groundcrew to finish connecting.
    Value: !GetAtt GroundcrewRole.Arn
  DeployRoleArn:
    Condition: IsDeploy
    Description: The role Groundcrew deploys with (Tier=deploy).
    Value: !GetAtt DeployRole.Arn
  ClusterName:
    Condition: IsDeploy
    Description: ECS cluster for Groundcrew deploys (GC_ECS_CLUSTER).
    Value: !Ref DeployCluster
  TaskExecutionRoleArn:
    Condition: IsDeploy
    Description: ECS task execution role for Groundcrew deploys (GC_TASK_EXECUTION_ROLE_ARN).
    Value: !GetAtt TaskExecutionRole.Arn
  OperateRoleArn:
    Condition: IsOperate
    Description: The role Groundcrew runs approved emergency fixes with (EnableOperate=true).
    Value: !GetAtt OperateRole.Arn
