#!/bin/bash
set -o pipefail

#
# pre-install:
# aws-cli, jq
#
# Usage
# ./init-m-account.sh profile dev ""
#

profile=$1
env=$2
bucketSuffix=$3
region=us-west-2

# get account id
accountId=$(aws sts get-caller-identity --query "Account" --profile=$profile --output text --no-cli-pager)
echo "accountId=$accountId"

# create oidc provider
function createOidcProvider() {
  local url=$1
  local audience=$2
  local oidcProviderArn="arn:aws:iam::$accountId:oidc-provider/$url"
  local oidcProviderList=$(aws iam list-open-id-connect-providers \
    --query "OpenIDConnectProviderList[?Arn=='$oidcProviderArn']" \
    --region=$region --profile=$profile --no-cli-pager )
  if test $? -ne 0; then
    echo "failed to list oidc provider"
    exit 1
  fi
  local hasCreated=$(echo "$oidcProviderList" | jq "length")
  if [[ $hasCreated == "0" ]]; then
    aws iam create-open-id-connect-provider --url="https://$url" --client-id-list=$audience \
      --region=$region --profile=$profile --no-cli-pager > /dev/null
    if test $? -ne 0; then
      echo "failed to create oidc provider"
      exit 1
    fi
    echo "created oidc provider $GITHUB_TOKEN_URL"
  else
    local clientList=$(aws iam get-open-id-connect-provider \
      --open-id-connect-provider-arn=$oidcProviderArn --query="ClientIDList" \
      --region=$region --profile=$profile --no-cli-pager)
    if test $? -ne 0; then
      echo "failed to get oidc provider"
      exit 1
    fi
    local hasAdded=$(echo "$clientList" | jq ".[] | select(. == \"$audience\") | length")
    if [[ $hasAdded == "0" || $hasAdded == "" ]]; then
      aws iam add-client-id-to-open-id-connect-provider \
        --open-id-connect-provider-arn=$oidcProviderArn --client-id=$audience \
        --region=$region --profile=$profile --no-cli-pager
      if test $? -ne 0; then
        echo "failed to add audience to oidc provider $url"
        exit 1
      else
        echo "added audience to oidc privider $url"
      fi
    else
      echo "oidc privider $url has already created"
    fi
  fi
}
AUDIENCE="auto-deploy-cli"
GITHUB_URL="token.actions.githubusercontent.com"
createOidcProvider $GITHUB_URL $AUDIENCE

# create roles
function createRole() {
  local roleName=$1
  local duration=$2
  local trustPolicy=$3
  local policy=$4
  local roleList=$(aws iam list-roles \
    --query="Roles[?RoleName=='$roleName']" \
    --region=$region --profile=$profile --no-cli-pager)
  if test $? -ne 0; then
    echo "failed to list roles"
    exit 1
  fi
  local hasCreated=$(echo "$roleList" | jq "length")
  if [[ $hasCreated == "0" ]]; then
    aws iam create-role --role-name=$roleName \
      --max-session-duration=$duration --assume-role-policy-document="$trustPolicy" \
      --region=$region --profile=$profile --no-cli-pager > /dev/null
    if test $? -ne 0; then
      echo "failed to create role $roleName"
      exit 1
    fi
    echo "created iam role $roleName"
  else
    echo "iam role $roleName has already created"
  fi
  aws iam put-role-policy --role-name=$roleName \
    --policy-name="$roleName-policy" --policy-document "$policy" \
    --region=$region --profile=$profile --no-cli-pager
  if test $? -ne 0; then
    echo "failed to set role policy $roleName"
    exit 1
  fi
  echo "updated iam role policy $roleName"
}

trustPolicy=$(cat <<EOF
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Federated": "arn:aws:iam::$accountId:oidc-provider/$GITHUB_URL"
            },
            "Action": "sts:AssumeRoleWithWebIdentity",
            "Condition": {
                "StringEquals": {
                    "$GITHUB_URL:aud": "$AUDIENCE"
                },
                "StringLike": {
                    "$GITHUB_URL:sub": "repo:tidbcloud/auto-deploy:*"
                }
            }
        }
    ]
}
EOF
)

