Skip to content

GitHub Actions Integration Guide

Run automated cost checks on pull requests, export SARIF findings, and block budget-exceeding deployments.

Quick Start (5-Minute Setup)

Prerequisites

  • GitHub account with admin access
  • GitHub Actions enabled on repository
  • CloudSlash API key (from dashboard)
  • (Optional) Personal Access Token for private repositories

Step 1: Install CloudSlash Action

Add CloudSlash to your .github/workflows/ directory:

# .github/workflows/cloudslash.yml
name: CloudSlash Security & Cost Scan

on:
  pull_request:
    branches: [ main, develop ]
  push:
    branches: [ main, develop ]
  schedule:
    # Run daily at 2 AM
    - cron: '0 2 * * *'

jobs:
  scan:
    name: CloudSlash Scan
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Setup CloudSlash
        uses: cloudslash/scan@v1
        with:
          api-key: ${{ secrets.CLOUDSLASH_API_KEY }}
          config: cloudslash.yml

      - name: Run PR/MR Scan
        if: github.event_name == 'pull_request'
        run: cloudslash scan --config cloudslash.yml --mode pr --commit-ref ${{ github.event.pull_request.head.sha }}

      - name: Run Scheduled Scan
        if: github.event_name == 'schedule'
        run: cloudslash scan --config cloudslash.yml --mode scheduled --scan-id daily-scan

      - name: Publish Report
        if: always()
        run: |
          cloudslash report --format html --output report.html
          echo "Report saved to report.html"

Step 2: Add Secrets

Go to your repository → Settings → Secrets → New repository secret:

  • Add CLOUDSLASH_API_KEY with your CloudSlash API key
  • (Optional) For private repos: Add GITHUB_TOKEN (already available)

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: "${{ secrets.SLACK_WEBHOOK }}"

Done! Your first scan will run on the next PR or push.

Full Configuration Reference

Common Workflow Patterns

Pattern 1: PR/MR Cost Scanning

Prevent costly changes from being merged:

# .github/workflows/cost-scan.yml
name: Infrastructure Cost Scan

on:
  pull_request:
    types:
      - opened
      - synchronize
      - reopened

permissions:
  pull-requests: write

jobs:
  cost-scan:
    name: Cost Analysis
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: CloudSlash Scan
        uses: cloudslash/scan@v1
        with:
          api-key: ${{ secrets.CLOUDSLASH_API_KEY }}
          config: cloudslash-config.yml

      - name: Upload Cost Report
        uses: actions/upload-artifact@v3
        with:
          name: cost-scan-report
          path: cost-report.json

      - name: Comment on PR
        uses: actions/github-script@v6
        if: github.event_name == 'pull_request'
        with:
          script: |
            const fs = require('fs');
            const report = JSON.parse(fs.readFileSync('cost-report.json', 'utf8'));

            const message = `### 📊 Cost Analysis

            Findings: ${report.summary.findings}
            Estimated Monthly Cost: $${report.summary.estimated_cost}

            \`\`\`
            ${report.findings.map(f => `- ${f.severity}: ${f.resource} - $${f.cost}/month`).join('\n')}
            \`\`\`

            [View full report](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})`;

            github.rest.issues.createComment({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: message
            });

Pattern 2: Scheduled Drift Detection

Monitor production infrastructure changes automatically:

# .github/workflows/drift-detection.yml
name: Infrastructure Drift Detection

on:
  schedule:
    # Run every day at 2 AM EST
    - cron: '0 2 * * *'
  workflow_dispatch:  # Allow manual trigger
    inputs:
      scan-id:
        description: 'Scan ID for identification'
        required: true
        type: text

permissions:
  issues: write

