Scan your AWS and Azure infrastructure for cost optimization opportunities using Cloud Custodian. Run scans directly in GitHub Actions - no infrastructure required from your side.
- 🔍 Safe Scanning - 100% read-only operations, no changes to your infrastructure
- ☁️ Multi-Cloud - Supports Azure and AWS
- 🚀 Zero Config - Works out-of-the-box with sensible defaults
- 🔐 Secure - Uses OIDC for authentication, no secrets storage needed
- 📊 Actionable - Findings submitted to LeftSize dashboard for tracking
- 🎯 Customizable - Filter policies, add custom rules
- Install the LeftSize GitHub App
- Complete onboarding to get your
installation-idandrepository-token - Configure cloud provider authentication (Azure or AWS)
name: LeftSize Cost Optimization Scanon:
schedule:
- cron: '0 0 * * *'# Daily at midnightworkflow_dispatch: # Manual triggerpermissions:
id-token: write # Required for OIDCcontents: readjobs:
leftsize-scan:
runs-on: ubuntu-lateststeps:
- name: Azure Login (OIDC)uses: azure/login@v1with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}tenant-id: ${{ secrets.AZURE_TENANT_ID }}subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
- name: Run LeftSize Scanuses: leftsize/leftsize-action@v1with:
installation-id: ${{ secrets.LEFTSIZE_INSTALLATION_ID }}repository-token: ${{ secrets.LEFTSIZE_REPOSITORY_TOKEN }}permissions:
id-token: write # Required for OIDCcontents: readjobs:
leftsize-scan:
runs-on: ubuntu-lateststeps:
- name: AWS Login (OIDC)uses: aws-actions/configure-aws-credentials@v4with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}aws-region: us-east-1
- name: Run LeftSize Scanuses: leftsize/leftsize-action@v1with:
installation-id: ${{ secrets.LEFTSIZE_INSTALLATION_ID }}repository-token: ${{ secrets.LEFTSIZE_REPOSITORY_TOKEN }}cloud-provider: aws| Input | Required | Default | Description |
|---|---|---|---|
installation-id | ✅ Yes | - | GitHub App installation ID from LeftSize onboarding |
repository-token | ✅ Yes | - | Secure repository token from LeftSize onboarding |
backend-url | No | https://api.leftsize.com | LeftSize backend API URL |
cloud-provider | No | azure | Cloud provider: azure or aws |
azure-subscription-ids | No | All accessible | Comma-separated Azure subscription IDs to scan |
aws-regions | No | All accessible | Comma-separated AWS regions to scan |
include-policies | No | All | Policy categories: cost-optimization, governance, security |
exclude-policies | No | None | Specific policy names to skip (comma-separated) |
verbose | No | false | Enable verbose logging |
| Output | Description |
|---|---|
findings-count | Total number of findings detected |
findings-submitted | Whether findings were successfully submitted (true/false) |
findings-json | JSON string of all findings for custom processing |
permissions:
id-token: write # Required for OIDCcontents: readjobs:
leftsize-scan:
runs-on: ubuntu-lateststeps:
- uses: leftsize/leftsize-action@v1with:
installation-id: ${{ secrets.LEFTSIZE_INSTALLATION_ID }}repository-token: ${{ secrets.LEFTSIZE_REPOSITORY_TOKEN }}azure-subscription-ids: "sub-id-1,sub-id-2,sub-id-3"permissions:
id-token: write # Required for OIDCcontents: readjobs:
leftsize-scan:
runs-on: ubuntu-lateststeps:
- uses: leftsize/leftsize-action@v1with:
installation-id: ${{ secrets.LEFTSIZE_INSTALLATION_ID }}repository-token: ${{ secrets.LEFTSIZE_REPOSITORY_TOKEN }}cloud-provider: awsaws-regions: "us-east-1,eu-west-1,ap-southeast-1"permissions:
id-token: write # Required for OIDCcontents: readjobs:
leftsize-scan:
runs-on: ubuntu-lateststeps:
- uses: leftsize/leftsize-action@v1with:
installation-id: ${{ secrets.LEFTSIZE_INSTALLATION_ID }}repository-token: ${{ secrets.LEFTSIZE_REPOSITORY_TOKEN }}include-policies: cost-optimization,governance # Only cost and governance policiesexclude-policies: leftsize-idle-vm # Skip specific rulepermissions:
id-token: write # Required for OIDCcontents: readjobs:
leftsize-scan:
runs-on: ubuntu-lateststeps:
- name: Run LeftSize Scanid: leftsizeuses: leftsize/leftsize-action@v1with:
installation-id: ${{ secrets.LEFTSIZE_INSTALLATION_ID }}repository-token: ${{ secrets.LEFTSIZE_REPOSITORY_TOKEN }}
- name: Process Findingsrun: | echo "Found ${{ steps.leftsize.outputs.findings-count }} findings" echo "Submitted: ${{ steps.leftsize.outputs.findings-submitted }}" # Custom processing echo '${{ steps.leftsize.outputs.findings-json }}' | jq '.[] | select(.severity == "high")'# Create app
az ad app create --display-name "LeftSize-GitHub-Action"# Get app ID
APP_ID=$(az ad app list --display-name "LeftSize-GitHub-Action" --query "[0].appId" -o tsv)# Create service principal
az ad sp create --id $APP_ID# Get object ID
OBJECT_ID=$(az ad sp list --display-name "LeftSize-GitHub-Action" --query "[0].id" -o tsv)# Create credential configuration
cat > credential.json <<EOF{ "name": "github-actions", "issuer": "https://token.actions.githubusercontent.com", "subject": "repo:YOUR-ORG/YOUR-REPO:ref:refs/heads/main", "audiences": ["api://AzureADTokenExchange"]}EOF# Add federated credential
az ad app federated-credential create --id $APP_ID --parameters credential.json# Assign Reader role (minimum required)
az role assignment create \
--assignee $OBJECT_ID \
--role "Reader" \
--scope /subscriptions/YOUR-SUBSCRIPTION-ID
# Assign Monitoring Reader (for metrics)
az role assignment create \
--assignee $OBJECT_ID \
--role "Monitoring Reader" \
--scope /subscriptions/YOUR-SUBSCRIPTION-IDAdd these secrets to your repository:
AZURE_CLIENT_ID: App ID from step 1AZURE_TENANT_ID: Your Azure AD tenant IDAZURE_SUBSCRIPTION_ID: Your subscription IDLEFTSIZE_INSTALLATION_ID: From LeftSize onboardingLEFTSIZE_REPOSITORY_TOKEN: From LeftSize onboarding
aws iam create-open-id-connect-provider \
--url https://token.actions.githubusercontent.com \
--client-id-list sts.amazonaws.com \
--thumbprint-list 6938fd4d98bab03faadb97b34396831e3780aea1{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::ACCOUNT-ID:oidc-provider/token.actions.githubusercontent.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com",
"token.actions.githubusercontent.com:sub": "repo:YOUR-ORG/YOUR-REPO:ref:refs/heads/main"
}
}
}
]
}aws iam attach-role-policy \
--role-name LeftSize-GitHub-Action \
--policy-arn arn:aws:iam::aws:policy/ReadOnlyAccessAWS_ROLE_ARN: ARN of the role created aboveLEFTSIZE_INSTALLATION_ID: From LeftSize onboardingLEFTSIZE_REPOSITORY_TOKEN: From LeftSize onboarding
The action includes curated Cloud Custodian policies:
leftsize-idle-vm- Idle virtual machinesleftsize-unattached-disk- Unattached managed disksleftsize-unused-public-ip- Unused public IP addressesleftsize-idle-app-service-plan- Underutilized App Service Plansleftsize-idle-app-service-plan-no-apps- Empty App Service Plansleftsize-inactive-azure-load-balancer- Inactive load balancers
leftsize-idle-ecs-instances- Idle ECS container instancesleftsize-unattached-ebs-volumes- Unattached EBS volumesleftsize-unused-elastic-ips- Unused Elastic IPsleftsize-old-snapshots- Old EBS snapshots- And more...
- Security group rules
- Unencrypted resources
- Missing tags
- And more...
ubuntu-latest, ubuntu-20.04, or ubuntu-22.04). It does not work on Windows or macOS runners due to Docker-based action limitations.
jobs:
leftsize-scan:
runs-on: ubuntu-latest # RequiredLeftSize runs on your GitHub Actions runners, not our infrastructure:
- Free tier: 2000 minutes/month (GitHub Free)
- Typical scan: 3-5 minutes
- Result: ~400 scans/month on free tier
For larger organizations, use self-hosted runners for unlimited usage.
- ✅ No Cloud Credentials Stored - Uses OIDC (keyless authentication)
- ✅ Read-Only Operations - All policies are 100% safe, no destructive actions
- ✅ Multi-Tenant Secure - Repository tokens prevent cross-tenant attacks
- ✅ Minimal Permissions - Only requires Reader and Monitoring Reader roles
Azure:
# Test authentication locally
az login
az account show
# Verify service principal
az ad sp show --id $AZURE_CLIENT_IDAWS:
# Test authentication locally
aws sts get-caller-identity
# Verify role trust policy
aws iam get-role --role-name LeftSize-GitHub-ActionThis is normal! It means your infrastructure is well-optimized. The action will still submit a report with zero findings.
Check:
LEFTSIZE_INSTALLATION_IDis correctLEFTSIZE_REPOSITORY_TOKENmatches your repository- Backend URL is reachable (
https://api.leftsize.com)
Findings are saved as artifacts even if submission fails.
MIT License - See LICENSE file for details.
Contributions welcome! Please read CONTRIBUTING.md for guidelines.
Made with ❤️ by the LeftSize team