policy=$(cat <<EOF
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "autoscaling:AttachInstances",
                "autoscaling:DetachInstances",
                "autoscaling:CreateAutoScalingGroup",
                "autoscaling:DeleteAutoScalingGroup",
                "autoscaling:DescribeAutoScalingGroups",
                "autoscaling:TerminateInstanceInAutoScalingGroup",
                "autoscaling:UpdateAutoScalingGroup",

                "cloudformation:CreateStack",
                "cloudformation:DeleteStack",
                "cloudformation:UpdateStack",
                "cloudformation:DescribeStacks",
                "cloudformation:ListStacks",
                "cloudformation:DescribeStackEvents",
                "cloudformation:DescribeStackResource",
                "cloudformation:DescribeStackResources",
                "cloudformation:GetTemplate",
                "cloudformation:ValidateTemplate",
                "cloudformation:GetTemplateSummary",
                "cloudformation:TagResource",
                "cloudformation:UntagResource",
                "cloudformation:CreateChangeSet",
                "cloudformation:DescribeChangeSet",
                "cloudformation:ExecuteChangeSet",
                "cloudformation:DeleteChangeSet",
                "cloudformation:ListChangeSets",

                "ec2:AllocateAddress",
                "ec2:ReleaseAddress",
                "ec2:DescribeAddresses",
                "ec2:CreateRouteTable",
                "ec2:DeleteRouteTable",
                "ec2:DescribeRouteTables",
                "ec2:AssociateRouteTable",
                "ec2:DisassociateRouteTable",
                "ec2:CreateInternetGateway",
                "ec2:DeleteInternetGateway",
                "ec2:DetachInternetGateway",
                "ec2:AttachInternetGateway",
                "ec2:DescribeInternetGateways",
                "ec2:CreateSecurityGroup",
                "ec2:DeleteSecurityGroup",
                "ec2:DescribeSecurityGroups",
                "ec2:AuthorizeSecurityGroupEgress",
                "ec2:RevokeSecurityGroupEgress",
                "ec2:AuthorizeSecurityGroupIngress",
                "ec2:RevokeSecurityGroupIngress",
                "ec2:CreateLaunchTemplate",
                "ec2:DeleteLaunchTemplate",
                "ec2:CreateLaunchTemplateVersion",
                "ec2:DeleteLaunchTemplateVersions",
                "ec2:DescribeLaunchTemplates",
                "ec2:DescribeLaunchTemplateVersions",
                "ec2:GetLaunchTemplateData",
                "ec2:ModifyLaunchTemplate",
                "ec2:CreateNatGateway",
                "ec2:DeleteNatGateway",
                "ec2:DescribeNatGateways",
                "ec2:CreateRoute",
                "ec2:DeleteRoute",
                "ec2:CreateSubnet",
                "ec2:DeleteSubnet",
                "ec2:DescribeSubnets",
                "ec2:ModifySubnetAttribute",
                "ec2:CreateTags",
                "ec2:DeleteTags",
                "ec2:DescribeTags",
                "ec2:CreateVpc",
                "ec2:DeleteVpc",
                "ec2:DescribeVpcs",
                "ec2:ModifyVpcAttribute",
                "ec2:DescribeInstances",
                "ec2:RunInstances",
                "ec2:TerminateInstances",
                "ec2:DescribeImages",
                "ec2:DescribeInstanceTypeOfferings",

                "eks:CreateAddon",
                "eks:DeleteAddon",
                "eks:DescribeAddon",
                "eks:DescribeAddonConfiguration",
                "eks:DescribeAddonVersions",
                "eks:ListAddons",
                "eks:UpdateAddon",
                "eks:CreateCluster",
                "eks:DeleteCluster",
                "eks:DescribeCluster",
                "eks:DescribeClusterVersions",
                "eks:UpdateClusterConfig",
                "eks:UpdateClusterVersion",
                "eks:AssociateIdentityProviderConfig",
                "eks:DisassociateIdentityProviderConfig",
                "eks:CreateNodegroup",
                "eks:DeleteNodegroup",
                "eks:DescribeNodegroup",
                "eks:ListNodegroups",
                "eks:UpdateNodegroupConfig",
                "eks:DescribeUpdate",
                
                "eks:ListUpdates",
                "eks:TagResource",
                "eks:UntagResource",
                                
                "iam:AttachRolePolicy",
                "iam:DeleteRolePolicy",
                "iam:DetachRolePolicy",
                "iam:GetRolePolicy",
                "iam:PutRolePolicy",
                "iam:CreatePolicy",
                "iam:DeletePolicy",
                "iam:CreateRole",
                "iam:DeleteRole",
                "iam:GetRole",
                "iam:TagRole",
                "iam:UntagRole",
                "iam:ListRolePolicies",
                "iam:ListAttachedRolePolicies",
                "iam:UpdateAssumeRolePolicy",
                "iam:CreateInstanceProfile",
                "iam:DeleteInstanceProfile",
                "iam:GetInstanceProfile",
                "iam:CreateOpenIDConnectProvider",
                "iam:DeleteOpenIDConnectProvider",
                "iam:GetOpenIDConnectProvider",
                "iam:ListOpenIDConnectProviders",
                "iam:TagOpenIDConnectProvider",
                "iam:UntagOpenIDConnectProvider",

                "s3:PutObject",
                "s3:GetObject",
                "s3:GetBucketLocation",

                "s3:ListBucket",
                "s3:DeleteObject",

                "secretsmanager:ReplicateSecretToRegions",
                
                "route53:GetHostedZone",
                "route53:ListHostedZonesByName",
                "route53:ListHostedZones",
                "ec2:DescribeAvailabilityZones",

                "route53:ListResourceRecordSets",
                "route53:ChangeResourceRecordSets"
            ],
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "sts:AssumeRole"
            ],
            "Resource": [
                "arn:aws:iam::*:role/auto-deploy-cli"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "iam:PassRole"
            ],
            "Resource": [
                "arn:aws:iam::${accountId}:role/eksctl-*"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "account:ListRegions",
                "account:DisableRegion",
                "account:EnableRegion",
                "account:GetRegionOptStatus",
                "ec2:DescribeRegions"
            ],
            "Resource": [
                "*"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "sns:GetTopicAttributes",
                "sns:SetTopicAttributes",
                "sns:AddPermission",
                "sns:RemovePermission"
            ],
            "Resource": [
                "*"
            ]
        },
        {
            "Action": [
                "ec2:*",
                "elasticloadbalancing:*",
                "autoscaling:*",
                "cloudwatch:*",
                "ebs:*",

                "ecr:*",
                "eks:*",

                "s3:*",
                "s3-object-lambda:*",
                "waf:*",
                "waf-regional:*",
                "wafv2:*",

                "lambda:*",
                "route53:*",
                "route53domains:*",

                "iam:*",
                "kms:*",
                "cloudformation:*",
                "acm:*",
                "tag:*",
                "events:*"
            ],
            "Effect": "Allow",
            "Resource": "*"
        }
    ]
}
EOF
)
createRole "auto-deploy-cli" "7200" "$trustPolicy" "$policy"