jobs:
  drift-scan:
    name: Check for Drift
    runs-on: ubuntu-latest
    outputs:
      scan-results: ${{ steps.scan.outputs.results }}
    steps:
      - uses: actions/checkout@v4

      - name: CloudSlash Drift Scan
        id: scan
        uses: cloudslash/scan@v1
        with:
          api-key: ${{ secrets.CLOUDSLASH_API_KEY }}
          config: cloudslash-config.yml
          mode: scheduled
          scan-id: ${{ github.event.inputs.scan-id || 'daily-drift' }}

      - name: Check for Drift
        run: |
          if [ "${{ steps.scan.outputs.results-drift-count }}" -gt 0 ]; then
            echo "⚠️ Drift detected: ${{ steps.scan.outputs.results-drift-count }} changes"
            exit 1
          fi
          echo "✅ No infrastructure drift detected"

      - name: Create Daily Report
        if: always()
        uses: actions/github-script@v6
        with:
          script: |
            const scanResult = '${{ steps.scan.outputs.results }}';
            const issueTitle = `📅 Daily Drift Report - ${new Date().toISOString().split('T')[0]}`;

            let body = `### 📅 Infrastructure Drift Report\n\n`;

            try {
              const data = JSON.parse(scanResult);
              body += `**Status**: ${data.summary.status}\n`;
              body += `**Drift Changes**: ${data.drift.changes.length}\n`;
              body += `**Cost Impact**: $${data.drift.costImpact}\n\n`;

              if (data.drift.changes.length > 0) {
                body += `#### Modified Resources:\n\n`;
                data.drift.changes.forEach(change => {
                  body += `- \`${change.resource}\` (${change.provider})\n`;
                  body += `  - Previous: ${JSON.stringify(change.previous)}\n`;
                  body += `  - Current: ${JSON.stringify(change.current)}\n\n`;
                });

                body += `🔗 [View full report](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})`;
              } else {
                body += `✅ **No drift detected** - Infrastructure is in sync with your IaC.`;
              }
            } catch (e) {
              body += `Error parsing scan results: ${e.message}`;
            }

            const result = await github.rest.issues.create({
              owner: context.repo.owner,
              repo: context.repo.repo,
              title: issueTitle,
              labels: ['ci', 'drift-detection', 'weekly'],
              body: body
            });

            console.log(`Created issue: ${result.data.number}`);

Pattern 3: Deployment Gates

Block deployments that fail security checks:

# .github/workflows/deployment-gates.yml
name: Deployment Security Gates

on:
  workflow_call:
    inputs:
      environment:
        required: true
        type: string
    outputs:
      scan-result:
        value: ${{ jobs.security-gate.outputs.result }}
    secrets:
      api-key:
        required: true

permissions:
  contents: read
  pull-requests: write

jobs:
  security-gate:
    name: Security Gate Check
    runs-on: ubuntu-latest
    outputs:
      result: ${{ steps.scan.outputs.passed }}
    steps:
      - uses: actions/checkout@v4

      - name: CloudSlash Security Check
        uses: cloudslash/scan@v1
        id: scan
        with:
          api-key: ${{ secrets.api-key }}
          config: cloudslash-config.yml
          mode: deployment-gate
          fail-on-errors: true
          fail-on-warnings: true

      - name: Verify Security
        run: |
          if [ "${{ steps.scan.outputs.passed }}" != "true" ]; then
            echo "❌ Security violations found"
            cat cloudslash-result.json
            exit 1
          fi
          echo "✅ Security checks passed"

Usage:

# In your deployment workflow
jobs:
  deploy:
    uses: ./.github/workflows/deployment-gates.yml
    secrets:
      api-key: ${{ secrets.CLOUDSLASH_API_KEY }}
    with:
      environment: production

Pattern 4: Monorepo Setup

Centralized scanning for multiple repositories:

# Root monorepo pipeline
# .github/workflows/multi-repo-scan.yml
name: Monorepo Infrastructure Scan

on:
  push:
    branches: [ main, develop ]
  pull_request:
    types: [opened, synchronize, reopened]

jobs:
  scan-all:
    name: Scan All Subrepos
    runs-on: ubuntu-latest
    strategy:
      matrix:
        repository: [frontend-api, backend-api, infrastructure, data-pipeline]
    steps:
      - uses: actions/checkout@v4

      - name: Checkout Subrepo
        uses: actions/checkout@v4
        with:
          repository: ${{ matrix.repository }}
          path: ${{ matrix.repository }}

      - name: CloudSlash Scan
        working-directory: ${{ matrix.repository }}
        uses: cloudslash/scan@v1
        with:
          api-key: ${{ secrets.CLOUDSLASH_API_KEY }}
          config: cloudslash.yml

      - name: Upload Results
        uses: actions/upload-artifact@v3
        with:
          name: scan-results-${{ matrix.repository }}
          path: ${{ matrix.repository }}/cloudslash-results.json

Pattern 5: Multi-Cloud Setup

Unified scanning across AWS, Azure, and GCP:

# .github/workflows/multi-cloud-scan.yml
name: Multi-Cloud Infrastructure Scan

on:
  push:
    branches: [ main ]
  pull_request:
    types: [opened, synchronize, reopened]

