Skip to content

Jenkins Integration Guide

Automate infrastructure scanning, cost policy enforcement, and SARIF export inside Jenkins Pipelines.

Quick Start (5-Minute Setup)

Prerequisites

  • Jenkins account with Pipeline permissions
  • Jenkinsfile configured
  • CloudSlash API key (from dashboard)
  • (Optional) Jenkins credentials store

Step 1: Install CloudStencil Plugin

Install the CloudStencil plugin in Jenkins:

  1. Go to Manage Jenkins → Manage Plugins → Available
  2. Search for "CloudStencil"
  3. Select and install (requires restart)

Or, for self-hosted/DevOps pipelines, you can use Jenkins' cloudslash-agent, cloudslash-dashboard, and cloudslash-runner agents.

Step 2: Configure Credentials

Add your CloudSlash API key in Jenkins:

  1. Go to Manage Jenkins → Credentials → System → Global credentials
  2. Click "Add Credentials"
  3. Enter:
  4. Kind: Secret text
  5. Secret: Your CloudSlash API key
  6. ID: cloudslash-api-key
  7. Description: "CloudSlash API Key"

Step 3: Create Jenkinsfile

Add CloudStencil to your project's Jenkinsfile:

pipeline {
    agent any

    stages {
        stage('Initialize CloudStencil') {
            steps {
                script {
                    // Define the configuration
                    def cloudslashConfig = """
                        version: 1.0
                        accounts:
                          - provider: aws
                            name: production
                            accounts:
                              - id: "123456789012"
                                role_arn: "arn:aws:iam::123456789012:role/CloudStencilAccess"
                        security:
                          compliance:
                            enabled: true
                            benchmark: cis
                    """
                    writeFile file: 'cloudslash.yml', text: cloudslashConfig
                }
            }
        }

        stage('Run CloudStencil Scan') {
            steps {
                sh '''
                    # Run the scan
                    docker run --rm -v $(pwd):/app -w /app \
                        cloudslash/scan:latest \
                        scan --config cloudslash.yml
                '''
            }
        }

        stage('Publish Results') {
            steps {
                script {
                    // Publish scan results to Jenkins
                    publishHTML([
                        reportDir: '.',
                        reportFiles: 'cloudslash-output/*',
                        reportName: 'CloudStencil Scan Report',
                        reportTitles: 'Infrastructure Security and Cost Analysis Report'
                    ])
                }
            }
        }
    }
}

Step 4: Run Pipeline

Click on "Build Now" in Jenkins to run your first scan.

Full Configuration Reference

Common Workflow Patterns

Pattern 1: PR/MR Cost Scanning

Prevent costly changes before merging:

pipeline {
    agent any

    environment {
        CLOUDSLASH_API_KEY = credentials('cloudslash-api-key')
        CLOUDSLASH_CONFIG = 'cloudslash.yml'
        CLOUDSLASH_MODE = 'pr'
    }

    stages {
        stage('Cost Analysis') {
            when {
                branch 'develop'
            }
            steps {
                script {
                    writeFile file: env.CLOUDSLASH_CONFIG, text: """
                        version: 1.0
                        accounts:
                          - provider: aws
                            name: development
                            accounts:
                              - id: "123456789012"
                                role_arn: "arn:aws:iam::123456789012:role/CloudStencilDevelopmentRole"
                    """
                }
                sh 'docker run --rm -v $(pwd):/app -w /app cloudslash/scan:latest scan --config cloudslash.yml'
            }
        }
    }

    post {
        always {
            // Publish results
            publishHTML([
                reportDir: '.',
                reportFiles: 'cloudslash-output/*',
                reportName: 'Cost Analysis Report'
            ])
        }
    }
}

Pattern 2: Scheduled Drift Detection

Monitor production infrastructure changes continuously:

