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:
- Go to Manage Jenkins → Manage Plugins → Available
- Search for "CloudStencil"
- 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:
- Go to Manage Jenkins → Credentials → System → Global credentials
- Click "Add Credentials"
- Enter:
- Kind: Secret text
- Secret: Your CloudSlash API key
- ID:
cloudslash-api-key - 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:
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:
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:
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:
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 neededCommon 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:
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