jobs:
  multi-cloud:
    name: Multi-Cloud Integration Test
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Deploy to AWS
        run: |
          echo "Deploying to AWS..."
          # Your deployment scripts

      - name: CloudSlash AWS Scan
        uses: cloudslash/scan@v1
        with:
          api-key: ${{ secrets.AWS_CLOUDSLASH_API_KEY }}
          config: aws-cloudslash.yml

      - name: Deploy to Azure
        run: |
          echo "Deploying to Azure..."
          # Your deployment scripts

      - name: CloudSlash Azure Scan
        uses: cloudslash/scan@v1
        with:
          api-key: ${{ secrets.AZURE_CLOUDSLASH_API_KEY }}
          config: azure-cloudslash.yml

      - name: Deploy to GCP
        run: |
          echo "Deploying to GCP..."
          # Your deployment scripts

      - name: CloudSlash GCP Scan
        uses: cloudslash/scan@v1
        with:
          api-key: ${{ secrets.GCP_CLOUDSLASH_API_KEY }}
          config: gcp-cloudslash.yml

      - name: Aggregate Results
        run: |
          echo "Aggregating multi-cloud reports..."
          # Combine results from all three providers

Security Best Practices

1. Use Workflow Secrets

Never hardcode credentials. Always use GitHub Secrets:

# ✅ DO THIS: Use secrets
- name: CloudSlash Scan
  uses: cloudslash/scan@v1
  with:
    api-key: ${{ secrets.CLOUDSLASH_API_KEY }}

# ❌ DON'T DO THIS: Hardcode credentials
- name: CloudSlash Scan
  uses: cloudslash/scan@v1
  with:
    api-key: "sk-1234567890"

2. Restrict Permissions

Limit workflow permissions to only what's necessary:

permissions:
  pull-requests: write    # Only required for PR comments
  contents: read         # Required for fetching code
  issues: write          # Required for creating reports
  discussions: write     # Required for posting updates

3. Use Dependabot for Updates

Update the CloudSlash action automatically:

# .github/driftDetection.yml
dependency:
  directories: []
  enabled: false

OR manually using dependabot.yml:

# .github/dependabot.yml
version: 2
updates:
  - package-ecosystem: "github-actions"
    directory: "/"
    schedule:
      interval: "weekly"
    open-pull-requests-limit: 10
    reviewers:
      - "your-team"
    labels:
      - "dependencies"
      - "github-actions"

4. Validate Inputs

Validate workflow inputs to prevent injection attacks:

jobs:
  validate:
    if: github.event_name == 'pull_request'
    runs-on: ubuntu-latest
    outputs:
      is-safe: ${{ steps.is-safe.outputs.result }}
    steps:
      - name: Validate Name
        id: is-safe
        run: |
          # Whitelist allowed environments
          if [[ "${{ github.event.inputs.environment }}" =~ ^(prod|staging|dev)$ ]]; then
            echo "result=true" >> $GITHUB_OUTPUT
          else
            echo "result=false" >> $GITHUB_OUTPUT
            echo "Invalid environment: ${{ github.event.inputs.environment }}"
            exit 1
          fi

5. Rate Limit Protection

Prevent GitHub rate limit issues:

jobs:
  scan:
    runs-on: ubuntu-latest
    outputs:
      results-qr: ${{ steps.qr.outputs.count }}
      results-pr: ${{ steps.pr.outputs.count }}
    steps:
      - uses: actions/github-script@v6
        id: check-rate
        with:
          script: |
            const {
              data: { remaining, limit }
            } = await github.rest.rateLimit.get();
            console.log(`Rate limit: ${remaining}/${limit}`);
            if (remaining < 50) {
              throw new Error('Rate limit approaching');
            }

Multi-Account Setup

Using AWS Organizations

jobs:
  org-scan:
    runs-on: ubuntu-latest
    steps:
      - uses: cloudslash/scan@v1
        with:
          api-key: ${{ secrets.API_KEY }}
          mode: pr
          config: org-config.yml

      # Use org-scan.yml for AWS Organizations scanning

org-config.yml:

version: 1.0
accounts:
  - provider: aws
    name: all-org-accounts
    accounts:
      - role_arn: "arn:aws:iam::111111111111:role/CloudSlashScanRole"
      - role_arn: "arn:aws:iam::222222222222:role/CloudSlashScanRole"
      # Scan all child accounts recursively
    recursive: true

security:
  compliance:
    enabled: true
    benchmark: cis

Using Cross-Account Roles

For complex AWS setups with multiple trust relationships:

# .github/workflows/cross-account-pr.yml
name: Cross-Account PR Scan