// Run this via Jenkins Console or scheduler for scheduled runs
pipeline {
    agent any

    environment {
        CLOUDSLASH_API_KEY = credentials('cloudslash-api-key')
        CLOUDSLASH_CONFIG = 'cloudslash.yml'
        CLOUDSLASH_MODE = 'scheduled'
        SCAN_ID = "drift-$(date +%Y%m%d-%H%M%S)"
    }

    triggers {
        cron('0 2 * * *')  // Daily at 2 AM UTC
    }

    stages {
        stage('Drift Detection') {
            steps {
                script {
                    writeFile file: env.CLOUDSLASH_CONFIG, text: """
                        version: 1.0
                        accounts:
                          - provider: aws
                            name: production
                            accounts:
                              - id: "123456789012"
                                role_arn: "arn:aws:iam::123456789012:role/CloudStencilProductionRole"
                    """
                    sh "docker run --rm -v $(pwd):/app -w /app cloudslash/scan:latest scan --config cloudslash.yml --mode ${env.CLOUDSLASH_MODE} --scan-id ${env.SCAN_ID}"
                }
            }
        }
    }

    post {
        always {
            // Send notification
            mail to: 'team@example.com',
                 subject: "Drift Detection Report - ${env.SCAN_ID}",
                 body: "See attached report for details"
        }
    }
}

Pattern 3: Deployment Gates

Block deployments that don't meet security requirements:

pipeline {
    agent any

    environment {
        CLOUDSLASH_API_KEY = credentials('cloudslash-api-key')
        CLOUDSLASH_CONFIG = 'cloudslash.yml'
        CLOUDSLASH_MODE = 'deployment-gate'
    }

    stages {
        stage('Security Check') {
            steps {
                script {
                    // Pre-deployment scan
                    sh 'docker run --rm -v $(pwd):/app -w /app cloudslash/scan:latest scan --config cloudslash.yml'
                }
            }
        }

        stage('Verify Gate') {
            steps {
                script {
                    // Verify the scan passed
                    def scanResult = sh(
                        script: "cat cloudslash-output/scan-result.json",
                        returnStdout: true
                    ).trim()

                    if (scanResult.contains('"status": "FAILED"')) {
                        error "Security gate failed! Deployment blocked."
                    }
                }
            }
        }
    }

    post {
        success {
            echo "✅ Security gate passed - deployment allowed"
        }
        failure {
            echo "❌ Security gate failed - deployment blocked"
        }
    }
}

Pattern 4: Monorepo Setup

Centralized scanning for multiple repositories:

pipeline {
    agent any

    stages {
        stage('Monorepo Scan') {
            steps {
                script {
                    // Define subrepositories
                    def subrepos = ['frontend', 'backend', 'infrastructure']

                    subrepos.each { subdir ->
                        dir subdir {
                            writeFile file: 'cloudslash.yml', text: """
                                version: 1.0
                                accounts:
                                  - provider: aws
                                    name: production
                                    accounts:
                                      - id: "123456789012"
                                        role_arn: "arn:aws:iam::123456789012:role/CloudStencilProductionRole"
                            """
                            sh "docker run --rm -v $(pwd):/app -w /app cloudslash/scan:latest scan --config cloudslash.yml"
                        }
                    }
                }
            }
        }

        stage('Aggregate Results') {
            steps {
                script {
                    // Aggregate findings from all subrepositories
                    def allFindings = [:]

                    dir 'frontend' {
                        allFindings.put('frontend', sh(script: 'cat cloudslash-output/*', returnStdout: true))
                    }
                    dir 'backend' {
                        allFindings.put('backend', sh(script: 'cat cloudslash-output/*', returnStdout: true))
                    }
                    dir 'infrastructure' {
                        allFindings.put('infrastructure', sh(script: 'cat cloudslash-output/*', returnStdout: true))
                    }

                    // Publish aggregated report
                    writeFile file: 'monorepo-report.json', text: allFindings.toString()
                }
            }
        }
    }
}

Pattern 5: Multi-Cloud Setup

Unified scanning across AWS, Azure, and GCP:

