#  ⠀⠀⠀⠀⠀⠀⠀⢀⣠⠤⠴⠒⠒⠒⠒⠒⠒⠒⠒⠦⢤⣀⡀⠀⠀⠀⠀⠀⠀⠀
#  ⠀⠀⠀⠀⣀⡴⠚⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠉⠳⢦⡀⠀⠀⠀⠀
#  ⠀⠀⢠⠞⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠙⢳⡀⠀⠀
#  ⠀⣰⠃⠀⠀⠀⠀⠀⠀ Hi there!! ⠀⠀⠀⠀⠀⠀⠙⣆⠀
#  ⢰⠃⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠸⡆
#  ⡟⠀⠀⠀ We hand-crafted this⠀⠀⠀⠀⣷
#  ⣧⠀⠀ ⠀⠀template for your⠀⠀⠀⠀⠀⠀⡿
#  ⢸⡄⠀⠀⠀ reading pleasure.⠀⠀⠀⠀⠀⢠⠇
#  ⠀⢳⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⢠⡯⠁
#  ⠀⠀⠙⢦⡀⠀⠀⠀⠀⠀  Enjoy!⠀⠀⠀⠀⠀⠀⢀⡴⠋⠁⠀
#  ⠀⠀⠀⠀⠙⠦⣄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣠⠶⠋⠀⠀⠀⠀
#  ⠀⠀⠀⠀⠀⠀⠀⠉⠛⠲⠤⢤⣀⠀⠀⠀⠀⠀⢶⠶⠛⠉⠀⠀⠀⠀⠀⠀⠀
#  ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠙⠳⢦⣄⡀⠈⠳⣄⠀⠀⠀⠀⠀⠀⠀⠀
#  ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠙⠓⢦⣝⣦⡀⠀⠀⠀⠀⠀⠀
#  ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠙⣓⠀⠀⠀⠀⠀⠀
#
#  ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣀⣤⣤⣤⣤⣤⣤⣤⣤⣤⣤⣀⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
#  ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣠⣴⣶⠿⠛⠛⠛⠉⠉⠉⠉⠀⠀⠉⠉⠉⠛⠛⠿⣶⣤⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
#  ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣤⣾⠿⠋⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠛⠻⣦⣤⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
#  ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣴⠟⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⠻⠷⣶⣦⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
#  ⠀⠀⠀⠀⠀⠀⠀⠀⣠⣾⠟⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢈⣻⣷⠀⠀⠀⠀⠀⠀⠀⠀⠀
#  ⠀⠀⠀⠀⢀⣠⣴⡾⠟⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⣾⣻⣿⠏⠀⠀⠀⠀⠀⠀⠀⠀⠀
#  ⠀⠀⠀⢠⣿⠟⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣀⣀⣀⠀⠀⠀⠀⠀⠘⣿⡟⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
#  ⠀⠀⠀⢻⣿⣶⢶⣤⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣀⡀⠀⠀⢼⡿⠟⠛⠛⠿⠀⠀⠀⠀⠀⠘⣿⣆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
#  ⠀⠀⠀⠀⠉⠻⢷⣿⠇⠀⠀⠀⠀⢀⣴⡿⠿⠿⠿⠇⠀⠀⠀⢾⣿⣿⣿⣟⣿⠟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⢿⣧⠀⠀⠀⠀⣀⣀⠀⠀⠀
#  ⠀⠀⠀⠀⠀⠀⣾⡟⠀⠀⠀⠀⠀⠈⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠛⣿⠛⠁⠀⠀⠀⠀⠀⠀⠸⡇⠀⠀⠀⠀⠶⠚⠛⠛⠿⢿⣿⣿⣷⣾⠟⢻⣷⣀⠀
#  ⠀⠀⠀⠀⠀⠀⣿⠃⠀⣀⣀⠀⠀⠀⠀⠀⢸⣷⠀⠀⠀⠀⠀⠀⠀⠀⣿⣄⡀⠀⠀⠀⠀⠀⣀⣼⠇⠀⠀⠀⠀⢤⣤⣄⣀⠀⢸⣿⡿⠀⠀⠀⠈⠛⣿⡇
#  ⠀⠀⠀⠀⠀⣸⣿⠟⠛⠉⠉⠁⠀⠀⠀⠀⠘⢿⣦⣄⣀⣀⣀⣠⣴⠞⠉⠙⠛⢿⣶⠶⠶⠿⠛⠁⠀⠀⠀⠀⠀⠀⠀⠉⠙⢷⣾⠟⠁⠀⠀⠀⠀⠀⣿⡅
#  ⠀⠀⠀⣴⠟⢻⡇⠀⠀⣠⡴⠖⠀⠀⠀⠀⠀⠀⠉⠉⠛⠛⠛⠻⣧⡀⠀⠀⢀⣴⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⡾⠋⠀⠀⠀⠀⠀⠀⢀⣿⠏
#  ⠀⠀⠈⠁⠀⢸⣇⣴⠞⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠛⠷⠾⠟⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣼⠟⠁⠀⠀⠀⠀⠀⠀⠀⣾⠃⠀
#  ⠀⠀⠀⠀⠀⢠⣿⣧⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⡾⠃⠀⠀⠀⠀⠀⠀⠀⠀⣼⡟⠀⠀
#  ⠀⠀⠀⠀⢠⡿⠉⢿⣷⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⠜⠁⠀⠀⠀⠀⠀⠀⠀⠀⣰⣿⠁⠀⠀
#  ⠀⠀⠀⠀⠘⠀⠀⢸⣿⠻⢷⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠋⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⣰⣿⠃⠀⠀⠀
#  ⠀⠀⠀⠀⠀⠀⢠⣿⠃⠀⠀⠈⠓⠒⢦⠰⠆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣿⠃⠀⠀⠀⠀
#  ⠀⠀⠀⠀⠀⢠⣿⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣰⡿⠃⠀⠀⠀⠀⠀
#  ⠀⠀⠀⠀⢠⣿⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⡟⠁⠀⠀⠀⠀⠀⠀
#  ⠀⠀⠀⢀⣿⠇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⠀⠀⠀⠀⠀⠀⠀⠀
#  ⠀⠀⢀⣼⡟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢰⣿⠀⠀⠀⠀⠀⠀⠀⠀
#  ⠀⣠⣾⠟⠀⠀⠀⠀⠀⠀⠀⠀⣠⣤⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⣿⠀⠀⠀⠀⠀⠀⠀⠀
#  ⠈⢿⣇⡀⠀⠀⠀⢀⣠⣴⣶⡿⠛⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⠀⠀⠀⠀⠀⠀⠀⠀
#  ⠀⠈⠻⢷⣶⣶⣿⠿⠛⠉⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⠀⠀⠀⠀⠀⠀⠀⠀
#  ⠀⠀⠀⠀⠀⠀⠟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠿⠀⠀⠀⠀⠀⠀⠀⠀
#
# Typically we would use the AWS CDK instead of writing CloudFormation templates directly, but CDK
# output is much less readable, and we feel it's important that you can review the templates you're
# deploying with ease.
Description: Cloud Snitch AWS Integration (V3)
#
# These are the input parameters you'll need to provide us.
Parameters:
    CloudSnitchAWSAccountId:
        Type: String
        Description: The id of Cloud Snitch's AWS account. You'll be granting this account access to your resources.
    TeamId:
        Type: String
        Description: The id of the team that the integration is being added to.
    AllowOrganizationsAccess:
        Type: String
        Default: 'Yes'
        AllowedValues:
            - 'Yes'
            - 'No'
        Description: Whether to allow read access to AWS Organizations data such as account names.
    AllowSCPManagement:
        Type: String
        Default: 'No'
        AllowedValues:
            - 'Yes'
            - 'No'
        Description: Whether to allow the creation of additional service control policies for accounts.
    S3BucketName:
        Type: String
        Default: ''
        Description: The name of the S3 bucket where your CloudTrail logs are stored.
    S3KeyPrefix:
        Type: String
        Default: ''
        Description: The key prefix within the S3 bucket where your CloudTrail logs are stored.