on:
  pull_request:
    types: [opened, synchronize, reopened]

env:
  # Shared assume-role policy for cross-account scanning
  AWS_ASSUME_ROLE: ${{ secrets.AWS_ASSUME_ROLE_ARN }}

jobs:
  scan:
    name: Cross-Account Security Scan
    runs-on: ubuntu-latest
    outputs:
      scan-timeout: ${{ steps.aws.config.outputs.timeout }}
      scan-duration: ${{ steps.aws.config.outputs.duration }}
    steps:
      - uses: cloudslash/scan@v1
        with:
          api-key: ${{ secrets.CLOUDSLASH_API_KEY }}
          config: config.yml
          assume-role-arn: ${{ env.AWS_ASSUME_ROLE }}

      - name: Configure AWS for Assume Role
        uses: aws-actions/configure-aws-credentials@v4
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          role-to-assume: ${{ env.AWS_ASSUME_ROLE }}
          aws-region: us-east-1

Notification Integration

Slack Integration

# .github/workflows/slack-notifications.yml
name: CloudSlash Slack Notifications

on:
  workflow_run:
    workflows: ["CloudSlash Scan"]
    types: [completed]
    branches: [ main, develop ]

jobs:
  notify:
    name: Post Results to Slack
    runs-on: ubuntu-latest
    if: github.event.workflow_run.conclusion == 'failure'
    steps:
      - uses: slackapi/slack-github-action@v1
        id: slack
        with:
          payload: |
            {
              "text": "CloudSlash Scan Failed",
              "blocks": [
                {
                  "type": "section",
                  "text": {
                    "type": "mrkdwn",
                    "text": "❌ <${{ github.event.workflow_run.html_url }}|CloudSlash Security Scan Failed>"
                  }
                },
                {
                  "type": "section",
                  "fields": [
                    {
                      "type": "mrkdwn",
                      "text": "*Repository:*\n${{ github.repository }}"
                    },
                    {
                      "type": "mrkdwn",
                      "text": "*Actor:*\n${{ github.event.sender.login }}"
                    },
                    {
                      "type": "mrkdwn",
                      "text": "*Time:*\n${{ github.event.workflow_run.created_at }}"
                    }
                  ]
                }
              ]
            }
        env:
          SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}

Email Notifications

# .github/workflows/email-notifications.yml
name: Email Summary Report

on:
  schedule:
    - cron: '0 9 * * 1'  # Mondays at 9 AM

env:
  REPORT_EMAIL: ${{ secrets.REPORT_EMAIL_RECIPIENTS }}

jobs:
  send-report:
    name: Send Weekly Report
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Generate Report
        run: |
          cloudslash report --format html --output report.html
          cloudslash report --format json --output report.json

      - name: Send Email Report
        uses: dawidd6/action-send-mail@v3
        with:
          server_address: smtp.gmail.com
          server_port: 465
          username: ${{ secrets.EMAIL_USERNAME }}
          password: ${{ secrets.EMAIL_PASSWORD }}
          subject: "Weekly Infrastructure Report - ${{ github.run_id }}"
          to: ${{ secrets.REPORT_EMAIL_RECIPIENTS }}
          from: "CloudSlash Bot"
          body: "See attached HTML report for infrastructure security and cost analysis."
          attachments: |
            report.html
            report.json

Webhook Integration

# .github/workflows/webhook-notifications.yml
name: Webhook Notifications

on:
  workflow_run:
    workflows: ["CloudSlash Scan"]
    types: [completed]

jobs:
  webhook:
    name: Send to Webhook Endpoint
    runs-on: ubuntu-latest
    if: github.event.workflow_run.conclusion == 'failure'
    steps:
      - name: Fetch Scan Results
        id: results
        run: |
          # Fetch results from previous workflow run
          gh api repos/${{ github.repository }}/actions/artifacts's name:'scan-results'&ref=${{ github.event.workflow_run.head_commit.id }}
          # Process and send to webhook

Troubleshooting

Common Issues

Issue 1: "API Key Invalid" Error

Symptom: Scan fails with authentication error

Solution:

# Verify your API key in CloudSlash dashboard
# Check that you're using the correct secret name
# Ensure the API key is not expired

# Test locally first
cloudslash scan --api-key "$CLOUDSLASH_API_KEY" --config cloudslash.yml

Issue 2: Rate Limit Exceeded

Symptom: "GitHub rate limit exceeded" error

Solution:

