Bring your own CI/CD

The default install plane, StrataCI, runs the deploy for you in an ephemeral, least-privilege CodeBuild runner inside your account. Many teams are happy with that. Others have an established CI/CD pipeline — with their own image scanning, policy-as-code, OpenTofu plan review, and change control — and want the install to flow through that instead.

StrataBI supports this. The licensed artifacts are designed to drop into your own pipeline: the runtime image is a standard container you can build and scan yourself, and the platform bundle is a standard OpenTofu module you can plan and apply yourself. This page covers the two ways to do it.

Which one do I need? If your requirement is "we build and scan every container image ourselves," use Option A — it is fully supported and turnkey, and StrataCI still runs the vetted platform Terraform. If your requirement is "we run all infrastructure changes through our own OpenTofu pipeline," use Option B, the self-managed path. The two compose: you can do both.

Here you own the container supply chain end to end, and StrataCI still applies the platform Terraform. This is the common case for teams whose control is primarily about image provenance and scanning.

1. Pull the entitled runtime into your build context

stratactl fetch-runtime is the licensing seam for images. It runs the same account-scoped, SigV4-signed entitlement check as an install (see Entitlements & token security), pulls the signed runtime wheel, verifies its hash and signature, and drops it into a Docker build context:

bash
stratactl --profile <profile> --region <region> fetch-runtime --version 1.7.1 --dir ./build
# -> ./build/dist/stratabi_runtime-1.7.1-py3-none-any.whl  (hash + signature verified)

No valid account entitlement means no artifact — the wheel never leaves StrataHQ without the entitlement check passing.

2. Build, scan, and push in your own pipeline

Build the image with your base image, your registry mirror, and your scanners in the loop, then push to your registry (ECR or otherwise). The provided build.sh honors standard --build-arg overrides so the same build works for public defaults or a private index:

bash
RUNTIME_WHEEL=dist/stratabi_runtime-1.7.1-py3-none-any.whl \
  BASE_IMAGE=<your-hardened-python-3.11-base> \
  PIP_INDEX_URL=<your-artifactory>/api/pypi/pypi/simple \
  IMAGE=<acct>.dkr.ecr.<region>.amazonaws.com/stratabi:1.7.1 \
  ./build.sh
# run your image scan here, then: docker push <acct>.dkr.ecr.<region>.amazonaws.com/stratabi:1.7.1

See Container image & supply chain for the full build-arg reference.

3. Point the install at your image

In the install wizard, set the Container image to your pushed image instead of leaving it blank. (Blank means "build it in-account"; a value means "use this image and skip the in-account build.")

bash
stratactl --profile <profile> --region <region> init
#   Container image (ECR URL): <acct>.dkr.ecr.<region>.amazonaws.com/stratabi:1.7.1
stratactl --profile <profile> --region <region> install-stratabi

The deploy now uses your scanned image; no privileged in-account image build runs. Everything else — the ALB/ECS/VPC Terraform — is applied by StrataCI as usual.


Option B — run the OpenTofu in your own pipeline (self-managed)

Here you run the platform's OpenTofu yourself, so every infrastructure change goes through your pipeline's plan review, policy checks, and approvals. You take on the state backend, the deploy role scoping, and the artifact verification that StrataCI would otherwise handle for you. This is an advanced path — reach for it when your change-control process requires that you run every tofu apply.

What StrataCI does for you that you now own. It verifies the bundle, configures an S3 state backend with a DynamoDB lock, assumes a module-scoped deploy role, enforces the account self-check, and records job status. In Option B those become your pipeline's responsibility. There is no turnkey --runner flag today; you drive OpenTofu directly.

This path starts from an environment with no StrataCI installed — no bootstrap, no CodeBuild. Two commands, entitlement resolve and fetch-bundle, retrieve the verified artifact directly; everything after that is your own pipeline.

1. Generate your variables

strata init collects configuration and writes OpenTofu variable files locally — these are yours to commit and review:

bash
stratactl --region <region> init          # writes ./infra/*.auto.tfvars