#
# In the interest of least privilege, we conditionally create policies based on the parameters
# provided. To do that we create some simple conditions:
Conditions:
    HasOrganizationsAccess: !Equals [!Ref AllowOrganizationsAccess, 'Yes']
    HasS3BucketName: !Not [!Equals [!Ref S3BucketName, '']]
    HasSCPManagement: !Equals [!Ref AllowSCPManagement, 'Yes']
#
# Now for the resources...
Resources:
    # This is the role that Cloud Snitch will assume to access your resources. By default, it has no
    # permissions.
    IntegrationRole:
        Type: AWS::IAM::Role
        Properties:
            AssumeRolePolicyDocument:
                Statement:
                    - Action: sts:AssumeRole
                      Effect: Allow
                      Condition:
                          # To prevent the confused deputy problem, we make absolutely sure that
                          # this role can only be used for the given team id.
                          StringEquals:
                              sts:ExternalId: !Ref TeamId
                      Principal:
                          # This allows the Cloud Snitch AWS account to assume this role. The
                          # reference to "root" in the ARN may be misleading - as a matter of best
                          # practice, our root users are disabled and root activity is disallowed in
                          # our accounts. But this let's us create policies that can grant the
                          # ability to assume the role.
                          AWS: !Sub 'arn:aws:iam::${CloudSnitchAWSAccountId}:root'
                Version: '2012-10-17'
            Description: The role that grants the required read access to Cloud Snitch.
    # If you specify that you want to allow Cloud Snitch to read AWS Organizations data, we create a
    # policy and attach it to the role.
    IntegrationOrganizationsPolicy:
        Condition: HasOrganizationsAccess
        Type: AWS::IAM::Policy
        Properties:
            PolicyDocument:
                Statement:
                    - Action: organizations:ListAccounts
                      Effect: Allow
                      Resource: '*'
                Version: '2012-10-17'
            PolicyName: organizations
            Roles:
                - !Ref IntegrationRole
    # If you specify that you want to allow Cloud Snitch to manage service control policies, we
    # create a policy and attach it to the role.
    IntegrationSCMManagementPolicy:
        Condition: HasSCPManagement
        Type: AWS::IAM::Policy
        Properties:
            PolicyDocument:
                Statement:
                    # These actions allow us to get general information about the organization.
                    - Action:
                          - organizations:DescribePolicy
                          - organizations:ListAccounts
                          - organizations:ListChildren
                          - organizations:ListParents
                          - organizations:ListPoliciesForTarget
                          - organizations:ListRoots
                          - iam:GenerateOrganizationsAccessReport
                          - iam:GetOrganizationsAccessReport
                      Effect: Allow
                      Resource: '*'
                    # Allow attaching service control policies to accounts. Note that attaching and
                    # detaching policies also requires permissions on the policy resource, which are
                    # granted in the next statement.
                    - Action:
                          - organizations:AttachPolicy
                          - organizations:DetachPolicy
                      Effect: Allow
                      Resource: !Sub 'arn:aws:organizations::${AWS::AccountId}:account/*'
                      Condition:
                          StringEquals:
                              organizations:PolicyType: SERVICE_CONTROL_POLICY
                    # Allow operating on service control policies that are tagged as being managed
                    # by Cloud Snitch. Permission is not granted to operate on any other policies.
                    - Action:
                          - organizations:AttachPolicy
                          - organizations:DetachPolicy
                          - organizations:CreatePolicy
                          - organizations:DeletePolicy
                          - organizations:UpdatePolicy
                      Effect: Allow
                      Resource: '*'
                      Condition:
                          StringEquals:
                              aws:ResourceTag/CloudSnitchManaged: 'true'
                              organizations:PolicyType: SERVICE_CONTROL_POLICY
                    # Lastly, we need to be able to assign the Cloud Snitch managed tag to policies,
                    # but only on creation. We cannot add the tag to existing policies.
                    - Action: organizations:TagResource
                      Effect: Allow
                      Resource: !Sub 'arn:aws:organizations::${AWS::AccountId}:policy/*'
                      Condition:
                          # There's an important subtlety at play here: When the policy is created,
                          # aws:ResourceTag is based on the tags in the request. Otherwise, the
                          # aws:ResourceTag is based on the tags already on the resource. This
                          # allows us to have full control over our policies without being able to
                          # touch existing policies.
                          StringEquals:
                              aws:ResourceTag/CloudSnitchManaged: 'true'
                Version: '2012-10-17'
            PolicyName: scp-management
            Roles:
                - !Ref IntegrationRole
    # If you specify an S3 bucket name, we create a policy for read access and attach it to the role.
    IntegrationS3Policy:
        Condition: HasS3BucketName
        Type: AWS::IAM::Policy
        Properties:
            PolicyDocument:
                Statement:
                    - Action:
                          - s3:GetObject
                          - s3:ListBucket
                      Effect: Allow
                      Resource:
                          - !Sub 'arn:aws:s3:::${S3BucketName}'
                          - !Sub 'arn:aws:s3:::${S3BucketName}/${S3KeyPrefix}*'
                Version: '2012-10-17'
            PolicyName: s3
            Roles:
                - !Ref IntegrationRole
#
# To make it easier to provide the role ARN to Cloud Snitch, we output it here.
Outputs:
    RoleArn:
        Description: The ARN of the role assumed by the integration.
        Value: !GetAtt IntegrationRole.Arn