# Add retry logic
- name: Retry on Rate Limit
  uses: nick-fields/retry-action@v2
  with:
    timeout_minutes: 10
    max_attempts: 3
    retry_wait_seconds: 120
    command: |
      cloudslash scan --config cloudslash.yml

Issue 3: Scan Takes Too Long (> 30 minutes)

Symptom: Workflow times out before scan completes

Solutions:

  1. Parallel scanning: Split by region/account

    jobs:
      scan-east:
        runs-on: ubuntu-latest
        steps:
          - uses: cloudslash/scan@v1
            with:
              config: config.yml
              regions: us-east-1
    
      scan-west:
        runs-on: ubuntu-latest
        steps:
          - uses: cloudslash/scan@v1
            with:
              config: config.yml
              regions: us-west-2
    

  2. Use differential scanning: Only scan changed resources

    - name: Differential Scan
      env:
        CHANGES: $(git diff --name-only main)
      run: |
        cloudslash scan --config cloudslash.yml --changed $CHANGES
    

Issue 4: PR Comments Not Appearing

Symptom: Expected PR comment doesn't show up

Steps:

# 1. Verify write permissions
permissions:
  pull-requests: write

# 2. Add debug output
- name: Debug PR Info
  run: |
    echo "PR Number: ${{ github.event.pull_request.number }}"
    echo "Repository: ${{ github.repository }}"
    echo "Viewer: ${{ github.actor }}"

# 3. Verify script runs
- name: Create Comment
  uses: actions/github-script@v6
  with:
    script: |
      console.log("Attempting to create comment...");
      await github.rest.issues.createComment({
        issue_number: context.issue.number,
        owner: context.repo.owner,
        repo: context.repo.repo,
        body: "Test comment"
      });

Issue 5: Secrets Not Available

Symptom: "undefined environment variable" error

Solution:

# 1. Check secret name matches exactly (case-sensitive)
# 2. Verify you're on a fork (forks have secret limitations)
# 3. Re-create the secret
#    Settings → Secrets and variables → Actions → New repository secret

Debug Mode

Enable debug logging:

- name: CloudSlash Scan (Debug)
  env:
    CLOUDSLASH_DEBUG: "true"
  uses: cloudslash/scan@v1
  with:
    api-key: ${{ secrets.API_KEY }}
    config: cloudslash.yml

Verbose Output

Get detailed scan information:

# Run with verbose flags
cloudslash scan \
  --config cloudslash.yml \
  --mode pr \
  --verbose \
  --debug

# Output goes to terminal and GitHub Actions logs

Feature Comparison Matrix

GitHub Actions 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 of Build Minutes ⚡ Free (public repos) ⚡ Free (public repos) ⭐ Free (small repos) ⭐ Limited ⚡ Free (self-hosted)
Integrations ⭐⭐⭐⭐⭐ (AWS, Azure, GCP) ⭐⭐⭐⭐ ⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐⭐⭐

Best Overall: GitHub Actions (Ease of use, free for public repos)


Click for detailed feature descriptions ### GitHub Actions Strengths - ✅ **GitHub-native**: Seamless integration with GitHub UI - ✅ **Docker containers**: Built-in container support - ✅ **Reusable workflows**: Share workflows across repositories - ✅ **Matrix jobs**: Parallel execution for multi-region/scenario scanning - ✅ **Marketplace**: Thousands of community actions - ✅ **Free for public** and brand-name private repos ### GitHub Actions Weaknesses - ❌ **CI minutes limits**: Paid plans have build minutes - ❌ **No self-hosted runners** for free (requires premium) - ❌ **Complex artifacts**: Managing large reports can be challenging

Common Patterns

Pattern 1: DAG-based Scanning

Sequence of dependent scans (security → cost → compliance):

# .github/workflows/scan-chain.yml
name: Infrastructure Scan Chain

on:
  pull_request: [opened, synchronize]

jobs:
  security-baseline:
    name: Security Baseline
    runs-on: ubuntu-latest
    steps:
      - uses: cloudslash/scan@v1
        with:
          config: config.yml
          mode: pr
        id: security

  cost-analysis:
    name: Cost Analysis
    runs-on: ubuntu-latest
    needs: security-baseline
    if: always()
    steps:
      - uses: cloudslash/scan@v1
        with:
          config: config.yml
          mode: cost

  compliance-check:
    name: Compliance Check
    runs-on: ubuntu-latest
    needs: [security-baseline]
    if: always() && steps.security.outputs.passed == 'true'
    steps:
      - uses: cloudslash/scan@v1
        with:
          config: config.yml
          mode: compliance

