From 3dcebb63f45e30e719384b3f8438bd30919835fb Mon Sep 17 00:00:00 2001 From: Yashar Hosseinpour <44054107+bugfloyd@users.noreply.github.com> Date: Sun, 28 Apr 2024 19:45:34 +0200 Subject: [PATCH] Add GitHub workflows (#4) * Add GitHub workflow to build and upload * Simplify terraform deployments * Remove terraform workspaces * Combine tf_backend and init stacks and deploy tf backend to the same AWS account * Add terraform jobs to GitHub actions workflow * Set bot webhook in GitHub workflow --- .github/workflows/main.yml | 67 +++++++++++++++++++++++++ README.md | 87 ++++++++------------------------- bot/go.mod | 2 +- infra/init/backend.tf | 7 --- infra/init/main.tf | 13 +---- infra/init/pipeline_exec.tf | 7 ++- infra/init/terraform_backend.tf | 52 ++++++++++++++++++++ infra/init/variables.tf | 19 ++++--- infra/main.tf | 28 +++-------- infra/tf_backend/main.tf | 23 --------- infra/tf_backend/variables.tf | 21 -------- infra/variables.tf | 9 ---- 12 files changed, 167 insertions(+), 168 deletions(-) create mode 100644 .github/workflows/main.yml delete mode 100644 infra/init/backend.tf create mode 100644 infra/init/terraform_backend.tf delete mode 100644 infra/tf_backend/main.tf delete mode 100644 infra/tf_backend/variables.tf diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..6ef0405 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,67 @@ +name: Build, Upload and Deploy + +on: + push: + branches: + - main + +jobs: + build-upload-deploy: + runs-on: ubuntu-latest + + permissions: + id-token: write + contents: read + steps: + - name: Set up Go + uses: actions/setup-go@v4 + with: + go-version: '1.21' + + - name: Checkout code + uses: actions/checkout@v4 + + - name: Build + run: | + cd bot + GOARCH=amd64 GOOS=linux go build -o bootstrap main.go + + - name: Zip + run: | + cd bot + zip lambda_function.zip bootstrap + + - name: Configure AWS credentials to upload bundle + uses: aws-actions/configure-aws-credentials@v4 + with: + aws-region: ${{ vars.AWS_REGION }} + role-to-assume: ${{ vars.PIPELINE_EXEC_ROLE_ARN }} + role-session-name: GitHubActions + role-duration-seconds: 3600 + + - name: Upload to S3 + run: aws s3 cp bot/lambda_function.zip s3://${{ vars.S3_LAMBDA_BUCKET }}/lambda_function.zip + + - name: Set up Terraform + uses: hashicorp/setup-terraform@v3 + with: + terraform_wrapper: false + + - name: Terraform init + run: | + cd infra + terraform init \ + -backend-config="region=${{ vars.AWS_REGION }}" \ + -backend-config="bucket=${{ vars.TERRAFORM_STATE_BUCKET }}" + + - name: Terraform apply + run: | + cd infra + terraform apply -auto-approve -var aws_region=${{ vars.AWS_REGION }} -var lambda_bucket=${{ vars.S3_LAMBDA_BUCKET }} -var bot_token=${{ secrets.BOT_TOKEN }} + + - name: Set webhook URL + shell: bash + run: | + cd infra + WEBHOOK_URL=$(terraform output -raw webhook_url) + curl https://api.telegram.org/bot${{ secrets.BOT_TOKEN }}/setWebhook -F "url=$WEBHOOK_URL" diff --git a/README.md b/README.md index 3accd45..b1dbb3f 100644 --- a/README.md +++ b/README.md @@ -1,63 +1,27 @@ -# Anonymous Telegram Chat Bot [UNDER DEVELOPMENT] +# Anonymous Telegram Chat Bot ## Deployment -### Terraform Backend -First we create a S3 bucket to store Terraform state, a DynamoDB table to persist Terraform state lock and a S3 bucket to deploy Lambda function code bundles. The Terraform state for this init stack is being kept locally. -```shell -cd infra/tf_backend -terraform init # Run once -``` - -Create a `terraform.tfvars` file in `infra/init`: +### Deploy Init Resources +Now we create and deploy the resources needed for the main stack including a S3 bucket to store Terraform state, a DynamoDB table to persist Terraform state lock and a S3 bucket to deploy Lambda function code bundles. Go to `infra/init` directory and create a file named `terraform.tfvars`: ```hcl -aws_region = -aws_profile = -terraform_state_bucket = -``` -**Note:** Here `` and `` are the profile (and/or account) and the region that we use to store and manage terraform state and lock. - -Now run the plan command: -```shell -terraform plan -out tf_backend.tfplan -``` - -After planning for the changes, apply the changeset: -```shell -terraform apply "tf_backend.tfplan" +init_aws_region = "" +terraform_state_bucket = "" +init_lambda_bucket = "" +github_repo = "" ``` - -### Deploy Initial Resources -Now we create and deploy the resources needed for the main stack. Go to `infra/init` directory and create a file named `backend_config.hcl`: -```hcl -profile = -bucket = -region = -``` -**Note:** Here `` and `` are the profile (and/or account) and the region that we use to store and manage terraform state and lock. - -Create a file named `terraform.tfvars`: -```hcl -init_aws_region = -init_aws_profile = { - development = - production = -} -init_lambda_bucket = -github_repo = -``` -**Note:** Here ``, `` and `` are the profile (and/or account) and the region that we use to deploy the main resources. +**Note:** The Terraform state for this init stack is being kept locally. Now plan and apply the changeset: ```shell -terraform init -backend-config backend_config.hcl # Run once -terraform plan -out init.tfplan -terraform apply "init.tfplan" +terraform init # Run once +AWS_PROFILE= terraform plan -out init.tfplan +AWS_PROFILE= terraform apply "init.tfplan" ``` ### Build and Bundle ```shell cd bot - +`` GOARCH=amd64 GOOS=linux go build -o bootstrap main.go ``` **Note:** In order to use the binary as the Lambda handler, it should be named `bootstrap`. @@ -72,37 +36,28 @@ Upload the build zip bundle to S3: ```shell aws s3 cp lambda_function.zip s3:///lambda_function.zip --profile ``` -**Note:** Here `` is the profile (and/or account) that we use to deploy the main resources. ### Deploy Main AWS Resources #### Add Terraform Variables File Create a file named `infra/terraform.tfvars`: ```hcl -aws_region = -aws_profile = { - development = - production = -} -lambda_bucket = -bot_token = +aws_region = "" +lambda_bucket = "" +bot_token = "" ``` -**Note:** Here ``, `` and `` are the profile (and/or account) and the region that we use to deploy the main resources. #### Initialize Terraform Create a Terraform backend configuration file named `infra/backend_config.hcl`: ```hcl -region = -profile = -bucket = +region = "" +bucket = "" ``` -**Note:** Here `` and `` are the profile (and/or account) and the region that we use to store and manage terraform state and lock. Initialize the main Terraform stack: ```shell cd infra -terraform init -backend-config backend_config.hcl # Run once -terraform workspace new production # Run once - Repeat it for other workspaces -terraform workspace select production + +AWS_PROFILE= init -backend-config backend_config.hcl # Run once ``` #### Deploy @@ -110,8 +65,8 @@ Use `terraform plan` to create a changeset and run `terraform apply` command to ```shell cd infra -terraform plan -out main.tfplan -terraform apply "main.tfplan" +AWS_PROFILE= terraform plan -out main.tfplan +AWS_PROFILE= terraform apply "main.tfplan" ``` ### Register Bot Webhook diff --git a/bot/go.mod b/bot/go.mod index 55714cc..5c7209f 100644 --- a/bot/go.mod +++ b/bot/go.mod @@ -1,6 +1,6 @@ module github.com/bugfloyd/anonymous-telegram-bot -go 1.21.9 +go 1.21 require ( github.com/PaulSonOfLars/gotgbot/v2 v2.0.0-rc.25 diff --git a/infra/init/backend.tf b/infra/init/backend.tf deleted file mode 100644 index 808d46c..0000000 --- a/infra/init/backend.tf +++ /dev/null @@ -1,7 +0,0 @@ -terraform { - backend "s3" { - key = "anonymous-chat-state/init/terraform.tfstate" - encrypt = true - dynamodb_table = "anonymous-chatbot-terraform-state-lock" - } -} \ No newline at end of file diff --git a/infra/init/main.tf b/infra/init/main.tf index 603bbac..42dfbe5 100644 --- a/infra/init/main.tf +++ b/infra/init/main.tf @@ -1,17 +1,8 @@ -locals { - workspace_prefixes = { - default = "dev" - development = "dev" - production = "prod" - } -} - provider "aws" { - region = var.init_aws_region - profile = var.init_aws_profile[terraform.workspace] + region = var.init_aws_region } # S3 bucket to store lambda function codes resource "aws_s3_bucket" "lambda_bucket" { - bucket = "${local.workspace_prefixes[terraform.workspace]}.${var.init_lambda_bucket}" + bucket = var.init_lambda_bucket } \ No newline at end of file diff --git a/infra/init/pipeline_exec.tf b/infra/init/pipeline_exec.tf index 4b5042b..edefc0e 100644 --- a/infra/init/pipeline_exec.tf +++ b/infra/init/pipeline_exec.tf @@ -31,13 +31,16 @@ resource "aws_iam_role" "github_actions" { }) } -# Attach policies to the IAM role as needed -# Example: attaching a read-only policy resource "aws_iam_role_policy_attachment" "administrator_access" { role = aws_iam_role.github_actions.name policy_arn = "arn:aws:iam::aws:policy/AdministratorAccess" } +resource "aws_iam_role_policy_attachment" "terraform_backend_access" { + role = aws_iam_role.github_actions.name + policy_arn = aws_iam_policy.terraform_backend_access_policy.arn +} + output "pipeline_execution_role_arn" { value = aws_iam_role.github_actions.arn } diff --git a/infra/init/terraform_backend.tf b/infra/init/terraform_backend.tf new file mode 100644 index 0000000..57a7ace --- /dev/null +++ b/infra/init/terraform_backend.tf @@ -0,0 +1,52 @@ +resource "aws_s3_bucket" "terraform_state" { + bucket = var.terraform_state_bucket +} + +resource "aws_dynamodb_table" "terraform_lock" { + name = var.terraform_state_lock_dynamodb_table + billing_mode = "PAY_PER_REQUEST" + hash_key = "LockID" + + attribute { + name = "LockID" + type = "S" + } + + tags = { + Name = "TerraformStateLocking" + } +} + +resource "aws_iam_policy" "terraform_backend_access_policy" { + name = "TerraformBackendAccessPolicy" + path = "/" + description = "Policy for allowing access to terraform backend S3 bucket and DynamoDB state lock table" + + policy = <