diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4ecc0a4..e204047 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,7 +21,7 @@ jobs: - name: Lints Markdown files uses: DavidAnson/markdownlint-cli2-action@v20 with: - globs: '**/*.md' + globs: "**/*.md" - name: Set up Python ${{ env.python_version }} uses: actions/setup-python@v5 with: diff --git a/.github/workflows/reusable-terraform-apply.yml b/.github/workflows/reusable-terraform-apply.yml new file mode 100644 index 0000000..e579be0 --- /dev/null +++ b/.github/workflows/reusable-terraform-apply.yml @@ -0,0 +1,133 @@ +name: Reusable workflow to apply Terraform + +on: + workflow_call: + inputs: + environment: + description: "GitHub environment" + type: string + required: true + working-directory: + description: "Working directory" + type: string + required: true + tfbackend-project: + description: "Terraform backend project" + type: string + required: true + custom-commands: + description: "Optional shell commands to run before apply" + type: string + required: false + default: "" + terraform-var-flags: + description: 'Extra -var "key=value" flags (space separated)' + type: string + required: false + default: "" + workflow-parts-version: + description: "GitHub workflow parts version (branch/tag/SHA)" + type: string + required: false + default: "main" + secrets: + atlas-publickey: + description: "Atlas public key" + required: true + atlas-privatekey: + description: "Atlas private key" + required: true + atlas-groupid: + description: "Atlas group IP" + required: true + tfbackend-connstring: + description: "Terraform backend connection string" + required: true + tfbackend-dbname: + description: "Terraform backend database name" + required: true + tfbackend-tenant: + description: "Terraform backend tenant" + required: true + tfbackend-username: + description: "Terraform backend user name" + required: true + tfbackend-userpwd: + description: "Terraform backend user password" + required: true + additional-vars: + description: "Additional variables" + required: false + +jobs: + deploy: + name: Deploy + runs-on: ubuntu-latest + environment: ${{ inputs.environment }} + defaults: + run: + working-directory: ${{ inputs.working-directory }} + services: + tfbackend: + image: devprofr/terraform-backend-mongodb:latest + env: + Application__IsHttpsRedirectionEnabled: false + ConnectionStrings__MongoDb: ${{ secrets.tfbackend-connstring }} + MongoDb__ConnectionStringName: MongoDb + MongoDb__DatabaseName: ${{ secrets.tfbackend-dbname }} + ports: + - 8080:8080 + steps: + - name: Clone repository + uses: actions/checkout@v6 + - name: Checkout workflow parts + uses: actions/checkout@v6 + with: + repository: devpro/github-workflow-parts + ref: ${{ inputs.workflow-parts-version }} + path: workflow-parts + - name: Add runner ID to MongoDB Atlas + # uses: devpro/github-workflow-parts/actions/mongodb-atlas/add-runner-ip@... cannot be used with an input parameter in it (must be static) so checkout is mandatory + uses: ./workflow-parts/actions/mongodb-atlas/add-runner-ip + with: + atlas-publickey: ${{ secrets.atlas-publickey }} + atlas-privatekey: ${{ secrets.atlas-privatekey }} + atlas-groupid: ${{ secrets.atlas-groupid }} + - name: Set additional variables + run: | + if [[ -z "${{ secrets.additional-vars }}" ]]; then + echo "No additional-vars bundle provided - skipping." + else + echo "${{ secrets.additional-vars }}" | while IFS='=' read -r key val; do + if [[ -n "$val" ]]; then + echo "::add-mask::$val" + fi + done + echo "${{ secrets.additional-vars }}" >> "$GITHUB_ENV" + fi + - name: Run optional custom commands + if: ${{ inputs.custom-commands != '' }} + run: | + ${{ inputs.custom-commands }} + - name: Cache Terraform plugins + uses: actions/cache@v5 + with: + path: | + ~/.terraform.d/plugin-cache + key: terraform-${{ hashFiles('**/.terraform.lock.hcl') }} + - name: Install terraform + uses: hashicorp/setup-terraform@v3 + - name: Terraform init + run: terraform init + - name: Terraform validate + run: terraform validate + - name: Terraform plan + run: terraform plan -out=plan.tfplan ${{ inputs.terraform-var-flags }} + - name: Terraform apply + run: terraform apply -auto-approve plan.tfplan ${{ inputs.terraform-var-flags }} + env: + TF_HTTP_ADDRESS: "http://localhost:8080/${{ secrets.tfbackend-tenant }}/state/${{ inputs.tfbackend-project }}" + TF_HTTP_LOCK_ADDRESS: "http://localhost:8080/${{ secrets.tfbackend-tenant }}/state/${{ inputs.tfbackend-project }}/lock" + TF_HTTP_UNLOCK_ADDRESS: "http://localhost:8080/${{ secrets.tfbackend-tenant }}/state/${{ inputs.tfbackend-project }}/lock" + TF_HTTP_USERNAME: "${{ secrets.tfbackend-username }}" + TF_HTTP_PASSWORD: "${{ secrets.tfbackend-userpwd }}" diff --git a/.github/workflows/reusable-terraform-validate.yml b/.github/workflows/reusable-terraform-validate.yml new file mode 100644 index 0000000..fcddeec --- /dev/null +++ b/.github/workflows/reusable-terraform-validate.yml @@ -0,0 +1,89 @@ +name: Reusable workflow to validate Terraform project + +on: + workflow_call: + inputs: + job-name: + description: "Job name" + required: false + type: string + default: "Validate" + working-directory: + description: "Working directory" + required: false + type: string + default: "." + +jobs: + terraform-validate: + name: ${{ inputs.job-name }} + runs-on: ubuntu-latest + defaults: + run: + working-directory: ${{ inputs.working-directory }} + steps: + - name: Clone repository + uses: actions/checkout@v6 + - name: Cache Terraform plugins + uses: actions/cache@v5 + with: + path: | + ~/.terraform.d/plugin-cache + key: terraform-${{ hashFiles('**/.terraform.lock.hcl') }} + - name: Install terraform + uses: hashicorp/setup-terraform@v3 + - name: Check Terraform format + run: terraform fmt -recursive -check + - name: Terraform Init + run: terraform init -backend=false + - name: Terraform Validate + run: terraform validate + # Checkov is a static code analysis tool for infrastructure as code (IaC) and also a software composition analysis (SCA) tool for images and open source packages (ref. https://github.com/bridgecrewio/checkov) + - name: Run Checkov + uses: bridgecrewio/checkov-action@v12 + with: + soft_fail: true + output_format: cli,sarif + output_file_path: console,results.sarif + # quiet: true + # directory: . + # framework: terraform kubernetes helm + # needs GitHub code security > code scanning, not available on private repos + # - name: Upload SARIF file + # uses: github/codeql-action/upload-sarif@v3 + # if: success() || failure() + # with: + # sarif_file: results.sarif + - name: Upload SARIF as artifact + uses: actions/upload-artifact@v6 + if: always() + with: + name: checkov-sarif-results + path: results.sarif + retention-days: 14 + # TFLint is a pluggable terraform linter (ref. https://github.com/terraform-linters/tflint) + - name: Cache TFLint plugins + uses: actions/cache@v5 + with: + path: ~/.tflint.d/plugins + key: tflint-${{ hashFiles('**/.tflint.hcl') }} + - name: Setup TFLint + uses: terraform-linters/setup-tflint@v6 + with: + tflint_version: v0.60.0 # ref. https://github.com/terraform-linters/tflint/pkgs/container/tflint + - name: Initialize TFLint + run: tflint --init --recursive + env: + GITHUB_TOKEN: ${{ github.token }} # ref. https://github.com/terraform-linters/tflint/blob/master/docs/user-guide/plugins.md#avoiding-rate-limiting + - name: Run TFLint + run: tflint --recursive --format compact + - name: Run Trivy IaC scan + uses: aquasecurity/trivy-action@0.33.1 + with: + scan-type: "config" + format: "sarif" + output: "trivy-results.sarif" + ignore-unfixed: true + severity: "HIGH,CRITICAL" + env: + TF_IN_AUTOMATION: true diff --git a/mongodb-atlas/add-runner-ip/action.yml b/actions/mongodb-atlas/add-runner-ip/action.yml similarity index 82% rename from mongodb-atlas/add-runner-ip/action.yml rename to actions/mongodb-atlas/add-runner-ip/action.yml index 4b2e238..15c321f 100644 --- a/mongodb-atlas/add-runner-ip/action.yml +++ b/actions/mongodb-atlas/add-runner-ip/action.yml @@ -2,18 +2,15 @@ name: Add GitHub Actions runner public IP to MongoDB Atlas description: Update project IP access list (temporary) inputs: - atlas_publickey: + atlas-publickey: description: MongoDB public key required: true - atlas_privatekey: + atlas-privatekey: description: MongoDB private key required: true - atlas_groupid: + atlas-groupid: description: MongoDB group ID required: true - github_runid: - description: GitHub run ID - required: true runs: using: "composite" @@ -36,11 +33,11 @@ runs: PAYLOAD="[{\"ipAddress\": \"$RUNNER_IP\", \"comment\": \"GH Actions temp - run ${{ github.run_id }}\", \"deleteAfterDate\": \"$DELETE_AFTER\"}]" # calls Atlas API v2 to add the entry (uses digest Auth via curl) - RESPONSE=$(curl -s -w "%{http_code}" --user "${{inputs.atlas_publickey}}:${{inputs.atlas_privatekey}}" --digest \ + RESPONSE=$(curl -s -w "%{http_code}" --user "${{ inputs.atlas-publickey }}:${{ inputs.atlas-privatekey }}" --digest \ -H "Accept: application/vnd.atlas.2023-01-01+json" \ -H "Content-Type: application/json" \ --data "$PAYLOAD" \ - "https://cloud.mongodb.com/api/atlas/v2/groups/${{inputs.atlas_groupid}}/accessList") + "https://cloud.mongodb.com/api/atlas/v2/groups/${{ inputs.atlas-groupid }}/accessList") HTTP_CODE=${RESPONSE: -3}