Pattern 2: Tag-based Scanning

Conditional scanning based on Git tags:

# .github/workflows/tag-scan.yml
name: Tag-based Infrastructure Scan

on:
  push:
    tags:
      - 'v*'

jobs:
  production-scan:
    name: Production Scan
    if: contains(github.ref, 'prod')
    runs-on: ubuntu-latest
    steps:
      - uses: cloudslash/scan@v1
        with:
          config: config.yml
          mode: deployment-gate

  staging-scan:
    name: Staging Scan
    if: contains(github.ref, 'stage')
    runs-on: ubuntu-latest
    steps:
      - uses: cloudslash/scan@v1
        with:
          config: config.yml
          mode: pr

Pattern 3: Environment-specific Scanning

Different scanning depth for production vs development:

jobs:
  dev-scan:
    name: Developer Scan
    if: github.ref == 'refs/heads/develop'
    steps:
      - uses: cloudslash/scan@v1
        with:
          config: minimal-config.yml  # Limited account scanning

  prod-scan:
    name: Production Scan
    if: github.ref == 'refs/heads/main'
    steps:
      - uses: cloudslash/scan@v1
        with:
          config: full-config.yml  # Full account scanning

Best Practices

1. Use Artifact Storage

Store large scan results:

- name: Upload Scan Results
  uses: actions/upload-artifact@v3
  with:
    name: scan-results-${{ github.run_id }}
    path: cloudslash-results.json
    retention-days: 30

- name: Download Report for Comment
  uses: dawidd6/action-download-artifact@v2
  with:
    workflow: cloudslash.yml
    name: scan-results-${{ github.run_id }}
    path: artifacts

2. Monitor Job Duration

Alert if scans take too long:

- name: Check Scan Duration
  id: time
  run: |
    START=$(date +%s)
    echo "Starting at $(date -u -d @$START)"

    # CloudSlash scan runs here...

    END=$(date +%s)
    DURATION=$((END - START))

    echo "Completed at $(date -u -d @$END)"
    echo "Duration: ${DURATION}s"

    # Alert if scan exceeds 30 minutes
    if [ $DURATION -gt 1800 ]; then
      echo "⚠️ Scan exceeded 30 minutes"
      exit 1
    fi

    echo "duration=${DURATION}" >> $GITHUB_OUTPUT

3. Use Composite Actions

Create reusable composite actions for repeated patterns:

# .github/actions/cloudslash-scan/action.yml
name: CloudSlash Scan
description: Run CloudSlash security and cost scanning
inputs:
  api-key:
    description: CloudSlash API key
    required: true
  config:
    description: CloudSlash config file
    required: true
    default: cloudslash.yml
  mode:
    description: Scan mode (pr, scheduled, deployment-gate)
    required: true
outputs:
  passed:
    description: Whether scan passed
    value: ${{ steps.scan.outputs.passed }}
runs:
  using: composite
  steps:
    - name: CloudSlash Scan
      run: |
        cloudslash scan \
          --api-key "${{ inputs.api-key }}" \
          --config "${{ inputs.config }}" \
          --mode "${{ inputs.mode }}"
      shell: bash

Usage:

- uses: ./.github/actions/cloudslash-scan
  with:
    api-key: ${{ secrets.API_KEY }}
    mode: pr

4. Cache Configuration

Cache CloudSlash config and dependencies:

- name: Cache CloudSlash
  uses: actions/cache@v3
  with:
    path: ~/.cache/cloudslash
    key: ${{ runner.os }}-cloudslash-${{ hashFiles('**/cloudslash.yml') }}

FAQ

Can I use CloudSlash with forked repositories? Yes, but with some limitations: - Secrets in forks are not automatically inherited - You'll need to re-enter secrets in fork - Fork runs use GitHub's extended permissions - Scheduled workflows don't work on forks See: [Fork Documentation](https://docs.github.com/en/actions/using-workflows/authenticating-with-the-github_token)
How do I handle multi-region deployments? Use matrix jobs to scan multiple regions in parallel:
jobs:
  multi-region-scan:
    strategy:
      matrix:
        region: [us-east-1, us-west-2, eu-central-1]
    steps:
      - run: cloudslash scan --regions ${{ matrix.region }}
Can I limit the number of accounts scanned? Yes, use filters in your config:
# Filter by account names
accounts:
  - provider: aws
    name: my-accounts
    accounts:
      - id: "111111111111"     # Scan only this account
Or use environment-based configuration for different branches/commits.

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