Skip to main content

Azure Pipelines Integration

Integrate Cloudgeni security scanning into your Azure Pipelines to automatically check Infrastructure as Code for security issues on every build and pull request.

What You'll Get

  • Automated security scanning in your CI/CD pipeline
  • PR validation that blocks insecure code
  • Build reports with detailed findings
  • Fail-on-severity configuration for quality gates
  • Integration with Azure DevOps dashboards

Prerequisites

  • Azure DevOps project with Pipelines enabled
  • Cloudgeni account and API key
  • Repository containing IaC files (.tf, .bicep, .hcl, .yaml, etc.)

Quick Start

Step 1: Create API Key

  1. Go to SettingsAPI Keys in Cloudgeni
  2. Click Create API Key
  3. Name it azure-pipelines
  4. Copy the generated key

Step 2: Add Pipeline Variable

  1. Go to your Azure DevOps project
  2. Navigate to PipelinesLibrary
  3. Create a new Variable Group or edit existing
  4. Add variable:
    • Name: CLOUDGENI_API_KEY
    • Value: Your API key
    • Keep this value secret: Checked
  5. Save the variable group

Step 3: Create Pipeline

Add azure-pipelines.yml to your repository:
trigger:
  - main

pool:
  vmImage: 'ubuntu-latest'

variables:
  - group: cloudgeni-variables

steps:
  - task: Bash@3
    displayName: 'Cloudgeni Security Scan'
    inputs:
      targetType: 'inline'
      script: |
        curl -sSL https://get.cloudgeni.ai/install.sh | bash
        cloudgeni scan --api-key $(CLOUDGENI_API_KEY)

Configuration Options

Scanner Options

OptionDescriptionDefault
--api-keyCloudgeni API keyRequired
--fail-on-criticalExit 1 on critical findingsfalse
--fail-on-highExit 1 on high findingsfalse
--pathDirectory to scan.
--excludePaths to excludeNone
--outputOutput format (text, json, sarif)text

Basic Job

- task: Bash@3
  displayName: 'Cloudgeni Scan'
  inputs:
    targetType: 'inline'
    script: |
      curl -sSL https://get.cloudgeni.ai/install.sh | bash
      cloudgeni scan --api-key $(CLOUDGENI_API_KEY)

Strict Mode

- task: Bash@3
  displayName: 'Cloudgeni Scan'
  inputs:
    targetType: 'inline'
    script: |
      curl -sSL https://get.cloudgeni.ai/install.sh | bash
      cloudgeni scan --api-key $(CLOUDGENI_API_KEY) --fail-on-critical --fail-on-high
  failOnStderr: true

Pipeline Examples

Basic Pipeline

trigger:
  - main

pool:
  vmImage: 'ubuntu-latest'

variables:
  - group: cloudgeni-variables

stages:
  - stage: Security
    jobs:
      - job: Scan
        steps:
          - checkout: self

          - task: Bash@3
            displayName: 'Install Cloudgeni'
            inputs:
              targetType: 'inline'
              script: |
                curl -sSL https://get.cloudgeni.ai/install.sh | bash

          - task: Bash@3
            displayName: 'Run Security Scan'
            inputs:
              targetType: 'inline'
              script: |
                cloudgeni scan --api-key $(CLOUDGENI_API_KEY)

Production Pipeline

trigger:
  branches:
    include:
      - main
  paths:
    include:
      - '**/*.tf'
      - '**/*.bicep'

pr:
  branches:
    include:
      - main

pool:
  vmImage: 'ubuntu-latest'

variables:
  - group: cloudgeni-variables
  - name: TF_ROOT
    value: '$(System.DefaultWorkingDirectory)/infrastructure'

stages:
  - stage: Security
    displayName: 'Security Scan'
    jobs:
      - job: CloudgeniScan
        displayName: 'Cloudgeni IaC Scan'
        steps:
          - checkout: self

          - task: Bash@3
            displayName: 'Install Cloudgeni'
            inputs:
              targetType: 'inline'
              script: |
                curl -sSL https://get.cloudgeni.ai/install.sh | bash

          - task: Bash@3
            displayName: 'Run Security Scan'
            inputs:
              targetType: 'inline'
              script: |
                cloudgeni scan \
                  --api-key $(CLOUDGENI_API_KEY) \
                  --path $(TF_ROOT) \
                  --fail-on-critical \
                  --fail-on-high \
                  --output json > $(Build.ArtifactStagingDirectory)/security-report.json

          - task: PublishBuildArtifacts@1
            displayName: 'Publish Security Report'
            inputs:
              pathToPublish: '$(Build.ArtifactStagingDirectory)/security-report.json'
              artifactName: 'security-report'
            condition: always()

  - stage: Plan
    displayName: 'Terraform Plan'
    dependsOn: Security
    jobs:
      - job: TerraformPlan
        steps:
          - checkout: self

          - task: TerraformInstaller@0
            inputs:
              terraformVersion: 'latest'

          - task: TerraformTaskV4@4
            displayName: 'Terraform Init'
            inputs:
              provider: 'azurerm'
              command: 'init'
              workingDirectory: '$(TF_ROOT)'

          - task: TerraformTaskV4@4
            displayName: 'Terraform Plan'
            inputs:
              provider: 'azurerm'
              command: 'plan'
              workingDirectory: '$(TF_ROOT)'

