# Cloud GUI read-only access, template v3.
#
# Creates ONE IAM role in your account that Cloud GUI (AWS account 487275459989)
# can use, for at most an hour at a time, to view what's in this account. Every
# permission is a read. Nothing here can create, change or delete anything.
#
# What the role can read:
# - AWS's own ViewOnlyAccess policy: the names and settings of resources across
#   AWS services (buckets, functions, servers, databases, IAM users and roles, and
#   so on), not what's inside them. AWS maintains it and lists every action:
#   https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_job-functions.html#jf_view-only-user
# - A few reads that policy leaves out, listed under cloudgui-viewer below.
# - Minus the reads it would allow that can hold secrets or details about people
#   (startup scripts, environment variables, job arguments, your app's users,
#   subscribers): explicitly denied in the NeverReadSecretsOrPeople statement.
# - Log lines from CloudWatch Logs, only while AllowLogs is 'true'. Log lines are
#   whatever your code prints, so they get their own switch: 'false' removes that
#   permission and everything else keeps working.
#
# How Cloud GUI uses it:
# - Cloud GUI never receives access keys. It asks AWS STS for a short-lived session on
#   this role, and only when the request carries your connection's ExternalId.
# - Each session is stamped with the Cloud GUI user's email (sts:SetSourceIdentity),
#   so every call shows up in your CloudTrail under the person who made it.
# - To revoke access, delete this stack. It takes effect immediately.
#
# Plain-English version: https://cloudgui.com/security

AWSTemplateFormatVersion: '2010-09-09'
Description: >-
  Cloud GUI read-only access (v3). One IAM role that Cloud GUI can use to view
  this account: AWS's ViewOnlyAccess policy, a few extra reads, and log lines while
  AllowLogs is true. Delete this stack to revoke access.

Parameters:
  ConnectionId:
    Type: String
    Description: Identifies this connection in Cloud GUI. Filled in for you.
    AllowedPattern: '[0-9a-f]{8}'
  ExternalId:
    Type: String
    Description: Cloud GUI must present this value to use the role. Filled in for you.
    AllowedPattern: '[0-9a-f]{32}'
  AllowLogs:
    Type: String
    Default: 'true'
    AllowedValues: ['true', 'false']
    Description: Let Cloud GUI show your CloudWatch log lines. They can contain anything your code prints. 'false' keeps them out, and everything else keeps working.

Conditions:
  LogsAllowed: !Equals [!Ref AllowLogs, 'true']

