Documentation

StackSage runs AWS audits in your environment (typically GitHub Actions) using a customer-controlled read-only role. It produces local artifacts you can share internally: a one-page summary, an HTML report, and machine-readable findings.

What StackSage does

  • Finds cost waste and concrete savings opportunities.
  • Flags security posture signals(IAM hygiene, exposure basics, audit logging baselines).
  • Makes findings explainable via evidence/provenance (e.g. measured vs heuristic vs skipped due to permissions).

Trial mode (self-serve)

StackSage Trial is designed to run without dependency on us: you can set it up in your own repo, run it on demand, and download artifacts. Trial uses a public GHCR image and does not require a license.

Trial is intentionally limited: it caps findings and does not compute exact savings.

What you need

  • A GitHub repo with Actions enabled
  • AWS credentials (owned by you) stored as GitHub secrets
  • A read-only IAM role ARN that the workflow can assume

Step 1 — Create the IAM role (AWS)

Create an IAM role in your AWS account and allow the GitHub runner to assume it (STS AssumeRole). Attach this minimal trial policy to that role:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "StackSageTrialReadOnly",
            "Effect": "Allow",
            "Action": [
                "sts:GetCallerIdentity",
                "ec2:DescribeRegions",
                "ec2:DescribeSecurityGroups",
                "ec2:DescribeAddresses",
                "ec2:DescribeVolumes",
                "iam:GetAccountSummary",
                "cloudtrail:DescribeTrails",
                "cloudtrail:GetTrailStatus",
                "s3control:GetPublicAccessBlock"
            ],
            "Resource": "*"
        }
    ]
}

Step 2 — Add GitHub secrets

In your repo: Settings → Secrets and variables → Actions

  • Required: AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY
  • Recommended: AWS_DEFAULT_REGION (example: us-east-1)
  • Required (unless you always provide it as a workflow input): CUSTOMER_ROLE_ARN
  • Optional: AWS_SESSION_TOKEN, CUSTOMER_EXTERNAL_ID

Step 3 — Add the workflow file

Create .github/workflows/stacksage_trial.yml with the workflow below. Run it from GitHub → Actions → “StackSage Audit (Trial)”.

name: StackSage Audit (Trial)

on:
    workflow_dispatch:
        inputs:
            customer_role_arn:
                description: "Customer role ARN to assume"
                required: false
                type: string
            regions:
                description: "Comma-separated AWS regions (optional; default: 1 region for trial)"
                required: false
                type: string
            log_level:
                description: "Log level (INFO/DEBUG)"
                required: false
                default: "INFO"
                type: choice
                options: ["INFO", "DEBUG"]

