CircleCI Integration Guide¶
Run automated cost checks, gate infrastructure jobs, and post PR summaries inside CircleCI workflows.
Quick Start (5-Minute Setup)¶
Prerequisites¶
- CircleCI account
- Project with CircleCI configuration
- CloudSlash API key (from dashboard)
- (Optional) CircleCI Context for secrets
Step 1: Install CloudStencil¶
Add CloudStencil to your .circleci/config.yml:
# .circleci/config.yml
version: 2.1
orbs:
cloudslash: cloudslash/scan@1.0.0
workflows:
version: 2
scan-workflow:
jobs:
- cloudslash/pr-scan
jobs:
pr-scan:
executor: cloudslash/default
steps:
- checkout
- cloudslash/scan:
config: cloudslash.yml
Step 2: Add Context for Secrets¶
Go to CircleCI → Contexts → Create New Context:
- Add
API_KEYwith your CloudSlash API key - Assign context to your project
Step 3: Configure CloudSlash¶
Create cloudslash.yml in your repository root:
version: 1.0
accounts:
- provider: aws
name: production
accounts:
- id: "123456789012"
role_arn: "arn:aws:iam::123456789012:role/CloudSlashAccess"
security:
compliance:
enabled: true
benchmark: cis
notifications:
slack:
webhook_url: $SLACK_WEBHOOK
Done! Your first scan will run on the next commit.
Full Configuration Reference¶
Common Workflow Patterns¶
Pattern 1: PR/MR Cost Scanning¶
Prevent costly changes before merging:
# .circleci/config.yml
version: 2.1
orbs:
cloudslash: cloudslash/scan@1.0.0
patchstep: ecs-github/aws/1.0.0
workflows:
version: 2
cost-scan-workflow:
jobs:
# Job to list changed files
- list-changed-files:
type: startup
filters:
branches:
only: /^.*$/
- cost-scan:
requires:
- list-changed-files
filters:
branches:
only: /^.*$/ || main/
type: build
filters:
branches:
only: /^.*$/ || main/
jobs:
list-changed-files:
docker:
- image: cimg/base:stable
steps:
- checkout
- run:
name: List changed files
command: |
git diff --name-only origin/main...${CIRCLE_SHA1} > changed_files.txt
if [ -s changed_files.txt ]; then
echo "Files changed:"
cat changed_files.txt
exit 0
else
echo "No changes detected"
exit 1
fi
- persist_to_workspace:
root: .
paths:
- changed_files.txt
cost-scan:
docker:
- image: cloudslash/scan:latest
steps:
- checkout
- attach_workspace:
at: .
- run:
name: Install CloudSlash
command: if [ -f changed_files.txt ]; then echo "Changed files detected"; else echo "No changes"; fi
- cloudslash/scan:
config: cloudslash.yml
mode: pr
artifacts: true
- store_artifacts:
path: cloudslash-output/report.json
destination: scan-results
- run:
name: Publish Scan Results to PR
command: |
# Create PR comment with findings
(comment_file_path)
if [ -f changed_files.txt ]; then
echo "Found changes in:"
cat changed_files.txt
fi
Pattern 2: Scheduled Drift Detection¶
Monitor production infrastructure changes continuously:
# .circleci/config.yml
version: 2.1
orbs:
cloudslash: cloudslash/scan@1.0.0
workflows:
version: 2
drift-detection-workflow:
jobs:
- drift-scan:
filters:
branches:
only: main
times: ' recurring-schedule: 0 2 * * *'
jobs:
drift-scan:
docker:
- image: cloudslash/scan:latest
steps:
- checkout
- cloudslash/scan:
config: cloudslash.yml
mode: scheduled
scan-id: drift-$(date +%Y%m%d-%H%M%S)
- store_artifacts:
path: cloudslash-output/drift-report.json
destination: drift-scan-results
scheduled-scan-job:
docker:
- image: cloudslash/scan:latest
environment:
SCAN_ID: drift-$(date +%Y%m%d-%H%M%S)
steps:
- checkout
- run:
name: Schedule Drift Scan
command: |
# Define your schedule for this job
echo "Schedule: 0 2 * * *" # Daily at 2 AM UTC
# And optionally configure recurring triggers via Jenkins, GitHub Actions schedules, or Azure DevOps Schedules
- cloudslash/scan:
config: cloudslash.yml
mode: scheduled
scan-id: $SCAN_ID
- store_artifacts:
path: cloudslash-output/drift-report.json
Pattern 3: Deployment Gates¶
Block deployments that don't meet security requirements:
# .circleci/config.yml
version: 2.1
orbs:
cloudslash: cloudslash/scan@1.0.0
workflows:
version: 2
deployment-workflow:
jobs:
- build:
filters:
branches:
only: main
- cloudslash-security-check:
requires:
- build
filters:
branches:
only: main
type: approval # Requires manual approval
- deploy:
requires:
- cloudslash-security-check
filters:
branches:
only: main
type: deploy
jobs:
build:
docker:
- image: cimg/node:18
steps:
- checkout
- run:
name: Build Application
command: |
npm install
npm run build
cloudslash-security-check:
docker:
- image: cloudslash/scan:latest
steps:
- checkout
- cloudslash/scan:
config: cloudslash.yml
mode: deployment-gate
fail-on-errors: true
fail-on-warnings: true
- run:
name: Verify Security Gate
command: |
if [ "${CLOUDSLASH_SCAN_PASSED}" != "true" ]; then
echo "Security gate failed!"
exit 1
fi
echo "Security gate passed!"
deploy:
docker:
- image: cimg/aws:latest
steps:
- checkout
- run:
name: Deploy to Production
command: |
aws s3 sync build/ s3://my-app-bucket
Pattern 4: Monorepo Setup¶
Centralized scanning for multiple repositories:
# .circleci/config.yml (monorepo .circleci/config.yml)
version: 2.1
orbs:
cloudslash: cloudslash/scan@1.0.0
workflows:
version: 2
monorepo-workflow:
jobs:
- scan-repository:
name: scan-frontend
context: cloudslash
- scan-repository:
name: scan-backend
context: cloudslash
- aggregate-results:
requires:
- scan-frontend
- scan-backend
jobs:
scan-repository:
docker:
- image: cloudslash/scan:latest
steps:
- checkout
# Change to frontend subdirectory
- run:
name: Scan frontend repository
working_directory: ./frontend
command: "cd frontend && cloudslash scan --config cloudslash.yml"
# Save results
- persist_to_workspace:
root: .
paths:
- frontend/cloudslash-results.json
- store_artifacts:
path: frontend/cloudslash-results.json
destination: frontend-scan-results
aggregate-results:
docker:
- image: cloudslash/scan:latest
steps:
- checkout
- attach_workspace:
at: .
- run:
name: Aggregate results from all repositories
command: |
# Read frontend and backend results
echo "Scanning from multiple subdirectories..."
# Combine and display results
Pattern 5: Multi-Cloud Setup¶
Unified scanning across AWS, Azure, and GCP:
# .circleci/config.yml
version: 2.1
orbs:
cloudslash: cloudslash/scan@1.0.0
aws-cli: circleci/aws-cli@2.1.0
azure-cli: circleci/azure-cli@2.0.0
gcp-cli: circleci/gcp-cli@2.0.0
workflows:
version: 2
multi-cloud-workflow:
jobs:
# AWS Scan
- aws-deploy-and-scan:
filters:
branches:
only: main
environment:
CLOUDSLASH_CONFIG: aws
CLOUDSLASH_MODE: pr
# Azure Scan
- azure-deploy-and-scan:
requires:
- aws-deploy-and-scan
# GCP Scan
- gcp-deploy-and-scan:
requires:
- azure-deploy-and-scan
jobs:
aws-deploy-and-scan:
docker:
- image: cloudslash/scan:latest
steps:
- checkout
- aws-cli/setup:
aws-access-key-id: $AWS_ACCESS_KEY
aws-secret-access-key: $AWS_SECRET
aws-region: us-east-1
# Deploy to AWS
- run:
name: Deploy to AWS
command: |
# Your deployment commands
echo "Deploying to AWS..."
# Run CloudSlash
- cloudslash/scan:
config: cloudslash.yml
- store_artifacts:
path: cloudslash-output/aws-results.json
destination: aws-scan-results
azure-deploy-and-scan:
docker:
- image: cloudslash/scan:latest
steps:
- checkout
- azure-cli/setup:
azure_subscription_id: $AZURE_SUBSCRIPTION_ID
azure_client_id: $AZURE_CLIENT_ID
azure_client_secret: $AZURE_CLIENT_SECRET
azure_tenant_id: $AZURE_TENANT_ID
- azure-cli/install_commands:
azure-cli: azure
# Deploy to Azure
- run:
name: Deploy to Azure
command: |
echo "Deploying to Azure..."
# Run CloudSlash
- cloudslash/scan:
config: cloudslash.yml
- store_artifacts:
path: cloudslash-output/azure-results.json
destination: azure-scan-results
gcp-deploy-and-scan:
docker:
- image: cloudslash/scan:latest
steps:
- checkout
- gcp-cli/install:
gcp_client_id: $GCP_CLIENT_ID
gcp_client_secret: $GCP_CLIENT_SECRET
gcp_project: $GCP_PROJECT
gcp_location: $GCP_LOCATION
# Deploy to GCP
- run:
name: Deploy to GCP
command: |
echo "Deploying to GCP..."
# Run CloudSlash
- cloudslash/scan:
config: cloudslash.yml
- store_artifacts:
path: cloudslash-output/gcp-results.json
destination: gcp-scan-results
Security Best Practices¶
1. Use Contexts for Secrets¶
NEVER hardcode secrets:
# ✅ GOOD: Use CircleCI Contexts
docker:
- image: cloudslash/scan:latest
environment:
API_KEY:
value: $CLOUDSLASH_API_KEY
# ❌ BAD: Hardcoded secrets
docker:
- image: cloudslash/scan:latest
environment:
API_KEY: "sk-1234567890"
2. Protect Environment Variables¶
Use restricted visibility for secrets:
environment:
CLOUDSLASH_API_KEY:
value: $CLOUDSLASH_API_KEY
description: "CloudSlash API key for scanning"
3. Run in Restricted Partners¶
Limit usage with appropriate policies:
4. Validate Input Parameters¶
Prevent injection attacks:
- run:
name: Validate Environment
command: |
if [ "${CIRCLE_BRANCH}" != "main" ]; then
echo "Building non-main branch..."
exit 1
fi
5. Handle Errors Gracefully¶
Provide clear error messages:
Multi-Account Setup¶
Using different configs per account¶
# CircleCI config.yml
version: 2.1
orbs:
cloudslash: cloudslash/scan@1.0.0
workflows:
version: 2
multi-account-workflow:
jobs:
- dev-scan:
filters:
branches:
only:
- /development/
context: cloudslash
env:
CLOUDSLASH_CONFIG: dev
- prod-scan:
filters:
branches:
only:
- main
context: cloudslash
env:
CLOUDSLASH_CONFIG: prod
jobs:
dev-scan:
docker:
- image: cloudslash/scan:latest
steps:
- checkout
- cloudslash/scan:
config: cloudslash-dev.yml
prod-scan:
docker:
- image: cloudslash/scan:latest
steps:
- checkout
- cloudslash/scan:
config: cloudslash-prod.yml
mode: deployment-gate
Notification Integration¶
Slack Integration¶
# .circleci/config.yml
version: 2.1
orbs:
cloudslash: cloudslash/scan@1.0.0
slack: circleci/slack@3.4.0
workflows:
version: 2
notification-workflow:
jobs:
- cloudslash-scan:
persist_to_workspace:
root: .
paths:
- cloudslash-output
- notify-slack:
requires:
- cloudslash-scan
filters:
branches:
only: main
jobs:
cloudslash-scan:
docker:
- image: cloudslash/scan:latest
steps:
- checkout
- cloudslash/scan:
config: cloudslash.yml
mode: pr
- persist_to_workspace:
root: .
paths:
- cloudslash-output
notify-slack:
docker:
- image: circleci/node:18
environment:
SLACK_WEBHOOK_URL: $SLACK_WEBHOOK
steps:
- attach_workspace:
at: .
- slack/notify:
custom: >
{
"text": "CloudSlash scan results for commit $CIRCLE_SHA1",
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "### 🔍 CloudSlash Infrastructure Scan"
}
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*Status*: ✅ Passed\n*Repository*: $CIRCLE_PROJECT_REPONAME\n*Commit*: <https://github.com/$CIRCLE_PROJECT_USERNAME/$CIRCLE_PROJECT_REPONAME/commit/$CIRCLE_SHA1|$CIRCLE_SHA1>"
}
}
]
}
event: always
mention: "here"
mention_on: failure
Email Integration¶
# .circleci/config.yml
version: 2.1
orbs:
cloudslash: cloudslash/scan@1.0.0
parameters:
email_recipient:
type: string
default: team@example.com
workflows:
version: 2
email-workflow:
jobs:
- cloudslash-scan:
persist_to_workspace:
root: .
paths:
- cloudslash-output
- send-email:
requires:
- cloudslash-scan
filters:
branches:
only: main
jobs:
cloudslash-scan:
docker:
- image: cloudslash/scan:latest
steps:
- checkout
- cloudslash/scan:
config: cloudslash.yml
- persist_to_workspace:
root: .
paths:
- cloudslash-output
send-email:
docker:
- image: cirruslabs/email:latest
environment:
RECIPIENT: << pipeline.parameters.email_recipient >>
steps:
- attach_workspace:
at: .
- run:
name: Send Email Report
command: |
subject="🔒 Infrastructure Scan: $CIRCLE_BUILD_NUM"
body="Scan completed successfully.
Repository: $CIRCLE_PROJECT_REPONAME
Branch: $CIRCLE_BRANCH"
mail -s "$subject" "$RECIPIENT" <<< "$body"
Troubleshooting¶
Common Issues¶
Issue 1: Context Not Found¶
Symptom: "Context 'cloudslash' not found"
Solution:
# Add context to workflow
workflows:
version: 2
scan-workflow:
jobs:
- cloudslash/pr-scan:
context: cloudslash # Add this
Issue 2: No changes detected¶
Symptom: "No files changed"
Solution:
- run:
name: List files
command: |
git fetch origin main
git diff --name-only origin/main...${CIRCLE_SHA1}
Issue 3: Very large report blocking scanning¶
Symptom: Scan hangs or times out
Solution:
Issue 4: Artifacts not found¶
Symptom: Error trying to attach running workspace
Solution:
- checkout
- cloudslash/scan:
config: cloudslash.yml
- persist_to_workspace:
root: .
paths:
- cloudslash-output
- attach_workspace:
at: .
Issue 5: Error parsing detection result¶
Symptom: Failed when parsing report
Fix/Target: Minimize resulting output by adjusting CloudSlash settings and re-execute; do not skip or invalidate detection(s).
Feature Comparison Matrix¶
CircleCI 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: GitHub Actions (Free for public repos, extensive marketplace)
Click for detailed feature descriptions
### CircleCI Strengths - ✅ **Docker-first**: Native and excellent Docker execution - ✅ **Configurable**: Fine-grained control over execution - ✅ **Affordable for small teams**: Free tier available ### CircleCI Weaknesses - ❌ **Limited free tier**: No-secrets limits on build minutes - ❌ **Custom actions**: Fewer marketplace actions compared to GitHub - ❌ **Higher cost at scale**: Build minutes are not free like GitHub ActionsCommon Patterns¶
Pattern 1: Conditional Scanning¶
Run scans based on branch:
jobs:
conditional-scan:
docker:
- image: cloudslash/scan:latest
steps:
- checkout
- run:
name: Check branch
command: |
if [ "$CIRCLE_BRANCH" = "main" ]; then
echo "Running production scan..."
cloudslash scan --config cloudslash-prod.yml
else
echo "Running dev scan..."
cloudslash scan --config cloudslash-dev.yml
fi
Pattern 2: Parallel Scanning Across Clouds¶
Run scans in parallel:
workflows:
version: 2
multi-cloud-workflow:
jobs:
- aws-scan:
filters:
branches:
only: main
- azure-scan:
filters:
branches:
only: main
requires:
- aws-scan
- gcp-scan:
filters:
branches:
only: main
requires:
- azure-scan
Pattern 3: With Data Fetch and Subprocess¶
Combine data fetching with scanning:
- run:
name: Fetch Costs
command: |
aws ec2 describe-instances > instances.json
cloudslash scan --config cloudslash.yml --costs-url https://example.com/report.json
FAQ¶
Can I use self-hosted runners?
See `https://discuss.circleci.com/t/express-runner-properly-lists-activations` for details and to run away from small runners if needed; update your config to `resource_class: docker/custom` to try leveraging community tools.How do I deal with large scan reports?
Large reports can slow down processing, especially EL-to-AE upload latency. If bandwidth is a constraint, consider CloudSlash orchestration or caching, minifying CloudSlash output, or offloading to self-hosted runners.Documentation Version: 2.0.0 Last Updated: September 10, 2026 Related: CI/CD Integration Guide | Best Practices