Resources:
  ViewerRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub 'cloudgui-viewer-${ConnectionId}'
      Description: Read-only access for Cloud GUI (cloudgui.com). Delete the CloudFormation stack to revoke.
      MaxSessionDuration: 3600
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          # Cloud GUI may use this role only with your ExternalId, and only when the
          # session names the Cloud GUI user it's for (so CloudTrail always can).
          - Sid: CloudGUIWithExternalId
            Effect: Allow
            Principal:
              AWS: 'arn:aws:iam::487275459989:root'
            Action: sts:AssumeRole
            Condition:
              StringEquals:
                sts:ExternalId: !Ref ExternalId
              'Null':
                sts:SourceIdentity: 'false'
          # Lets that session carry the user's email as its source identity. On its
          # own it grants nothing: it only applies within an AssumeRole allowed above.
          - Sid: NameTheUser
            Effect: Allow
            Principal:
              AWS: 'arn:aws:iam::487275459989:root'
            Action: sts:SetSourceIdentity
      # AWS's view-only job-function policy: names and settings, not contents.
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/job-function/ViewOnlyAccess
      Policies:
        - PolicyName: cloudgui-viewer
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              # Find which region each S3 bucket is in
              - Sid: FindBucketRegions
                Effect: Allow
                Action:
                  - s3:GetBucketLocation
                Resource: '*'
              # View one Lambda function's settings and web address
              - Sid: ViewFunctionSettings
                Effect: Allow
                Action:
                  - lambda:GetFunctionConfiguration
                  - lambda:GetFunctionUrlConfig
                Resource: '*'
              # View Aurora DSQL clusters
              - Sid: ViewDsqlClusters
                Effect: Allow
                Action:
                  - dsql:ListClusters
                  - dsql:GetCluster
                Resource: '*'
              # View a CloudFront distribution's full settings
              - Sid: ViewWebsites
                Effect: Allow
                Action:
                  - cloudfront:GetDistribution
                Resource: '*'
              # See when certificates expire
              - Sid: ViewCertificates
                Effect: Allow
                Action:
                  - acm:DescribeCertificate
                Resource: '*'
              # See which servers Systems Manager can reach
              - Sid: ViewServerAgents
                Effect: Allow
                Action:
                  - ssm:DescribeInstanceInformation
                Resource: '*'
              # Check whether S3 buckets are public
              - Sid: CheckPublicBuckets
                Effect: Allow
                Action:
                  - s3:GetBucketPolicyStatus
                  - s3:GetBucketPublicAccessBlock
                  - s3:GetAccountPublicAccessBlock
                Resource: '*'
              # See what your account costs
              - Sid: ReadCosts
                Effect: Allow
                Action:
                  - ce:GetCostAndUsage
                  - ce:GetCostForecast
                Resource: '*'
              # NEVER, even though ViewOnlyAccess allows them: settings and lists that can
              # hold secrets or details about people. An explicit Deny overrides any Allow.
              - Sid: NeverReadSecretsOrPeople
                Effect: Deny
                Action:
                  # Startup scripts
                  - ec2:DescribeInstanceAttribute
                  - ec2:DescribeLaunchTemplateVersions
                  - ec2:DescribeSpotInstanceRequests
                  - ec2:DescribeSpotFleetRequests
                  - autoscaling:DescribeLaunchConfigurations
                  - sagemaker:DescribeNotebookInstanceLifecycleConfig
                  - sagemaker:DescribeStudioLifecycleConfig
                  # Environment variables in containers, ML jobs and test runs
                  - ecs:DescribeTaskDefinition
                  - ecs:DescribeTasks
                  - ecs:DescribeDaemonTaskDefinition
                  - ecs:DescribeExpressGatewayService
                  - sagemaker:DescribeAIRecommendationJob
                  - sagemaker:DescribeAlgorithm
                  - sagemaker:DescribeAutoMLJob
                  - sagemaker:DescribeAutoMLJobV2
                  - sagemaker:DescribeDataQualityJobDefinition
                  - sagemaker:DescribeHyperParameterTuningJob
                  - sagemaker:DescribeInferenceComponent
                  - sagemaker:DescribeModel
                  - sagemaker:DescribeModelBiasJobDefinition
                  - sagemaker:DescribeModelExplainabilityJobDefinition
                  - sagemaker:DescribeModelPackage
                  - sagemaker:DescribeModelQualityJobDefinition
                  - sagemaker:DescribeMonitoringSchedule
                  - sagemaker:DescribeOptimizationJob
                  - sagemaker:DescribePartnerApp
                  - sagemaker:DescribeProcessingJob
                  - sagemaker:DescribeTrainingJob
                  - sagemaker:DescribeTransformJob
                  - sagemaker:ListCandidatesForAutoMLJob
                  - devicefarm:ListProjects
                  - devicefarm:ListRuns
                  - lambda:ListMicrovmImageVersions
                  # Job arguments and stack outputs
                  - glue:GetJobRuns
                  - glue:ListSessions
                  - cloudformation:DescribeStacks
                  - cloudformation:ListExports
                  # Details about people
                  - cognito-idp:ListUsers
                  - cognito-idp:ListUsersInGroup
                  - sns:ListSubscriptions
                  - sns:ListSubscriptionsByTopic
                  - sns:ListPhoneNumbersOptedOut
                  - sns:ListSMSSandboxPhoneNumbers
                  - ses:ListContacts
                  - ses:ListSuppressedDestinations
                  # Activity and conversations
                  - cloudtrail:LookupEvents
                  - lex:GetUtterancesView
                Resource: '*'
      Tags:
        - Key: cloudgui:connection
          Value: !Ref ConnectionId
        - Key: cloudgui:template-version
          Value: '3'

  # Reading log lines. This policy exists only while AllowLogs is 'true'.
  LogsPolicy:
    Type: AWS::IAM::RolePolicy
    Condition: LogsAllowed
    Properties:
      RoleName: !Ref ViewerRole
      PolicyName: cloudgui-viewer-logs
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
          # Read log lines (only while AllowLogs is true)
          - Sid: ReadLogEvents
            Effect: Allow
            Action:
              - logs:FilterLogEvents
              - logs:GetLogEvents
              - logs:StartQuery
              - logs:GetQueryResults
              - logs:StopQuery
            Resource: '*'

Outputs:
  RoleArn:
    Description: The role Cloud GUI uses. Cloud GUI finds it on its own; nothing to copy.
    Value: !GetAtt ViewerRole.Arn