guardPolicy=$(cat <<EOF
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "FixRepoPermission",
            "Effect": "Allow",
            "Action": [
                "ecr:GetRepositoryPolicy",
                "ecr:SetRepositoryPolicy"
            ],
            "Resource": "*"
        },
        {
            "Sid": "DockerLogin",
            "Effect": "Allow",
            "Action": [
                "ecr:GetAuthorizationToken"
            ],
            "Resource": "*"
        },
        {
           "Sid": "PushAutoDeployImage",
           "Effect": "Allow",
            "Action": [
                "ecr:CreateRepository",
                "ecr:CompleteLayerUpload",
                "ecr:DescribeRepositories",
                "ecr:UploadLayerPart",
                "ecr:InitiateLayerUpload",
                "ecr:PutImage",
                "ecr:GetDownloadUrlForLayer",
                "ecr:BatchCheckLayerAvailability",
                "ecr:BatchGetImage",
                "ecr:ListTagsForResource",
                "ecr:TagResource"
            ],
            "Resource": "arn:aws:ecr:us-west-2:*:repository/auto-deploy/*"
        }
    ]
}
EOF
)
createRole "auto-deploy-account-guard" "7200" "$trustPolicy" "$guardPolicy"

# create bucket
function createBucket() {
  local bucketName=$1
  stderr=$(aws s3 ls "s3://$bucketName" --profile=$profile 2>&1)
  if test $? -ne 0; then
    if [[ $stderr == *"An error occurred (NoSuchBucket) when calling the ListObjectsV2 operation"* ]]; then
      aws s3 mb "s3://$bucketName" --region=$region --profile=$profile --no-cli-pager
      if test $? -ne 0; then
        echo "failed to create bucket $bucketName"
      fi
    elif [[ $stderr == *"An error occurred (AccessDenied) when calling the ListObjectsV2 operation"* ]]; then
      echo "bucket $bucketName has created by other account"
      exit 1
    else
      echo "failed to list bucket $bucketName"
      exit 1
    fi
  else
    echo "bucket $bucketName has already created"
  fi
}
createBucket "$env-auto-deploy-o11y${bucketSuffix}"
# createBucket "$env-auto-deploy-tf-state${bucketSuffix}"
