To follow this guide you need a SafeDep Cloud API Key and Tenant Identifier. See Cloud Quickstart on how to onboard to SafeDep Cloud and get an API key.

In this guide, we’ll discover Terraform providers used in a Terraform project, synchronize the inventory (BOM) with SafeDep Cloud, and execute queries to discover unofficial providers that may pose risks to developer, cloud, and CI/CD infrastructure security.

Why Audit Terraform Providers?

Supply Chain Security

Unofficial providers may lack security oversight and could introduce vulnerabilities

Compliance Requirements

Many organizations require approved provider lists for infrastructure code

Risk Management

Understand the security posture of your infrastructure dependencies

Centralized Visibility

Get organization-wide visibility into Terraform provider usage

Prerequisites

  • vet installed (see quickstart for installation options)
  • SafeDep Cloud account with API key and tenant identifier
  • Terraform project with initialized providers (.terraform.lock.hcl must exist)

Scan and Discover Terraform Providers

Run vet on your Terraform codebase to scan for Terraform providers. The Terraform project must be initialized so that .terraform.lock.hcl is available in the project directory.

vet scan -D /path/to/terraform-code \
  --report-sync \
  --report-sync-project gh/test/infra1 \
  --report-sync-project-version main

What This Command Does

1

Provider Discovery

Scans Terraform configuration files and lock files to identify all providers

2

Metadata Collection

Gathers information about provider versions, sources, and tiers (official, partner, community)

3

Cloud Synchronization

Uploads the provider inventory to SafeDep Cloud for centralized analysis

4

Project Tracking

Associates findings with specific projects and versions for tracking over time

Query Provider Inventory

Use SafeDep Cloud SQL queries to analyze your Terraform provider inventory. See SafeDep Cloud Quickstart for more details on the query interface.

Find Unofficial Terraform Providers

Query for providers that are not officially maintained by HashiCorp:

SELECT 
  projects.name, 
  projects.version, 
  packages.name, 
  packages.version 
FROM projects 
WHERE terraform_providers.tier != 'official'

Execute the query using the vet CLI:

vet cloud query execute --sql "
  SELECT projects.name, projects.version, packages.name, packages.version 
    FROM projects 
      WHERE terraform_providers.tier != 'official'
"

Example Response

Here’s an example response from our test repository:

┌───────────────────────────────────────────┬──────────────────┬────────────────┬──────────────────┐
│ PACKAGES.NAME                             │ PACKAGES.VERSION │ PROJECTS.NAME  │ PROJECTS.VERSION │
├───────────────────────────────────────────┼──────────────────┼────────────────┼──────────────────┤
│ registry.terraform.io/hetznercloud/hcloud │ 1.48.0           │ gh/test/infra1 │ main             │
├───────────────────────────────────────────┼──────────────────┼────────────────┼──────────────────┤
│ registry.terraform.io/digitalocean/do    │ 2.34.1           │ gh/test/infra1 │ main             │
└───────────────────────────────────────────┴──────────────────┴────────────────┴──────────────────┘

Advanced Queries

Provider Tier Analysis

Get a breakdown of providers by their tier classification:

SELECT 
  terraform_providers.tier,
  COUNT(*) as provider_count,
  COUNT(DISTINCT projects.name) as project_count
FROM projects 
GROUP BY terraform_providers.tier

Community Provider Risk Assessment

Find community providers that might need additional security review:

SELECT 
  packages.name,
  packages.version,
  projects.name as project,
  terraform_providers.tier,
  terraform_providers.source
FROM projects 
WHERE terraform_providers.tier = 'community'
ORDER BY packages.name

Provider Version Consistency

Check for version inconsistencies across projects:

SELECT 
  packages.name,
  packages.version,
  COUNT(DISTINCT projects.name) as project_count
FROM projects 
GROUP BY packages.name, packages.version
HAVING COUNT(DISTINCT projects.name) > 1
ORDER BY packages.name

Recent Provider Additions

Find recently added providers (useful for change tracking):

SELECT 
  packages.name,
  packages.version,
  projects.name,
  projects.created_at
FROM projects 
WHERE projects.created_at > DATE('now', '-7 days')
ORDER BY projects.created_at DESC

CI/CD Integration

GitHub Actions

Integrate Terraform provider auditing into your CI/CD pipeline:

name: Terraform Security Audit
on:
  push:
    paths:
      - '**/*.tf'
      - '.terraform.lock.hcl'
  pull_request:
    paths:
      - '**/*.tf'
      - '.terraform.lock.hcl'

jobs:
  terraform-audit:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Setup Terraform
        uses: hashicorp/setup-terraform@v3
        
      - name: Terraform Init
        run: terraform init
        
      - name: Audit Terraform Providers
        run: |
          vet scan -D . \
            --report-sync \
            --report-sync-project ${{ github.repository }} \
            --report-sync-project-version ${{ github.ref_name }}
        env:
          SAFEDEP_CLOUD_API_KEY: ${{ secrets.SAFEDEP_CLOUD_API_KEY }}
          SAFEDEP_CLOUD_TENANT: ${{ secrets.SAFEDEP_CLOUD_TENANT }}
          
      - name: Check for Unofficial Providers
        run: |
          vet cloud query execute --sql "
            SELECT packages.name 
            FROM projects 
            WHERE terraform_providers.tier != 'official' 
              AND projects.name = '${{ github.repository }}'
              AND projects.version = '${{ github.ref_name }}'
          " --format json > unofficial-providers.json
          
          if [ $(jq length unofficial-providers.json) -gt 0 ]; then
            echo "⚠️ Unofficial providers detected:"
            jq -r '.[].name' unofficial-providers.json
            echo "Please review these providers for security compliance."
          fi

GitLab CI

stages:
  - init
  - audit

terraform-init:
  stage: init
  image: hashicorp/terraform:latest
  script:
    - terraform init
  artifacts:
    paths:
      - .terraform.lock.hcl
    expire_in: 1 hour

terraform-audit:
  stage: audit
  image: ghcr.io/safedep/vet:latest
  script:
    - vet scan -D . 
        --report-sync 
        --report-sync-project $CI_PROJECT_PATH 
        --report-sync-project-version $CI_COMMIT_REF_NAME
  dependencies:
    - terraform-init
  variables:
    SAFEDEP_CLOUD_API_KEY: $SAFEDEP_CLOUD_API_KEY
    SAFEDEP_CLOUD_TENANT: $SAFEDEP_CLOUD_TENANT

Policy Enforcement

Custom Provider Policies

Create policies to enforce provider compliance:

# terraform-provider-policy.yml
name: Terraform Provider Security Policy
description: Enforce approved provider usage
filters:
  - name: unofficial-providers-blocked
    value: |
      terraform_providers.tier != "official" && 
      !packages.name.startsWith("registry.terraform.io/hashicorp/")
      
  - name: community-providers-require-approval
    value: |
      terraform_providers.tier == "community" && 
      !packages.name.contains("approved-community-providers")

Use the policy during scanning:

vet scan -D . \
  --filter-suite terraform-provider-policy.yml \
  --filter-fail \
  --report-sync \
  --report-sync-project myproject \
  --report-sync-project-version main

Schema Exploration

List all queryable columns for Terraform providers:

vet cloud query schema

This will show you all available fields for building custom queries, including:

  • terraform_providers.tier - Provider tier (official, partner, community)
  • terraform_providers.source - Provider source URL
  • terraform_providers.namespace - Provider namespace
  • packages.name - Full provider name
  • packages.version - Provider version

Best Practices

Troubleshooting