pipeline {
    agent any

    stages {
        stage('AWS Scan') {
            environment {
                AWS_ACCESS_KEY = credentials('cloudslash-aws-access-key')
                AWS_SECRET_KEY = credentials('cloudslash-aws-secret-access-key')
                CLOUDSLASH_CONFIG = 'aws-cloudslash.yml'
            }

            steps {
                script {
                    writeFile file: env.CLOUDSLASH_CONFIG, text: """
                        version: 1.0
                        accounts:
                          - provider: aws
                            name: production
                            accounts:
                              - id: "123456789012"
                                role_arn: "arn:aws:iam::123456789012:role/CloudStencilProductionRole"
                    """
                    sh "docker run --rm -v $(pwd):/app -w /app cloudslash/scan:latest scan --config cloudslash.yml"
                }
            }
        }

        stage('Azure Scan') {
            environment {
                AZURE_CLIENT_ID = credentials('cloudslash-azure-client-id')
                AZURE_CLIENT_SECRET = credentials('cloudslash-azure-client-secret')
                AZURE_TENANT_ID = credentials('cloudslash-azure-tenant-id')
                CLOUDSLASH_CONFIG = 'azure-cloudslash.yml'
            }

            steps {
                script {
                    writeFile file: env.CLOUDSLASH_CONFIG, text: """
                        version: 1.0
                        accounts:
                          - provider: azure
                            name: production
                            subscriptions:
                              - id: "11111111-1111-1111-1111-111111111111"
                    """
                    sh "docker run --rm -v $(pwd):/app -w /app cloudslash/scan:latest scan --config cloudslash.yml"
                }
            }
        }

        stage('GCP Scan') {
            environment {
                GCP_CLIENT_ID = credentials('cloudslash-gcp-client-id')
                GCP_CLIENT_SECRET = credentials('cloudslash-gcp-client-secret')
                CLOUDSLASH_CONFIG = 'gcp-cloudslash.yml'
            }

            steps {
                script {
                    writeFile file: env.CLOUDSLASH_CONFIG, text: """
                        version: 1.0
                        accounts:
                          - provider: gcp
                            name: production
                            projects:
                              - id: "my-project-123"
                    """
                    sh "docker run --rm -v $(pwd):/app -w /app cloudslash/scan:latest scan --config cloudslash.yml"
                }
            }
        }

        stage('MultiCloud Report') {
            steps {
                script {
                    // Combine all reports
                    writeFile file: 'multicloud-report.json', text: """
                        {
                          "aws": $(find cloudslash-output -name '*aws*' -exec cat {} \\;),
                          "azure": $(find cloudslash-output -name '*azure*' -exec cat {} \\;),
                          "gcp": $(find cloudslash-output -name '*gcp*' -exec cat {} \\;)
                        }
                    """
                }
            }
        }
    }
}

Security Best Practices

1. Use Secure Credentials

Always use Jenkins credentials:

environment {
    CLOUDSLASH_API_KEY = credentials('cloudslash-api-key')
}

// ❌ BAD: Hardcoded credentials
// environment {
//     CLOUDSLASH_API_KEY = "sk-1234567890"
// }

2. Restrict Pipeline Access

Use agent constraints:

pipeline {
    agent {
        node {
            label 'cloudslash-only'
        }
    }
}

3. Validate Inputs

Prevent injection attacks:

stage('Validate Input') {
    steps {
        script {
            if (!(env.INPUT_BRANCH =~ /^(main|develop|release.*)$/)) {
                error "Invalid branch: ${env.INPUT_BRANCH}"
            }
        }
    }
}

4. Use Docker for Isolation

Run scans in Docker containers:

steps {
    // Use separate Docker container for security
    sh 'docker run --rm -v $(pwd):/app -w /app cloudslash/scan:latest scan --config cloudslash.yml'
}

5. Clean Up Resources

Remove old build artifacts:

post {
    always {
        sh 'docker rm -f cloudslash-scan 2>/dev/null || true'
    }
}

Multi-Account Setup

Using AWS Cross-Account Roles

stage('AWS Cross-Account') {
    steps {
        script {
            // Assume role intermediate
            sh '''
                # Assume cross-account role
                aws sts assume-role \
                    --role-arn "arn:aws:iam::123456789012:role/CloudStencilScanRole" \
                    --role-session-name "CloudStencilScanSession" \
                    --duration-seconds 900 \
                    --query 'Credentials.AssumeRoleResult.Credentials' \
                    --output text > credentials.json

                # Set environment variables for AWS access
                export AWS_ACCESS_KEY_ID=$(grep AccessKeyId credentials.json | awk -F'"' '{print $4}')
                export AWS_SECRET_ACCESS_KEY=$(grep SecretAccessKey credentials.json | awk -F'"' '{print $4}')
                export AWS_SESSION_TOKEN=$(grep SessionToken credentials.json | awk -F'"' '{print $4}')
            '''
            // Then run CloudStencil scan with these credentials
        }
    }
}