Keep the environment's network values in their own pinned file. The values that tie the deployment to your account's network and identity — name_prefix, region, vpc_id, public_subnet_ids, enable_private_subnets, private_subnet_ids — should not drift between upgrades. Changing any of them replaces the load balancer and the ECS security groups. Keep them in one file per environment, committed and reviewed like any other infrastructure change, and lay it down last so it always wins:

json
// envs/prod/zzz-env.auto.tfvars.json
{
  "name_prefix": "stratabi",
  "region": "us-east-1",
  "vpc_id": "vpc-0123456789abcdef0",
  "public_subnet_ids": ["subnet-aaaa", "subnet-bbbb"],
  "enable_private_subnets": false,
  "private_subnet_ids": []
}

This is the same file StrataCI keeps for you at config/env/<env>.auto.tfvars.json in its bucket. If you are moving a StrataCI-managed install to your own pipeline, stratactl env export --env <env> --out envs/<env>/zzz-env.auto.tfvars.json writes it for you.

2. Fetch and verify the bundle — directly, no StrataCI

stratactl fetch-bundle runs the account-scoped, SigV4-signed entitlement check, downloads the signed module.zip over the short-lived presigned URL, verifies its SHA-256 and its KMS signature against the pinned public key, and writes the verified bundle to the path you give it:

bash
stratactl --profile production --region us-east-1 \
  fetch-bundle --module stratabi --version 1.7.1 \
  --output ./artifacts/module.zip \
  --metadata ./artifacts/bundle.verification.json      # optional receipt (no presigned URL)

Verification is fail-closed: if the signature can't be verified against your pinned key, the command refuses and deletes the partial download rather than fall back to hash-only. That is the control guaranteeing the OpenTofu you are about to run is the OpenTofu Shaleio signed.

If your pipeline prefers to fetch the artifact itself, resolve just the metadata instead and hand the presigned URL to your downloader — then verify with your own tooling before use:

bash
stratactl entitlement resolve --module stratabi --version 1.7.1 --output ./entitlement.json
#   -> { "artifact_url": "...", "sha256": "...", "signature": "...", "version": "1.7.1", "expires_at": "..." }
The presigned URL is a short-lived bearer credential (default 15 minutes — this applies to the artifact URL, not your perpetual license). Treat entitlement.json as a secret: it is written with 0600 permissions and the URL is never printed to the terminal. Do not commit it; fetch close to when you deploy.

3. Plan and apply with your backend and role

Unzip the bundle and run OpenTofu against your state backend, with your own scanning and plan-review between plan and apply. The in-account runner lays the variables down in this order, and you should too (OpenTofu auto-loads *.auto.tfvars.json in lexical order, so later files win):

  1. the bundle's config/defaults.json as defaults.auto.tfvars.json
  2. your strata init values as zz-customer.auto.tfvars.json
  3. your environment file as zzz-env.auto.tfvars.json, so network and identity always come from it
