#!/bin/bash
set -o pipefail

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

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

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

# create oidc provider
function createOidcProvider() {
  local url=$1
  local audience=$2
  local oidcProviderArn="arn:aws:iam::$mAccountId:oidc-provider/$url"
  local oidcProviderList=$(aws iam list-open-id-connect-providers \
    --query "OpenIDConnectProviderList[?Arn=='$oidcProviderArn']" \
    --region=$region --profile=$mAccountProfile --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=$mAccountProfile --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=$mAccountProfile --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=$mAccountProfile --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=$mAccountProfile --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=$mAccountProfile --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=$mAccountProfile --no-cli-pager
  if test $? -ne 0; then
    echo "failed to set role policy $roleName"
    exit 1
  fi
  echo "update iam role policy $roleName"
}

trustPolicy=$(cat <<EOF
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Federated": "arn:aws:iam::$mAccountId:oidc-provider/$GITHUB_URL"
            },
            "Action": "sts:AssumeRoleWithWebIdentity",
            "Condition": {
                "StringEquals": {
                    "$GITHUB_URL:aud": "$AUDIENCE"
                },
                "StringLike": {
                    "$GITHUB_URL:sub": "repo:tidbcloud/auto-deploy:*"
                }
            }
        },
        {
            "Effect": "Allow",
            "Principal": {
                "Service": "config.amazonaws.com"
            },
            "Action": "sts:AssumeRole"
        }
    ]
}
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",
                "ec2:CreateVpcEndpoint",
                "ec2:DeleteVpcEndpoints",
                "ec2:DescribeVpcEndpoints",

                "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",
                "iam:ListInstanceProfilesForRole",

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

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

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

                "route53:ListResourceRecordSets",
                "route53:ChangeResourceRecordSets",
                "route53:AssociateVPCWithHostedZone"
            ],
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "sts:AssumeRole"
            ],
            "Resource": [
                "arn:aws:iam::*:role/auto-deploy-cli"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "iam:PassRole"
            ],
            "Resource": [
                "arn:aws:iam::${mAccountId}:role/eksctl-*"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "secretsmanager:GetSecretValue"
            ],
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "account:ListRegions",
                "account:DisableRegion",
                "account:EnableRegion",
                "account:GetRegionOptStatus",
                "ec2:DescribeRegions"
            ],
            "Resource": [
                "*"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "iam:PassRole",
                "s3:CreateBucket",
                "s3:PutBucketPolicy",
                "s3:PutBucketAcl",
                "s3:ListBucket",
                "s3:PutObject",
                "s3:PutBucketTagging",
                "config:DeleteDeliveryChannel",
                "config:PutDeliveryChannel",
                "config:StartConfigurationRecorder",
                "config:StopConfigurationRecorder",
                "config:PutConfigurationRecorder",
                "config:DeleteConfigurationRecorder"
            ],
            "Resource": [
                "*"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "securityhub:EnableSecurityHub",
                "securityhub:DisableSecurityHub",
                "securityhub:ListEnabledProductsForImport",
                "securityhub:GetFindings"
            ],
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "guardduty:GetDetector",
                "guardduty:CreateDetector",
                "guardduty:DeleteDetector",
                "guardduty:ListDetectors",
                "guardduty:UpdateDetector",
                "guardduty:CreateThreatIntelSet",
                "guardduty:DeleteThreatIntelSet",
                "guardduty:ListThreatIntelSets",
                "guardduty:UpdateThreatIntelSet",
                "guardduty:CreateIPSet",
                "guardduty:DeleteIPSet",
                "guardduty:ListIPSets",
                "guardduty:UpdateIPSet",
                "guardduty:CreateMembers",
                "guardduty:DeleteMembers",
                "guardduty:ListMembers",
                "guardduty:InviteMembers",
                "guardduty:AcceptInvitation",
                "guardduty:GetFindings",
                "guardduty:ListFindings",
                "guardduty:UpdateFindingsFeedback",
                "iam:CreateServiceLinkedRole",
                "iam:GetRole"
            ],
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "rds:AddTagsToResource",
                "rds:CreateDBCluster",
                "rds:CreateDBClusterParameterGroup",
                "rds:CreateDBInstance",
                "rds:CreateDBSubnetGroup",
                "rds:DeleteDBCluster",
                "rds:DeleteDBClusterParameterGroup",
                "rds:DeleteDBInstance",
                "rds:DeleteDBSubnetGroup",
                "rds:ModifyDBCluster",
                "rds:ModifyDBClusterParameterGroup",
                "rds:RemoveTagsFromResource",
                "rds:ModifyDBSubnetGroup",
                "rds:ModifyDBInstance",
                "rds:CreateDBProxy",
                "rds:DeleteDBProxy",
                "rds:ModifyDBProxy",
                "rds:DescribeDBProxies",
                "rds:RegisterDBProxyTargets",
                "rds:DeregisterDBProxyTargets",
                "rds:ModifyDBProxyTargetGroup",

                "vpc-lattice:CreateResourceGateway",
                "vpc-lattice:GetResourceGateway",
                "vpc-lattice:ListResourceGateways",
                "vpc-lattice:DeleteResourceGateway",
                "vpc-lattice:UpdateResourceGateway",
                "vpc-lattice:CreateResourceConfiguration",
                "vpc-lattice:GetResourceConfiguration",
                "vpc-lattice:CreateResourceConfiguration",
                "vpc-lattice:UpdateResourceConfiguration",
                "vpc-lattice:DeleteResourceConfiguration",
                "vpc-lattice:TagResource",
                "vpc-lattice:UntagResource",

                "secretsmanager:CreateSecret",
                "secretsmanager:DeleteSecret",
                "secretsmanager:DescribeSecret",
                "secretsmanager:GetResourcePolicy",
                "secretsmanager:GetResourcePolicy",
                "secretsmanager:ListSecretVersionIds",
                "secretsmanager:PutSecretValue",
                "secretsmanager:TagResource",
                "secretsmanager:UntagResource",

                "ec2:DisassociateAddress",
                "ec2:CreateVpcEndpointServiceConfiguration",
                "ec2:DeleteVpcEndpointServiceConfigurations",
                "ec2:DescribeVpcEndpointServiceConfigurations",

                "elasticloadbalancing:CreateLoadBalancer",
                "elasticloadbalancing:DeleteLoadBalancer",
                "elasticloadbalancing:DescribeLoadBalancers",
                "elasticloadbalancing:CreateListener",
                "elasticloadbalancing:DeleteListener",
                "elasticloadbalancing:DescribeListeners",
                "elasticloadbalancing:CreateTargetGroup",
                "elasticloadbalancing:DeleteTargetGroup",
                "elasticloadbalancing:DescribeTargetGroups",
                "elasticloadbalancing:RegisterTargets",
                "elasticloadbalancing:DeregisterTargets",
                "elasticloadbalancing:AddTags",
                "elasticloadbalancing:RemoveTags",
                "elasticloadbalancing:ModifyLoadBalancerAttributes",
                "elasticloadbalancing:ModifyTargetGroupAttributes",
                "elasticloadbalancing:DescribeListenerAttributes",

                "lambda:CreateFunction",
                "lambda:DeleteFunction",
                "lambda:GetFunction",
                "lambda:UpdateFunctionCode",
                "lambda:GetFunctionConfiguration",
                "lambda:UpdateFunctionConfiguration",
                "lambda:InvokeFunction",
                "lambda:AddPermission",
                "lambda:RemovePermission",
                "lambda:TagResource",
                "lambda:UntagResource",

                "logs:CreateLogGroup",
                "logs:CreateLogStream",
                "logs:PutLogEvents",

                "events:PutRule",
                "events:DeleteRule",
                "events:PutTargets",
                "events:RemoveTargets",
                "events:DescribeRule",
                "events:TagResource",
                "events:UntagResource"
            ],
            "Resource": "*"
        }
    ]
}
EOF
)
wafPolicy=$(cat <<EOF
{
    "Effect": "Allow",
    "Action": [
        "wafv2:GetIPSet",
        "wafv2:UpdateIPSet"
    ],
    "Resource": "arn:aws:wafv2:us-west-2:${mAccountId}:regional/ipset/DBaas-Whitelist-For-Prod-Inner/*"
},
{
    "Effect": "Allow",
    "Action": [
        "wafv2:ListIPSets"
    ],
    "Resource": "arn:aws:wafv2:us-west-2:${mAccountId}:regional/ipset/*/*"
}
EOF
)
if [[ $env == "prod" ]]; then
  policy=$(echo "$policy" | jq ".Statement += [$wafPolicy]")
fi
createRole "auto-deploy-cli" "7200" "$trustPolicy" "$policy"
aws iam attach-role-policy --role-name "auto-deploy-cli" \
  --policy-arn "arn:aws:iam::aws:policy/service-role/AWS_ConfigRole" \
  --region=$region --profile=$mAccountProfile --no-cli-pager

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=$mAccountProfile 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=$mAccountProfile --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${bucketSuffix}"
createBucket "$env-auto-deploy-tf-state${bucketSuffix}"
