Scan entire a Terraform repository by Checkov

In the last post Running Chechov as IaC scanner on Azure DevOps, we saw how we implemented an IaC scanning by the Checkov. As you may know, we have some limitations on that implementation. The first obstruction is we can use that one as a solution for scanning whole a terraform code repo because the checkov only searches for terraform code in the current directory. Yeah, the Checkov has this limitation that we can set a switch to scan the entire repo. But we have a solution to make it possible! And the answer is to execute a bash code to crawl a repository and find the Terraform files and run the Checkov for any of the found files. let see how we can manage it. CheckOv

I found this bash script which can do our task, checkov.sh As you can see in line 46 we have

for folder in $(find_folders_by "main.tf"); do

so it may make some issue because in some directory we did not have main.tf but we have the terraform code in other .tf files. so I changed the line to

for folder in $(find_folders_by "*.tf"); do

And for generating report per-directory I changed the run_checkov function to the following:

run_checkov() {
  local test_dir=$1
  local outputname=${test_dir##*/}
  docker run -v "${test_dir}":/tf bridgecrew/checkov:latest --directory /tf --output junitxml > output/${outputname}-Checkov-Report.xml
}

So the final result should be like the below run_checkov.sh:

#!/bin/bash      
#title           :run_checkov.sh
#description     :Runs the Checkov static analysis tool on all subdirectories of the target.
#author            :[email protected]
#date            :20200510
#version         :0.1    
#usage             :./checkov.sh {WORKk_DIR}
#bash_version    :5.0.16(1)-release 
# 
set -eo pipefail
# The target directory for scanning.
WORK_DIR=${1-$(pwd)}
#######################################
# run_checkov() docker command
# Arguments:
#   test_dir folder
# Outputs:
#   Writes test command outputs to stdout
#   Exits on $? != 0
#######################################
run_checkov() {
  local test_dir=$1
  local outputname=${test_dir##*/}
  docker run -v "${test_dir}":/tf bridgecrew/checkov:latest --directory /tf --output junitxml > output/${outputname}-Checkov-Report.xml
}
#######################################
# find_folders_by() file pattern
# Globals:
#   WORK_DIR -path
# Arguments:
#   pattern - regex
# Outputs:
#   Writes folders list to stdout
#######################################
find_folders_by() {
  local pattern=${1:-"main.tf"}
  find "${WORK_DIR}" -type f -name "${pattern}" -printf '%h\n' | sort -u
}
#######################################
# Runs the Checkov static analysis tool on all subdirectories
#######################################
run_main() {
  for folder in $(find_folders_by "*.tf"); do
    run_checkov "${folder}" &
  done
  wait
}
#######################################
#  Be able to run this one either as standalone or import as lib
#######################################
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
  run_main
fi

So just save the bash code as run_checkov.sh in same directory with .yaml pipeline and let see the changes that we have for the .yaml file.

I added this task to the azure-pipeline.yaml file:

- task: Bash@3
  inputs:
    targetType: inline
    script: | 
      cd (To the git repo directory for run_checkov.sh)
      mkdir output
      chmod +x run_checkov.sh
      ./run_checkov.sh $(tf_folder)
  displayName: 'Checkov Static Code Analysis'

On the other side I found another limitation which is for each repo I should add this .yaml file to the repository but I want to have a pipeline for all repository so I changed the file as follows:

- checkout: git://ProjectName/RepoName

Also for having the bash file in there I added another task to the .yaml file:

- task: Bash@3
  inputs:
    targetType: inline
    script: |
      git clone "the run_checkov.sh file repo"        
  displayName: 'Clone the code'

So the final the azure-pipeline.yaml file is as follows:

trigger:
  branches:
    include:
      - master
pool:
  vmImage: 'ubuntu-latest'
variables:
- name: tf_folder
  value: $(System.DefaultWorkingDirectory)
stages:
  - stage: StaticCodeAnalysisStage
    displayName: Static Code Analysis Stage
    jobs:
      - job: ScanningCode
        displayName: Run Checkov
        steps:
          - checkout: git://ProjectName/RepoName
          - task: Bash@3
            inputs:
              targetType: inline
              script: |
                git clone "the run_checkov.sh file repo"                 
            displayName: 'Clone the code'
          - task: Bash@3
            inputs:
              targetType: inline
              script: | 
                cd (To the git repo directory for run_checkov.sh)
                mkdir output
                chmod +x run_checkov.sh
                ./run_checkov.sh $(tf_folder)
            displayName: 'Checkov Static Code Analysis'
          - task: PublishTestResults@2
            displayName: Publish Checkov Test Results
            condition: succeededOrFailed()
            inputs:
              testResultsFormat: 'JUnit'
              testResultsFiles: '**/*Checkov-Report.xml'
              searchFolder: '$(tf_folder)/[the git run_checkov.sh directory ]/output'
              mergeTestResults: true
              failTaskOnFailedTests: false
              testRunTitle: Checkov Scan 
              publishRunAttachments: true

Done!