diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..5bef95b --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,21 @@ +#### ๐Ÿ”— Jira ticket + +CCAP-XXX + +#### โœ๏ธ Description + + + +#### ๐Ÿ“ท Design reference + + + +#### ๐Ÿงช Testing instructions + +- [ ] Step 1... +- [ ] Step 2... + +#### โœ… Completion tasks + +- [ ] Added relevant tests +- [ ] Meets acceptance criteria diff --git a/.github/workflows/branch.yml b/.github/workflows/branch.yml new file mode 100644 index 0000000..0a40df0 --- /dev/null +++ b/.github/workflows/branch.yml @@ -0,0 +1,117 @@ +name: Branch Checks + +on: + push: + branches-ignore: + - main + +jobs: + find-modules: + runs-on: ubuntu-latest + steps: + - name: Checkout source code + uses: actions/checkout@v4 + + - name: Find all terraform modules + id: find + uses: bendrucker/find-terraform-modules@v1 + with: + working-directory: tofu + + - name: Show all matching modules + shell: bash + run: | + mods=(${{ join(fromJSON(steps.find.outputs.modules), ' ') }}) + printf "%s\n" "${mods[@]}" + + - name: Find all changed files + id: diff + uses: technote-space/get-diff-action@v6 + with: + FORMAT: json + + - name: Show changed files + run: | + echo "${{ steps.diff.outputs.diff }}" + + - name: Get the modified modules + id: modified + uses: actions/github-script@v7 + with: + script: | + const modules = ${{ steps.find.outputs.modules }} + const diff = ${{ steps.diff.outputs.diff }} + const modifiedModules = modules.filter( + (module) => { + return !!diff.find(file => new RegExp(`^${module}/.+`).test(file)) + } + ) + + core.setOutput('modules', modifiedModules) + + - name: Show modified modules + run: | + echo "${{ steps.modified.outputs.modules }}" + outputs: + modules: ${{ steps.modified.outputs.modules }} + + lint: + runs-on: ubuntu-latest + needs: find-modules + steps: + - name: Checkout source code + uses: actions/checkout@v4 + + - uses: actions/cache@v4 + name: Cache plugin directory + with: + path: ~/.tflint.d/plugins + key: tflint-${{ hashFiles('.tflint.hcl') }} + + - uses: terraform-linters/setup-tflint@v4 + name: Setup TFLint + + - name: Show version + run: tflint --version + + - name: Init TFLint + run: tflint --init + + # Use a bash script to run tflint on each modified module. + - name: Run TFLint + shell: bash + run: | + set +e + + exit_code=0 + modules=(${{ join(fromJSON(needs.find-modules.outputs.modules), ' ') }}) + for module in ${modules[@]} + do + echo "Linting module $module" + tflint --format compact --chdir $module + exit_code=$(( $? > exit_code ? $? : exit_code )) + done + + exit $exit_code + + trivy: + name: trivy + runs-on: ubuntu-latest + steps: + - name: Checkout source code + uses: actions/checkout@v4 + - name: Run Trivy vulnarability scanner + uses: aquasecurity/trivy-action@master + with: + scan-type: config + ignore-unfixed: true + skip-dirs: '"**/*/.terraform"' + exit-code: 1 + format: sarif + output: 'trivy-results.sarif' + + - name: Parse SARIF file + if: always() + uses: Ayrx/sarif_to_github_annotations@v0.2.2 + with: + sarif_file: 'trivy-results.sarif' diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml new file mode 100644 index 0000000..cc89e0f --- /dev/null +++ b/.github/workflows/deploy.yaml @@ -0,0 +1,46 @@ +name: Deploy pipeline + +on: + pull_request: + workflow_dispatch: + inputs: + environment: + description: 'Environment to deploy to' + default: 'staging' + required: true + type: environment + config: + description: 'The OpenTofu configuration to plan' + default: 'staging' + required: true + type: choice + options: + - staging + +jobs: + deploy: + runs-on: ubuntu-latest + environment: ${{ github.event.inputs.environment }} + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: us-east-1 + + - name: Setup OpenTofu + uses: opentofu/setup-opentofu@v1 + + - name: Initialize OpenTofu + working-directory: ./tofu/config/${{ inputs.config }} + run: tofu init + + # TODO: Add a manual approval step here. For now, we'll use GitHub + # Actions' environment protection feature for sensitive environments. + - name: Apply changes + working-directory: ./tofu/config/${{ inputs.config }} + run: tofu apply --auto-approve diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml new file mode 100644 index 0000000..0e2c086 --- /dev/null +++ b/.github/workflows/main.yaml @@ -0,0 +1,117 @@ +name: Main Checks + +on: + push: + branches: + - main + +jobs: + find-modules: + runs-on: ubuntu-latest + steps: + - name: Checkout source code + uses: actions/checkout@v4 + + - name: Find all terraform modules + id: find + uses: bendrucker/find-terraform-modules@v1 + with: + working-directory: tofu + + - name: Show all matching modules + shell: bash + run: | + mods=(${{ join(fromJSON(steps.find.outputs.modules), ' ') }}) + printf "%s\n" "${mods[@]}" + + - name: Find all changed files + id: diff + uses: technote-space/get-diff-action@v6 + with: + FORMAT: json + + - name: Show changed files + run: | + echo "${{ steps.diff.outputs.diff }}" + + - name: Get the modified modules + id: modified + uses: actions/github-script@v7 + with: + script: | + const modules = ${{ steps.find.outputs.modules }} + const diff = ${{ steps.diff.outputs.diff }} + const modifiedModules = modules.filter( + (module) => { + return !!diff.find(file => new RegExp(`^${module}/.+`).test(file)) + } + ) + + core.setOutput('modules', modifiedModules) + + - name: Show modified modules + run: | + echo "${{ steps.modified.outputs.modules }}" + outputs: + modules: ${{ steps.modified.outputs.modules }} + + lint: + runs-on: ubuntu-latest + needs: find-modules + steps: + - name: Checkout source code + uses: actions/checkout@v4 + + - uses: actions/cache@v4 + name: Cache plugin directory + with: + path: ~/.tflint.d/plugins + key: tflint-${{ hashFiles('.tflint.hcl') }} + + - uses: terraform-linters/setup-tflint@v4 + name: Setup TFLint + + - name: Show version + run: tflint --version + + - name: Init TFLint + run: tflint --init + + # Use a bash script to run tflint on each modified module. + - name: Run TFLint + shell: bash + run: | + set +e + + exit_code=0 + modules=(${{ join(fromJSON(needs.find-modules.outputs.modules), ' ') }}) + for module in ${modules[@]} + do + echo "Linting module $module" + tflint --format compact --chdir $module + exit_code=$(( $? > exit_code ? $? : exit_code )) + done + + exit $exit_code + + trivy: + name: trivy + runs-on: ubuntu-latest + steps: + - name: Checkout source code + uses: actions/checkout@v4 + - name: Run Trivy vulnarability scanner + uses: aquasecurity/trivy-action@master + with: + scan-type: config + ignore-unfixed: true + skip-dirs: '**/*/.terraform' + exit-code: 1 + format: sarif + output: 'trivy-results.sarif' + + - name: Parse SARIF file + if: always() + uses: Ayrx/sarif_to_github_annotations@v0.2.2 + with: + sarif_file: 'trivy-results.sarif' diff --git a/.github/workflows/plan.yaml b/.github/workflows/plan.yaml new file mode 100644 index 0000000..bc0a011 --- /dev/null +++ b/.github/workflows/plan.yaml @@ -0,0 +1,70 @@ +name: Plan the deployment pipeline + +on: + pull_request: + workflow_call: + inputs: + environment: + description: 'Environment to plan on' + default: 'staging' + required: true + type: string + config: + description: 'The OpenTofu configuration to plan' + default: 'staging' + required: true + type: string + outputs: + plan: + description: "The plan output from the tofu plan command" + value: ${{ jobs.plan.outputs.plan }} + secrets: + AWS_ACCESS_KEY_ID: + required: true + AWS_SECRET_ACCESS_KEY: + required: true + workflow_dispatch: + inputs: + environment: + description: 'Environment to plan on' + default: 'staging' + required: true + type: environment + config: + description: 'The OpenTofu configuration to plan' + required: true + type: choice + options: + - staging + +jobs: + plan: + runs-on: ubuntu-latest + environment: ${{ inputs.environment }} + outputs: + plan: ${{ steps.plan.outputs.stdout }} + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: us-east-1 + + - name: Setup OpenTofu + uses: opentofu/setup-opentofu@v1 + + - name: Initialize OpenTofu + working-directory: ./tofu/config/${{ inputs.config }} + run: tofu init + + - name: Plan changes + id: plan + working-directory: ./tofu/config/${{ inputs.config }} + run: tofu plan -no-color + + - name: Display plan + run: echo "${{ steps.plan.outputs.stdout }}"