Using Azure RBAC

stage('Azure RBAC Setup') {
    steps {
        script {
            // Create service principal
            sh '''
                az ad sp create-for-rbac \
                    --role="Contributor" \
                    --scopes="/subscriptions/12345678-1234-1234-1234-123456789012" \
                    --name="CloudStencilScan" \
                    --sdk-auth > azure-credentials.json
            '''
        }
    }
}

Notification Integration

Slack Integration

post {
    always {
        script {
            if (currentBuild.currentResult == 'SUCCESS') {
                slackSend(
                    channel: '#infra-ops',
                    color: 'good',
                    message: "✅ CloudStencil scan passed for ${env.JOB_NAME} (${env.BUILD_NUMBER})"
                )
            } else {
                slackSend(
                    channel: '#infra-ops',
                    color: 'danger',
                    message: "❌ CloudStencil scan failed for ${env.JOB_NAME} (${env.BUILD_NUMBER})"
                )
            }
        }
    }
}

Email Notifications

post {
    failure {
        // Notify on failure only
        emailext (
            subject: "❌ CloudStencil Scan Failed - ${env.JOB_NAME} (${env.BUILD_NUMBER})",
            body: """
                <h3>CloudStencil Scan Failed</h3>
                <p>Build: <a href="${env.BUILD_URL}">${env.JOB_NAME} #${env.BUILD_NUMBER}</a></p>
                <p>Changes: ${env.CHANGES}</p>
                <p>View scan report: ${env.REPORT_URL}</p>
            """,
            to: 'team@example.com',
            mimeType: 'text/html'
        )
    }
    success {
        emailext subject: "✅ CloudStencil Scan Passed - ${env.JOB_NAME} (${env.BUILD_NUMBER})",
                  body: "Infrastructure scan passed for ${env.JOB_NAME} #${env.BUILD_NUMBER}",
                  to: 'team@example.com'
    }
}

Troubleshooting

Common Issues

Issue 1: "Credentials not found" error

Symptom: Jenkins cannot find the credentials

Solution:

// Verify credentials stored with the correct ID
environment {
    CLOUDSLASH_API_KEY = credentials('cloudslash-api-key')
}

// Check if credential exists
// Go to Manage Jenkins → Credentials → System → Global credentials

Issue 2: "Docker not available" error

Symptom: Cannot run Docker commands

Solution:

pipeline {
    agent {
        docker {
            image 'cloudslash/scan:latest'
        }
    }
}

Issue 3: "No such file or directory" errors

Symptom: Cannot find previously saved scan artifacts

Solution:

post {
    always {
        // Clean up and ensure no stale artifacts
        sh '''
            rm -rf cloudslash-output/
            mkdir -p cloudslash-output
        '''
    }
}

Issue 4: "Max token exceeded" error

Symptom: Large report is too large for the supported token limit

Solution:

// Split the report into parts
// OR reduce the verbosity level
sh 'cloudslash scan --config cloudslash.yml --output-type compact'

Issue 5: "Job stuck/unresponsive"

Symptom: Pipeline gets stuck

Solution:

pipeline {
    timeout(time: 30, unit: 'MINUTES') {
        // Pipeline content here
    }
}

Feature Comparison Matrix

Jenkins vs Other Platforms

Feature GitHub Actions GitLab CI Azure DevOps CircleCI Jenkins
Setup Time (min) ⚡ 2-3 ⚡ 2-3 ⭐ 3-5 ⚡ 2-4 ⭐ 5-10
PR/MR Comments ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐ ⭐⭐
Free Plan ⭐⭐⭐⭐⭐ (unlimited) ⭐⭐⭐⭐⭐ (unlimited) ⭐⭐⭐⭐ (limited) ⭐⭐ (limited) ⭐⭐ (limited)
Scheduled Jobs ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐⭐⭐
Monorepo Support ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐⭐⭐
Multi-Cloud Support ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐⭐⭐
Docker Execution ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐⭐
Custom Actions ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐ ⭐⭐⭐⭐⭐
Cost Build Minutes ⚡ Free (public) ⚡ Free (public) ⭐ Free (small) ⭐ Limited ⚡ Free (self-hosted)
Integrations ⭐⭐⭐⭐⭐ (AWS, Azure, GCP) ⭐⭐⭐⭐ ⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐⭐⭐

