diff --git a/.github/workflows/terraform-apply.yml b/.github/workflows/terraform-apply.yml index 6e1163d..fd19672 100644 --- a/.github/workflows/terraform-apply.yml +++ b/.github/workflows/terraform-apply.yml @@ -1,73 +1,40 @@ -name: 'Terraform Plan' +name: 'Terraform Apply' on: - pull_request: + push: + branches: + - main + env: - TF_CLOUD_ORGANIZATION: "florenciacomuzzi-org" + TF_CLOUD_ORGANIZATION: "florenciacomuzzi-org" # template field TF_API_TOKEN: "${{ secrets.TF_API_TOKEN }}" - TF_WORKSPACE: "production" + TF_WORKSPACE: "production" # template field CONFIG_DIRECTORY: "./" jobs: terraform: if: github.repository != 'hashicorp-education/learn-terraform-github-actions' - name: "Terraform Plan" + name: "Terraform Apply" runs-on: ubuntu-latest permissions: contents: read - pull-requests: write steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Upload Configuration uses: hashicorp/tfc-workflows-github/actions/upload-configuration@v1.0.0 - id: plan-upload + id: apply-upload with: workspace: ${{ env.TF_WORKSPACE }} directory: ${{ env.CONFIG_DIRECTORY }} - speculative: true - - name: Create Plan Run + - name: Create Apply Run uses: hashicorp/tfc-workflows-github/actions/create-run@v1.0.0 - id: plan-run + id: apply-run with: workspace: ${{ env.TF_WORKSPACE }} - configuration_version: ${{ steps.plan-upload.outputs.configuration_version_id }} - plan_only: true - - name: Get Plan Output - uses: hashicorp/tfc-workflows-github/actions/plan-output@v1.0.0 - id: plan-output + configuration_version: ${{ steps.apply-upload.outputs.configuration_version_id }} + - name: Apply + uses: hashicorp/tfc-workflows-github/actions/apply-run@v1.0.0 + if: fromJSON(steps.apply-run.outputs.payload).data.attributes.actions.IsConfirmable + id: apply with: - plan: ${{ fromJSON(steps.plan-run.outputs.payload).data.relationships.plan.data.id }} - - name: Update PR - uses: actions/github-script@v6 - id: plan-comment - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - // 1. Retrieve existing bot comments for the PR - const { data: comments } = await github.rest.issues.listComments({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: context.issue.number, - }); - const botComment = comments.find(comment => { - return comment.user.type === 'Bot' && comment.body.includes('HCP Terraform Plan Output') - }); - const output = `#### HCP Terraform Plan Output - \`\`\` - Plan: ${{ steps.plan-output.outputs.add }} to add, ${{ steps.plan-output.outputs.change }} to change, ${{ steps.plan-output.outputs.destroy }} to destroy. - \`\`\` - [HCP Terraform Plan](${{ steps.plan-run.outputs.run_link }}) - `; - // 3. Delete previous comment so PR timeline makes sense - if (botComment) { - github.rest.issues.deleteComment({ - owner: context.repo.owner, - repo: context.repo.repo, - comment_id: botComment.id, - }); - } - github.rest.issues.createComment({ - issue_number: context.issue.number, - owner: context.repo.owner, - repo: context.repo.repo, - body: output - }); \ No newline at end of file + run: ${{ steps.apply-run.outputs.run_id }} + comment: "Apply Run from GitHub Actions CI ${{ github.sha }}" diff --git a/.github/workflows/terraform-plan.yml b/.github/workflows/terraform-plan.yml index e69de29..6a64c9c 100644 --- a/.github/workflows/terraform-plan.yml +++ b/.github/workflows/terraform-plan.yml @@ -0,0 +1,106 @@ +name: 'Terraform Plan' +on: + pull_request: +env: + TF_CLOUD_ORGANIZATION: "florenciacomuzzi-org" # template field + TF_API_TOKEN: "${{ secrets.TF_API_TOKEN }}" + TF_WORKSPACE: "production" # template field + CONFIG_DIRECTORY: "./" +jobs: + tflint: + name: "TFLint" + runs-on: ${{ matrix.os }} + + strategy: + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + + steps: + - uses: actions/checkout@v4 + name: Checkout source code + + - uses: actions/cache@v4 + name: Cache plugin dir + with: + path: ~/.tflint.d/plugins + key: ${{ matrix.os }}-tflint-${{ hashFiles('.tflint.hcl') }} + + - uses: terraform-linters/setup-tflint@v4 + name: Setup TFLint + with: + tflint_version: v0.52.0 + - name: Show version + run: tflint --version + + - name: Init TFLint + run: tflint --init + env: + # https://github.com/terraform-linters/tflint/blob/master/docs/user-guide/plugins.md#avoiding-rate-limiting + GITHUB_TOKEN: ${{ github.token }} + + - name: Run TFLint + run: tflint -f compact + terraform: + if: github.repository != 'hashicorp-education/learn-terraform-github-actions' + name: "Terraform Plan" + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: write + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Upload Configuration + uses: hashicorp/tfc-workflows-github/actions/upload-configuration@v1.0.0 + id: plan-upload + with: + workspace: ${{ env.TF_WORKSPACE }} + directory: ${{ env.CONFIG_DIRECTORY }} + speculative: true + - name: Create Plan Run + uses: hashicorp/tfc-workflows-github/actions/create-run@v1.0.0 + id: plan-run + with: + workspace: ${{ env.TF_WORKSPACE }} + configuration_version: ${{ steps.plan-upload.outputs.configuration_version_id }} + plan_only: true + - name: Get Plan Output + uses: hashicorp/tfc-workflows-github/actions/plan-output@v1.0.0 + id: plan-output + with: + plan: ${{ fromJSON(steps.plan-run.outputs.payload).data.relationships.plan.data.id }} + - name: Update PR + uses: actions/github-script@v6 + id: plan-comment + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + // 1. Retrieve existing bot comments for the PR + const { data: comments } = await github.rest.issues.listComments({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + }); + const botComment = comments.find(comment => { + return comment.user.type === 'Bot' && comment.body.includes('HCP Terraform Plan Output') + }); + const output = `#### HCP Terraform Plan Output + \`\`\` + Plan: ${{ steps.plan-output.outputs.add }} to add, ${{ steps.plan-output.outputs.change }} to change, ${{ steps.plan-output.outputs.destroy }} to destroy. + \`\`\` + [HCP Terraform Plan](${{ steps.plan-run.outputs.run_link }}) + `; + // 3. Delete previous comment so PR timeline makes sense + if (botComment) { + github.rest.issues.deleteComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: botComment.id, + }); + } + github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: output + }); \ No newline at end of file diff --git a/SETUP.md b/SETUP.md new file mode 100644 index 0000000..fe67be5 --- /dev/null +++ b/SETUP.md @@ -0,0 +1,33 @@ +# florenciacomuzzi-site-terraform +This repository contains Terraform code to create a Linode instance and deploy a website to it. +The infrastructure is managed using Hashicorp Terraform Cloud. +Deployments are triggered from GitHub Actions workflows. + +## Setting up your own project +* Login to Linode account. +* Create a personal access token. This secret is the value of "token" input variable of the Terraform module. + + +* Login to Terraform Cloud. +* Create an organization like "mysite-org". +* Create a Team Token scoped to owners. This secret is the value of TF_API_TOKEN used by GitHub Actions to authenticate to Hashicorp Terraform Cloud. +* Create a variable set called "MYSITE__PRODUCTION". +* Add the following variables to the set: + * khj +* Create a project like "mysite-site". +* Create a workspace like "production". +* In the workspace, create a workspace variable called "token" with the value of the Linode Personal Access Token. +* Go back to the organization-level variable set you created previously and apply to the workspace specifically. + + +* Clone this repository. Name it like "mysite-site-terraform". +* Create a TF_API_TOKEN repository secret by going to Settings > Secrets and variables > Actions. This secret is used by GitHub Actions to authenticate to Hashicorp Terraform Cloud during runs. +* Change the values of TF_CLOUD_ORGANIZATION and TF_WORKSPACE in .github/workflows/terraform-apply.yml and .github/workflows/terraform-plan.yml. + + +You should now be able to run the terraform-apply and terraform-plan workflows via CICD. terraform-plan runs on pull request events, and terraform-apply runs on push events to the main branch. + +## Making changes to your infrastructure +1. To make changes to the Linode instance, check out a feature branch based on main like "feature/my-changes", make the changes and push them to the feature branch, then create a pull request. +Terraform Cloud will run terraform-plan on the pull request. +2. Once you are satisfied with the terraform plan to make changes to the Linode instance, merge the pull request, and Terraform Cloud will run terraform-apply. \ No newline at end of file diff --git a/providers.tf b/providers.tf index f4a3a37..90e4a23 100644 --- a/providers.tf +++ b/providers.tf @@ -1,8 +1,9 @@ terraform { + required_version = ">= 1.3.0" required_providers { linode = { - source = "linode/linode" - # version = "..." + source = "linode/linode" + version = "2.34.1" } } } diff --git a/variables.tf b/variables.tf index 743835e..6f6edfa 100644 --- a/variables.tf +++ b/variables.tf @@ -1,5 +1,5 @@ variable "token" { - sensitive = true - type = string + sensitive = true + type = string description = "Linode API Token" } \ No newline at end of file