Multi-Environment Pipeline

trigger:
  - main
  - develop

pool:
  vmImage: 'ubuntu-latest'

variables:
  - group: cloudgeni-variables

stages:
  - stage: ScanDev
    displayName: 'Scan Development'
    condition: eq(variables['Build.SourceBranch'], 'refs/heads/develop')
    jobs:
      - job: Scan
        steps:
          - checkout: self
          - bash: |
              curl -sSL https://get.cloudgeni.ai/install.sh | bash
              cloudgeni scan --api-key $(CLOUDGENI_API_KEY) --path ./environments/dev --fail-on-critical

  - stage: ScanProd
    displayName: 'Scan Production'
    condition: eq(variables['Build.SourceBranch'], 'refs/heads/main')
    jobs:
      - job: Scan
        steps:
          - checkout: self
          - bash: |
              curl -sSL https://get.cloudgeni.ai/install.sh | bash
              cloudgeni scan --api-key $(CLOUDGENI_API_KEY) --path ./environments/prod --fail-on-critical --fail-on-high

Template-Based Pipeline

Create a template templates/security-scan.yml:
parameters:
  - name: scanPath
    type: string
    default: '.'
  - name: failOnHigh
    type: boolean
    default: false

steps:
  - task: Bash@3
    displayName: 'Install Cloudgeni'
    inputs:
      targetType: 'inline'
      script: |
        curl -sSL https://get.cloudgeni.ai/install.sh | bash

  - task: Bash@3
    displayName: 'Run Security Scan'
    inputs:
      targetType: 'inline'
      script: |
        ARGS="--api-key $(CLOUDGENI_API_KEY) --path ${{ parameters.scanPath }} --fail-on-critical"
        if [ "${{ parameters.failOnHigh }}" == "True" ]; then
          ARGS="$ARGS --fail-on-high"
        fi
        cloudgeni scan $ARGS
Use the template:
stages:
  - stage: Security
    jobs:
      - job: Scan
        steps:
          - template: templates/security-scan.yml
            parameters:
              scanPath: './infrastructure'
              failOnHigh: true

Pull Request Validation

Branch Policies

Require security scans for pull requests:
  1. Go to ReposBranches
  2. Click on your target branch
  3. Select Branch policies
  4. Under Build Validation, click Add build policy
  5. Select your security pipeline
  6. Configure:
    • Trigger: Automatic
    • Policy requirement: Required
    • Build expiration: Immediately

Status Checks

Cloudgeni can update PR status:
- task: Bash@3
  displayName: 'Update PR Status'
  inputs:
    targetType: 'inline'
    script: |
      # Run scan and capture result
      cloudgeni scan --api-key $(CLOUDGENI_API_KEY) --output json > report.json

      # Check for failures
      if grep -q '"severity": "CRITICAL"' report.json; then
        echo "##vso[task.complete result=Failed;]Critical findings detected"
      fi
  condition: eq(variables['Build.Reason'], 'PullRequest')

Variable Groups

Project-Level Variables

  1. Go to PipelinesLibrary
  2. Create Variable Group: cloudgeni-variables
  3. Add CLOUDGENI_API_KEY (secret)
  4. Reference in pipeline: - group: cloudgeni-variables

Organization-Level Variables

For multiple projects:
  1. Go to Organization SettingsPipelinesVariable Groups
  2. Create shared variable group
  3. Grant access to projects
  4. Reference in pipelines

Artifacts and Reports

Publish Report

- task: Bash@3
  displayName: 'Run Scan'
  inputs:
    targetType: 'inline'
    script: |
      cloudgeni scan --api-key $(CLOUDGENI_API_KEY) --output json > $(Build.ArtifactStagingDirectory)/report.json

- task: PublishBuildArtifacts@1
  inputs:
    pathToPublish: '$(Build.ArtifactStagingDirectory)/report.json'
    artifactName: 'SecurityReport'
  condition: always()

Test Results

Publish as test results:
- task: Bash@3
  displayName: 'Run Scan'
  inputs:
    targetType: 'inline'
    script: |
      cloudgeni scan --api-key $(CLOUDGENI_API_KEY) --output junit > $(Common.TestResultsDirectory)/security-results.xml

- task: PublishTestResults@2
  inputs:
    testResultsFormat: 'JUnit'
    testResultsFiles: '**/security-results.xml'
  condition: always()

Troubleshooting

Variable Not Found:
  • Check variable group is linked to pipeline
  • Verify variable name is correct
  • Ensure secret variable is accessible
Authentication Failed:
  • Verify API key is correct
  • Check variable is not empty
  • Test key in Cloudgeni dashboard
Pipeline Timeout:
  • Add timeoutInMinutes: 30 to job
  • Consider scanning specific paths
  • Check for large files
No Findings in Report:
  • Verify IaC files exist in scan path
  • Check file extensions
  • Review exclude patterns
Branch Policy Not Triggering:
  • Verify policy is configured correctly
  • Check pipeline exists and is enabled
  • Ensure trigger matches PR branches