Best Overall: Jenkins (Highly customizable, self-hosted, enterprise-grade control)


Click for detailed feature descriptions ### Jenkins Strengths - ✅ **Fully self-hosted**: Complete control over infrastructure - ✅ **Maximum flexibility**: Custom plugins and integrations - ✅ **Enterprise-ready**: Rich set of security and monitoring features ### Jenkins Weaknesses - ❌ Steeper learning curve: More complex configuration - ❌ Requires manual maintenance: Self-hosting responsibilities - ❌ Less intuitive UI: More manual configuration needed

Common Patterns

Pattern 1: Conditional Execution

Run scans based on branch or environment:

stages {
    stage('Conditional Scan') {
        when {
            branch 'production'
        }
        steps {
            script {
                sh 'docker run --rm -v $(pwd):/app -w /app cloudslash/scan:latest scan --config cloudslash-prod.yml'
                // Also run linting for production
                sh 'docker run --rm -v $(pwd):/app -w /app cloudslash/scan:latest lint --config cloudslash-prod.yml'
            }
        }
    }
}

Pattern 2: Matrix Builds

Run scans across multiple environments:

stages {
    stage('Environment Matrix') {
        parallel {
            stage('Development') {
                environment {
                    ENV = 'dev'
                }
                steps {
                    script {
                        sh 'docker run --rm -v $(pwd):/app -w /app cloudslash/scan:latest scan --config cloudslash-dev.yml'
                    }
                }
            }
            stage('Production') {
                environment {
                    ENV = 'prod'
                }
                steps {
                    script {
                        sh 'docker run --rm -v $(pwd):/app -w /app cloudslash/scan:latest scan --config cloudslash-prod.yml'
                    }
                }
            }
        }
    }
}

Pattern 3: Docker-based Customization

Use custom Docker containers:

pipeline {
    agent {
        docker {
            image 'custom-cloudslash-image:latest'
            args '-v /var/run/docker.sock:/var/run/docker.sock'
        }
    }

    stages {
        stage('Custom Scan') {
            steps {
                script {
                    // Use custom environment
                    writeFile file: 'cloudslash.yml', text: custom_config
                    sh 'custom-scanner --config cloudslash.yml'
                }
            }
        }
    }
}

Best Practices

1. Use Flexible Pipelines

Use parameterized builds:

pipeline {
    agent any

    parameters {
        string(name: 'ENVIRONMENT', defaultValue: 'development', description: 'Environment to scan')
        booleanParam(name: 'RUN_LINTING', defaultValue: true, description: 'Run linting checks')
    }

    stages {
        stage('Scan') {
            steps {
                script {
                    def configFile = "cloudslash-${params.ENVIRONMENT}.yml"
                    sh "docker run --rm -v $(pwd):/app -w /app cloudslash/scan:latest scan --config ${configFile}"
                }
            }
        }
    }
}

2. Archive and Retain Artifacts

Archive scan results:

post {
    always {
        archiveArtifacts artifacts: 'cloudslash-output/**', allowEmptyArchive: true
    }
}

3. Use Notification Hooks

Integrate with external systems:

post {
    always {
        jiraComment jiraIssueKey: 'JIRA-123', message: "CloudStencil scan completed: ${currentBuild.currentResult}"
    }
}

4. Use Job-Level Descriptions

Provide clear task descriptions:

pipeline {
    agent any

    description: 'Automated CloudStencil scanning for multiple platforms'

    options {
        buildDiscarder(logRotator(numToKeepStr: '20'))
    }

    // ... rest of pipeline
}

FAQ

Can I use self-hosted runners with CloudStencil? Yes! You can run CloudStencil on self-hosted runners. Simply set up a custom Docker container with the CloudStencil scan image and configure your Jenkins pipeline to use the appropriate agent or environment.
How do I handle scheduled scans? Use the Jenkins triggers feature to schedule scans or use the Jenkins Console to run the pipeline manually at specific times.
How do I protect sensitive information? Always use Jenkins credentials for sensitive data like API keys. Never hardcode them in your pipeline scripts.

Documentation Version: 2.0.0 Last Updated: September 10, 2026 Related: CI/CD Integration Guide | Best Practices