bash
unzip ./artifacts/module.zip -d ./bundle
cp ./infra/*.auto.tfvars ./bundle/tofu/                     # your strata init values
cp ./envs/prod/zzz-env.auto.tfvars.json ./bundle/tofu/      # network + identity, loaded last
cd ./bundle/tofu

tofu init \
  -backend-config=bucket=<your-state-bucket> \
  -backend-config=key=<state-key> \
  -backend-config=region=<region> \
  -backend-config=dynamodb_table=<your-lock-table> \
  -backend-config=encrypt=true

tofu plan -out tfplan
tofu show -json tfplan > tfplan.json
stratactl plan-guard tfplan.json    # fails if protected infrastructure would be destroyed/replaced
# <-- your own policy-as-code / plan review / approval gate here
tofu apply tfplan

stratactl plan-guard is the same check the StrataCI runner runs before every apply. It fails the pipeline step if the plan would destroy or replace load-bearing resources — VPC-bound security groups, the load balancer, listeners and target groups, the ECS cluster and service, S3 buckets, DynamoDB tables, the Glue catalog, KMS keys, ECR, Cognito. Routine upgrade churn (task definitions, Lambda code, the image build, IAM policies, S3 objects) passes. It reads only the plan file — no AWS credentials, no StrataHQ contact — and works on terraform show -json output too. Pass --allow-replace for a change you have reviewed and intend.

Run apply under a role scoped to what the module needs. If you assume a broad role, narrow it with a session policy so the effective permissions are the intersection of the role and the module's declared scope — the same least-privilege property StrataCI gives you automatically.

4. Lifecycle: upgrade, rollback, teardown

On this path you own the full lifecycle through your own OpenTofu state backend and execution environment. Do not use stratactl update or stratactl uninstall for a self-managed deployment — those drive the StrataCI runner against the runner's state, not yours, and mixing the two can diverge or clobber state. Everything below is tofu against the backend you configured in step 3.

In-place upgrade (the default — no destroy-and-reinstall). Fetch the newly entitled bundle and apply it against your existing state. OpenTofu computes the delta; unchanged resources are left alone and persistent stores are preserved:

bash
stratactl fetch-bundle --module stratabi --version <new> --output ./artifacts/module.zip
unzip -o ./artifacts/module.zip -d ./bundle
cp ./infra/*.auto.tfvars ./bundle/tofu/
cp ./envs/prod/zzz-env.auto.tfvars.json ./bundle/tofu/   # same environment file as the install
cd ./bundle/tofu
tofu init -backend-config=...        # same backend as the install
tofu plan -out tfplan                # REVIEW the plan before applying — see below
tofu show -json tfplan > tfplan.json && stratactl plan-guard tfplan.json
tofu apply tfplan

Use the same environment file for every upgrade. If a plan wants to replace the ALB or the ECS security groups, the usual cause is a changed vpc_id or subnet list — check the environment file before reaching for --allow-replace.

Review the plan before every upgrade. Treat any destroy/replace on a stateful resource (S3 buckets, DynamoDB tables, Glue catalog, RDS if present) as a stop-and-check: those hold persistent data. A routine upgrade should show in-place updates and the ECS task/image rolling forward, not replacement of data stores. Do not apply a plan that would destroy a stateful resource unless you have explicitly decided to and have a backup — tofu plan is your gate, and the prevent_destroy lifecycle guards on the data stores are a backstop, not a substitute for review.

Compatibility & state migrations. Minor/point releases within a major version are designed for in-place upgrade. Before a major version jump, check the release notes for that version: any release that requires a schema/state migration — or that cannot be upgraded in place and needs a staged migration — is called out there explicitly. When a migration is required, follow the documented steps before apply; when a release is marked not-in-place-upgradeable, treat it as a migration project, not a routine apply.

Rollback. Because your state and the bundles are versioned, rolling back is the same in-place flow aimed at the previous version — re-fetch the prior bundle and apply:

bash
stratactl fetch-bundle --module stratabi --version <previous> --output ./artifacts/module.zip
# unzip / init / plan / apply as above

Roll back only across versions documented as in-place-compatible in both directions; if the forward upgrade performed an irreversible state migration, a plain re-apply of the old bundle is not a safe rollback — restore from backup per the release notes instead. Keep your OpenTofu state versioned (S3 bucket versioning on the backend) so you can recover a prior state file.

Teardown. tofu destroy with the same backend config. This is destructive and removes the data stores — take final backups first. There is no StrataHQ dependency: fetch-bundle already gave you the verified bundle, and destroy runs entirely from your state.

If you ever want to hand the platform back to StrataCI, a local-delivery install reconciles against the same state.

What you own on this path

Running OpenTofu yourself means you do not inherit the guarantees the StrataCI runner provides automatically. These become your pipeline's responsibility:

Shaleio still guarantees the artifact's authenticity (entitlement + signature); it does not guarantee the safety of a pipeline it doesn't run.


Choosing

Option A — own imageOption B — own OpenTofu
Container build & scanYouYou (or StrataCI builds in-account)
Platform Terraform applyStrataCI (vetted)You
State backend & lockingStrataCIYou
Deploy-role scopingStrataCI (automatic)You
Artifact signature verificationAutomaticYou (don't skip it)
EffortLowHigher

Most teams that say "we use our own CI/CD" mean image provenance and scanning — Option A covers that with no bespoke work. Option B is for organizations whose change control requires that every tofu apply run through their own pipeline.