# yaml-language-server: $schema=https://json.schemastore.org/github-action.json # permissions: # - contents: write # Required for committing fixes # - security-events: write # Required for uploading SARIF reports --- name: Terraform Lint and Fix description: 'Lints and fixes Terraform files with advanced validation and security checks.' author: 'Ismo Vuorinen' branding: icon: server color: green inputs: terraform-version: description: 'Terraform version to use' required: false default: 'latest' tflint-version: description: 'TFLint version to use' required: false default: 'latest' working-directory: description: 'Directory containing Terraform files' required: false default: '.' config-file: description: 'Path to TFLint config file' required: false default: '.tflint.hcl' fail-on-error: description: 'Fail workflow if issues are found' required: false default: 'true' auto-fix: description: 'Automatically fix issues when possible' required: false default: 'true' max-retries: description: 'Maximum number of retry attempts' required: false default: '3' format: description: 'Output format (compact, json, checkstyle, junit, sarif)' required: false default: 'sarif' token: description: 'GitHub token for authentication' required: false default: '' username: description: 'GitHub username for commits' required: false default: 'github-actions' email: description: 'GitHub email for commits' required: false default: 'github-actions@github.com' outputs: error-count: description: 'Number of errors found' value: ${{ steps.lint.outputs.error_count }} fixed-count: description: 'Number of issues fixed' value: ${{ steps.fix.outputs.fixed_count }} sarif-file: description: 'Path to SARIF report file' value: ${{ steps.lint.outputs.sarif_file }} runs: using: composite steps: - name: Checkout Repository uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: token: ${{ inputs.token || github.token }} - name: Validate Inputs id: validate uses: ./validate-inputs with: action-type: 'terraform-lint-fix' token: ${{ inputs.token || github.token }} email: ${{ inputs.email }} username: ${{ inputs.username }} terraform-version: ${{ inputs.terraform-version }} tflint-version: ${{ inputs.tflint-version }} max-retries: ${{ inputs.max-retries }} - name: Write Validated Inputs to Environment shell: bash env: INPUT_WORKING_DIR: ${{ inputs.working-directory }} INPUT_CONFIG: ${{ inputs.config-file }} INPUT_FORMAT: ${{ inputs.format }} INPUT_FAIL: ${{ inputs.fail-on-error }} INPUT_RETRIES: ${{ inputs.max-retries }} run: | set -euo pipefail # Write validated inputs to GITHUB_ENV for safe use in shell contexts { echo "VALIDATED_WORKING_DIR=$INPUT_WORKING_DIR" echo "VALIDATED_CONFIG=$INPUT_CONFIG" echo "VALIDATED_FORMAT=$INPUT_FORMAT" echo "VALIDATED_FAIL=$INPUT_FAIL" echo "VALIDATED_RETRIES=$INPUT_RETRIES" } >> "$GITHUB_ENV" - name: Check for Terraform Files id: check-files shell: bash run: | set -euo pipefail # Use validated environment variable WORKING_DIRECTORY="$VALIDATED_WORKING_DIR" cd "$WORKING_DIRECTORY" # Check for Terraform files if ! find . -name "*.tf" -o -name "*.tfvars" | grep -q .; then echo "No Terraform files found. Skipping lint and fix." echo "found=false" >> $GITHUB_OUTPUT exit 0 fi echo "found=true" >> $GITHUB_OUTPUT - name: Setup Terraform if: steps.check-files.outputs.found == 'true' uses: hashicorp/setup-terraform@b9cd54a3c349d3f38e8881555d616ced269862dd # v3.1.2 with: terraform_version: ${{ inputs.terraform-version }} terraform_wrapper: false - name: Validate Terraform Syntax if: steps.check-files.outputs.found == 'true' shell: bash run: | set -euo pipefail echo "Validating Terraform file syntax..." for file in $(find . -name "*.tf" -o -name "*.tfvars"); do if ! terraform fmt -check=true "$file" >/dev/null 2>&1; then echo "::warning::Invalid Terraform syntax in $file" fi done - name: Install TFLint if: steps.check-files.outputs.found == 'true' shell: bash run: | set -euo pipefail # Use validated environment variable MAX_RETRIES="$VALIDATED_RETRIES" # Function to install TFLint with retries install_tflint() { local attempt=1 local max_attempts="$MAX_RETRIES" while [ $attempt -le $max_attempts ]; do echo "Installing TFLint (Attempt $attempt of $max_attempts)" if curl -sSL "https://raw.githubusercontent.com/terraform-linters/tflint/master/install_linux.sh" | bash; then echo "TFLint installed successfully" return 0 fi attempt=$((attempt + 1)) if [ $attempt -le $max_attempts ]; then echo "Installation failed, waiting 10 seconds before retry..." sleep 10 fi done echo "::error::Failed to install TFLint after $max_attempts attempts" return 1 } install_tflint # Initialize TFLint plugins tflint --init - name: Configure TFLint if: steps.check-files.outputs.found == 'true' shell: bash run: | set -euo pipefail # Use validated environment variable CONFIG_FILE="$VALIDATED_CONFIG" # Create default config if none exists if [ ! -f "$CONFIG_FILE" ]; then cat > "$CONFIG_FILE" < "$tflint_output"; then error_count=$(grep -c "level\": \"error\"" "$tflint_output" || echo 0) echo "error_count=$error_count" >> $GITHUB_OUTPUT if [[ "$FAIL_ON_ERROR" == "true" ]]; then echo "::error::Found $error_count linting errors" exit 1 fi fi echo "sarif_file=$tflint_output" >> $GITHUB_OUTPUT - name: Run Terraform Format if: steps.check-files.outputs.found == 'true' && inputs.auto-fix == 'true' id: fix shell: bash run: | set -euo pipefail # Use validated environment variable WORKING_DIRECTORY="$VALIDATED_WORKING_DIR" cd "$WORKING_DIRECTORY" # Track fixed files fixed_count=0 # Format Terraform files for file in $(find . -name "*.tf" -o -name "*.tfvars"); do if ! terraform fmt -check "$file" >/dev/null 2>&1; then terraform fmt "$file" fixed_count=$((fixed_count + 1)) fi done echo "fixed_count=$fixed_count" >> $GITHUB_OUTPUT - name: Set Git Config for Fixes if: ${{ fromJSON(steps.fix.outputs.fixed_count) > 0 }} uses: ./set-git-config with: token: ${{ inputs.token || github.token }} username: ${{ inputs.username }} email: ${{ inputs.email }} - name: Commit Fixes if: ${{ fromJSON(steps.fix.outputs.fixed_count) > 0 }} shell: bash env: FIXED_COUNT: ${{ steps.fix.outputs.fixed_count }} run: | set -euo pipefail # Use validated environment variable and output WORKING_DIRECTORY="$VALIDATED_WORKING_DIR" cd "$WORKING_DIRECTORY" if git diff --quiet; then echo "No changes to commit." else git add . git commit -m "fix: applied terraform formatting fixes to $FIXED_COUNT files" git push || { echo "Push failed, pulling latest changes..." git pull --rebase git push } fi - name: Upload SARIF Report if: steps.check-files.outputs.found == 'true' && inputs.format == 'sarif' uses: github/codeql-action/upload-sarif@16140ae1a102900babc80a33c44059580f687047 # v4.30.9 with: sarif_file: ${{ env.VALIDATED_WORKING_DIR }}/reports/tflint.sarif category: terraform-lint - name: Cleanup if: always() shell: bash run: |- set -euo pipefail # Remove temporary files rm -rf .terraform/ rm -rf reports/ # Clean up TFLint cache rm -rf ~/.tflint.d/