jobs:
    audit:
        runs-on: ubuntu-latest
        env:
            STACKSAGE_TRIAL_IMAGE: ghcr.io/amitdubey428/stacksage-trial:latest
            AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
            AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
            AWS_SESSION_TOKEN: ${{ secrets.AWS_SESSION_TOKEN }}
            AWS_DEFAULT_REGION: ${{ secrets.AWS_DEFAULT_REGION }}
            CUSTOMER_EXTERNAL_ID: ${{ secrets.CUSTOMER_EXTERNAL_ID }}

        steps:
            - name: Pull StackSage trial image
              run: docker pull "${STACKSAGE_TRIAL_IMAGE}"

            - name: Run audit (trial mode)
              shell: bash
              run: |
                  set -euo pipefail

                  : "${AWS_ACCESS_KEY_ID:?Missing secret AWS_ACCESS_KEY_ID}"
                  : "${AWS_SECRET_ACCESS_KEY:?Missing secret AWS_SECRET_ACCESS_KEY}"

                  if [[ -z "${AWS_DEFAULT_REGION:-}" ]]; then
                      export AWS_DEFAULT_REGION="us-east-1"
                  fi

                  ROLE_ARN="${{ inputs.customer_role_arn || secrets.CUSTOMER_ROLE_ARN }}"
                  : "${ROLE_ARN:?Missing role ARN. Set input customer_role_arn or repo secret CUSTOMER_ROLE_ARN}"

                  REGIONS_ARG=""
                  if [[ -n "${{ inputs.regions }}" ]]; then
                      REGIONS_ARG="--regions ${{ inputs.regions }}"
                  fi

                  EXT_ID_ARG=""
                  if [[ -n "${CUSTOMER_EXTERNAL_ID:-}" ]]; then
                      EXT_ID_ARG="--external-id ${CUSTOMER_EXTERNAL_ID}"
                  fi

                  mkdir -p reports

                  docker run --rm                       -e AWS_ACCESS_KEY_ID="${AWS_ACCESS_KEY_ID}"                       -e AWS_SECRET_ACCESS_KEY="${AWS_SECRET_ACCESS_KEY}"                       -e AWS_SESSION_TOKEN="${AWS_SESSION_TOKEN:-}"                       -e AWS_DEFAULT_REGION="${AWS_DEFAULT_REGION:-}"                       -v "$PWD":/work -w /app                       "${STACKSAGE_TRIAL_IMAGE}"                       bash -lc "python -m stacksage_trial.cli audit --role-arn ${ROLE_ARN} ${EXT_ID_ARG} ${REGIONS_ARG} --out /work/reports --log-level ${{ inputs.log_level }}"

            - name: Upload reports artifact
              uses: actions/upload-artifact@v4
              with:
                  name: stacksage-reports
                  path: reports/
                  if-no-files-found: error
              env:
                  AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
                  AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
                  AWS_SESSION_TOKEN: ${{ secrets.AWS_SESSION_TOKEN }}
                  AWS_DEFAULT_REGION: ${{ secrets.AWS_DEFAULT_REGION }}
                  CUSTOMER_EXTERNAL_ID: ${{ secrets.CUSTOMER_EXTERNAL_ID }}

Outputs

The workflow uploads an artifact named stacksage-reports containing:

  • audit_report.html — includes Security Findings + a limited Cost/Waste Preview
  • summary.md
  • findings.json / findings.csv
  • run_provenance.json

Troubleshooting

  • AccessDenied: ensure the trial policy is attached to the assumed role and the trust policy allows your GitHub principal.
  • No regions: set AWS_DEFAULT_REGIONor provide regions input.
  • Missing artifact: confirm the workflow step writes to reports/ before upload.

How it works (high level)

  1. Run StackSage on a schedule (GitHub Actions) or locally.
  2. Assume a customer-controlled read-only role (STS AssumeRole).
  3. Read resource metadata and (optional) aggregate utilization/spend totals.
  4. Generate local artifacts as workflow outputs under your control.

Outputs

  • summary.md — one-page account brief (top savings, security posture, skipped checks, next actions)
  • audit_report.html — full interactive report (financial findings vs security posture findings, plus complete appendix)
  • findings.json / findings.csv — machine-readable exports
  • remediation_plan.md / remediation_plan.json — prioritized fix plan with safe verification steps (paid runs)

See a real example at /demo-report.

Security posture signals (what we check)

StackSage includes baseline posture checks intended to surface obvious risks early and reduce audit blind spots. These checks are designed to be privacy-first: we prefer aggregate evidence and avoid raw sensitive policy contents.

  • IAM hygiene: root MFA, root keys, password policy baseline, access key rotation signals
  • Exposure basics: public security group ingress on sensitive ports, S3 public access block posture, RDS public/encryption/backup baselines
  • Audit logging: CloudTrail baseline, AWS Config recorder status, high-level enablement checks

Privacy & permissions

StackSage is privacy-first by default. It runs inside your environment and produces local artifacts; it does not require exporting AWS inventory to a hosted SaaS.

Read the detailed permissions matrix and opt-ins here: /privacy-access.

Support

If you get stuck, email hello@stacksageai.com.