Skip to content

CI/CD Pipelines

Query the LightPane API from your CI/CD pipeline to check infrastructure state before, during, or after a deployment. Use cases include:

  • Verify expected resources exist before deploying
  • Check that no CloudWatch alarms are firing before a release
  • Log the current infrastructure state as a deployment artifact
  • Block a deploy if infrastructure does not match expectations

Requirements:

  • A LightPane API key (csl_ak_ prefix) — create one here
  • The API key stored as a CI secret (not in code)

Store the token as a secret

Add the token as a repository secret named LIGHTPANE_API_KEY:

  1. Go to Settings > Secrets and variables > Actions
  2. Click New repository secret
  3. Name: LIGHTPANE_API_KEY
  4. Value: your csl_ak_ token

Add the token as a CI/CD variable:

  1. Go to Settings > CI/CD > Variables
  2. Click Add variable
  3. Key: LIGHTPANE_API_KEY
  4. Value: your csl_ak_ token
  5. Check Mask variable

Add the token as a credential:

  1. Go to Manage Jenkins > Credentials
  2. Add a Secret text credential
  3. ID: lightpane-api-key
  4. Secret: your csl_ak_ token

Check infrastructure before deploying

GitHub Actions

.github/workflows/deploy.yml
name: Deploy
on:
  push:
    branches: [main]

jobs:
  check-infra:
    runs-on: ubuntu-latest
    steps:
      - name: Check infrastructure state
        env:
          LIGHTPANE_API_KEY: ${{ secrets.LIGHTPANE_API_KEY }}
        run: |
          RESPONSE=$(curl -s -w "\n%{http_code}" \
            -X POST https://api.lightpane.cloud/services/discover \
            -H "Authorization: Bearer $LIGHTPANE_API_KEY" \
            -H "Content-Type: application/json" \
            -d '{
              "services": [
                {"service": "ec2", "provider": "aws", "region": "eu-west-2",
                 "attributes": ["name", "instance_id", "state"]}
              ]
            }')

          HTTP_CODE=$(echo "$RESPONSE" | tail -1)
          BODY=$(echo "$RESPONSE" | head -n -1)

          if [ "$HTTP_CODE" != "200" ]; then
            echo "LightPane API returned $HTTP_CODE"
            echo "$BODY"
            exit 1
          fi

          echo "$BODY" | jq .

  deploy:
    needs: check-infra
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Deploy application
        run: echo "Deploying..."

GitLab CI

.gitlab-ci.yml
check-infra:
  stage: pre-deploy
  image: curlimages/curl:latest
  script:
    - |
      RESPONSE=$(curl -s -w "\n%{http_code}" \
        -X POST https://api.lightpane.cloud/services/discover \
        -H "Authorization: Bearer $LIGHTPANE_API_KEY" \
        -H "Content-Type: application/json" \
        -d '{
          "services": [
            {"service": "ec2", "provider": "aws", "region": "eu-west-2",
             "attributes": ["name", "instance_id", "state"]}
          ]
        }')
      HTTP_CODE=$(echo "$RESPONSE" | tail -1)
      if [ "$HTTP_CODE" != "200" ]; then
        echo "Infrastructure check failed"
        exit 1
      fi

Check for active CloudWatch alarms

Block a deploy if any CloudWatch alarms are in ALARM state:

check_alarms.sh
#!/bin/bash
set -e

RESPONSE=$(curl -s \
  -X POST https://api.lightpane.cloud/services/discover \
  -H "Authorization: Bearer $LIGHTPANE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "services": [
      {"service": "cloudwatch_alarms", "provider": "aws", "region": "eu-west-2",
       "attributes": ["alarm_name", "state_value", "metric_name"]}
    ]
  }')

ALARM_COUNT=$(echo "$RESPONSE" | jq '[.results[0].rows[] | select(.state_value == "ALARM")] | length')

if [ "$ALARM_COUNT" -gt "0" ]; then
  echo "BLOCKED: $ALARM_COUNT CloudWatch alarms are firing"
  echo "$RESPONSE" | jq '.results[0].rows[] | select(.state_value == "ALARM")'
  exit 1
fi

echo "No active alarms. Proceeding with deploy."

Save infrastructure state as a build artifact

Capture the current state of your infrastructure and store it alongside the deployment for audit purposes.

.github/workflows/deploy.yml
      - name: Capture infrastructure state
        env:
          LIGHTPANE_API_KEY: ${{ secrets.LIGHTPANE_API_KEY }}
        run: |
          curl -s \
            -X POST https://api.lightpane.cloud/services/discover \
            -H "Authorization: Bearer $LIGHTPANE_API_KEY" \
            -H "Content-Type: application/json" \
            -d '{
              "services": [
                {"service": "ec2", "provider": "aws", "region": "eu-west-2"},
                {"service": "rds_instances", "provider": "aws", "region": "eu-west-2"},
                {"service": "lambda_functions", "provider": "aws", "region": "eu-west-2"}
              ]
            }' > infra-state.json

      - name: Upload artifact
        uses: actions/upload-artifact@v4
        with:
          name: infra-state-${{ github.sha }}
          path: infra-state.json

Python pre-deploy check

For more complex validation, use a Python script:

pre_deploy_check.py
import os
import sys
import requests

API_URL = "https://api.lightpane.cloud/services/discover"
TOKEN = os.environ["LIGHTPANE_API_KEY"]

# Define expected infrastructure
EXPECTED = {
    "ec2": {"min_running": 2},
    "rds_instances": {"min_count": 1},
}

response = requests.post(API_URL, json={
    "services": [
        {"service": "ec2", "provider": "aws", "region": "eu-west-2",
         "attributes": ["name", "instance_id", "state"]},
        {"service": "rds_instances", "provider": "aws", "region": "eu-west-2",
         "attributes": ["name", "engine", "status"]},
    ]
}, headers={
    "Authorization": f"Bearer {TOKEN}",
    "Content-Type": "application/json"
})

response.raise_for_status()
data = response.json()

failed = False
for result in data.get("results", []):
    service = result["metadata"]["service"]
    rows = result.get("rows", [])

    if service == "ec2":
        running = [r for r in rows if r.get("state") == "running"]
        if len(running) < EXPECTED["ec2"]["min_running"]:
            print(f"FAIL: Expected at least {EXPECTED['ec2']['min_running']} "
                  f"running EC2 instances, found {len(running)}")
            failed = True

    if service == "rds_instances":
        if len(rows) < EXPECTED["rds_instances"]["min_count"]:
            print(f"FAIL: Expected at least {EXPECTED['rds_instances']['min_count']} "
                  f"RDS instance(s), found {len(rows)}")
            failed = True

if failed:
    sys.exit(1)

print("All pre-deploy checks passed.")

Access key recommendations for CI/CD

Setting Recommendation
Type api_key
allowed_ips Set to your CI runner IP range if known
rate_limit_rpm 60 is sufficient for most pipelines
Services Scope to only the services your pipeline checks
Expiry Set to match your key rotation schedule (90 days or 1 year)

Do not commit tokens to version control

Store the token as a CI secret. If a token is accidentally committed, revoke it immediately in Access Keys and create a new one.