Skip to content

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_KEY with 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:

-orbs:
  cloudslash: cloudslash/scan@1.0.0

environments:
  production:
    deployment: false

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:

- run:
    name: Run CloudSlash
    command: |
      cloudslash scan --config cloudslash.yml || exit 1

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:

resource_class: xlarge  # Use larger executor

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 Actions

Common 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