Skip to main content
Version: v5.7.0

The AWS pack (packs:aws)

packs:aws

The packs:aws entitlement gates opschain-pack-aws, the signed commercial content pack for Amazon Web Services: an enterprise aws:* action surface built on the official AWS SDK (aws-sdk-go-v2) via the pack's own awsu tool — never a wrapper around the vendor CLI. The pack's signed manifest names requires: packs:aws, so the entitlement in your licence must spell exactly that; like every packs:<name> grant it is validated by shape and minted per customer (see entitlements).

The pack mounts like any other action pack — a packs: entry in the template's .opschain/packs.yaml naming the pack remote and a pinned revision (opschain docs getting-started/2-4-using-packs) — or installs into the pack store as a signed bundle. Both doors enforce the manifest: installing a signed bundle verifies its signature and its required entitlement, and running any aws:* action without packs:aws refuses with the named pack refusal before anything dials AWS.

The shipped actions​

  • aws:api — dial ANY operation of any allowlisted service (cloudformation, ec2, eks, elbv2, iam, lambda, rds, route53, s3, sts): service, operation, params (a JSON object keyed by the operation's input fields), optional assume_role_arn/external_id, auto-drained pagination (paginate, max_pages), waiters (wait, wait_timeout), streaming or over-budget results landed as workspace files, and a uniform exit-code taxonomy (usage, validation, not-found, throttled, access-denied).
  • aws:<service>:<operation> — one generated typed action per SDK operation, 1,600+ across the ten services, with inputs derived from the operation's input struct and required-ness from the SDK's own validators; aws:<service>:wait_<waiter> covers every SDK waiter. The full generated table, including every excluded operation with its reason, ships as CATALOG.md inside the pack.
  • aws:whoami — resolve the caller identity the handed-over credentials dial as (account, arn, user_id). Run it first: a broken credential hand-over fails here, loudly, before anything mutates.
  • aws:ec2_run_instance — provision one instance (image and instance type; optional subnet, key pair, security groups, tags, cloud-init user data). Waits until running by default and publishes instance_id, state, addresses — and host_target (ssh_user@address), ready to hand to the infrastructure pack's host_register so the new machine becomes a governed infrastructure host node.
  • aws:ec2_instance_state — start / stop / reboot / terminate with a settled-state wait by default; aws:ec2_describe_instance — the read half: state, addresses, placement and tags as typed outputs.
  • aws:s3_put / aws:s3_get — stage one file out to, or fetch one object into, the step workspace; etag, size and path published.
  • aws:ensure — pre-warm the pinned awsu binary (PATH, the runner cache, the pack's own bundled tools/ directory, or a sha256-verified download from a configured mirror; a public URL is never a default). A pack installed from its signed bundle carries the tool, so a fresh runner needs no toolchain and no download configuration. Never a precondition: every other action's steps resolve awsu the same way themselves — the platform dispatches step by step, so sibling steps of one action may execute on different runners of a group, and a step never trusts a path a sibling published. With no channel available a step refuses by name, pointing at aws:ensure and the signed bundle.
  • aws:cli — gated escape hatch: one AWS CLI command on a runner that already carries the aws binary, for genuine gaps the native actions do not cover yet. The step log announces the lane.

Worked examples​

Each example is a complete declarative action file (.opschain/actions/<name>.yaml) composing the pack's actions with uses: + with: (opschain docs orchestration). Nothing secret appears in any of them — credential custody is server-side (see the next section) — and region is omitted wherever the node's aws.region property should carry it.

Prove the identity first (aws:whoami)​

Open anything that mutates with the liveness probe: a broken hand-over fails here, loudly, and the identity lands as ordinary step outputs.

# .opschain/actions/preflight.yaml
action: preflight
description: Prove the credential hand-over before anything mutates
steps:
- name: identity
uses: aws:whoami
- name: announce
run: echo "dialling account ${{ steps.identity.outputs.account }} as ${{ steps.identity.outputs.arn }}"

Provision and hand over (aws:ec2_run_instance)​

The instance settles to running by default, and the published host_target (ssh_user@address) seeds the infrastructure pack's membership hand-over, so the new machine lands as a governed host node.

# .opschain/actions/provision_web.yaml
action: provision_web
description: Provision a web instance and register it as a governed host
steps:
- name: provision
uses: aws:ec2_run_instance
with:
image_id: ami-0abcdef1234567890
instance_type: t3.micro
name: web-01
tags: service=web,env=dev
- name: register
uses: infrastructure:host_register
with:
parent: /projects/acme/environments/dev
code: web_01
address: '${{ steps.provision.outputs.private_ip }}'
properties: '{"rpc":{"target":"${{ steps.provision.outputs.host_target }}"}}'

Lifecycle verbs (aws:ec2_instance_state)​

One verb per call — start, stop, reboot or terminate — settled to the verb's target state before the step succeeds.

# .opschain/actions/stop_web.yaml
action: stop_web
description: Stop an instance and settle to stopped
inputs:
instance_id: { type: string, required: true }
steps:
- name: stop
uses: aws:ec2_instance_state
with: { instance_id: '${{ inputs.instance_id }}', state: stop }

Stage artefacts (aws:s3_put / aws:s3_get)​

s3_put ships a workspace file out, s3_get pulls an object in, and both publish typed outputs for later steps to consume.

# .opschain/actions/stage_release.yaml
action: stage_release
description: Ship the built artefact out and pull the live manifest in
steps:
- name: build
run: tar -czf release.tar.gz build/
- name: ship
uses: aws:s3_put
with: { bucket: acme-releases, key: web/release.tar.gz, file: release.tar.gz, sse: AES256 }
- name: manifest
uses: aws:s3_get
with: { bucket: acme-releases, key: web/manifest.json, file: manifest.json }
- name: record
run: echo "shipped etag ${{ steps.ship.outputs.etag }}; manifest at ${{ steps.manifest.outputs.path }}"

The full-coverage lane (aws:api)​

Any operation of an allowlisted service: params is a JSON object keyed by the operation's input fields, pagination auto-drains up to max_pages, and each top-level response field publishes as a snake_case step output (volumes here, compact JSON).

# .opschain/actions/audit_volumes.yaml
action: audit_volumes
description: List every unencrypted EBS volume in the region
steps:
- name: volumes
uses: aws:api
with:
service: ec2
operation: DescribeVolumes
params: '{"Filters":[{"Name":"encrypted","Values":["false"]}]}'
max_pages: "5"

The same lane drives every SDK waiter: name the waiter in wait: instead of an operation: and the step parks until the condition holds, or fails when wait_timeout passes.

# .opschain/actions/await_running.yaml
action: await_running
description: Park until the instance settles running
inputs:
instance_id: { type: string, required: true }
steps:
- name: until_running
uses: aws:api
with:
service: ec2
wait: instance_running
params: '{"InstanceIds":["${{ inputs.instance_id }}"]}'
wait_timeout: 15m

A generated typed action (aws:s3:list_objects_v2)​

Every generated action is the same lane with the operation's input struct as typed inputs — here bucket is required and prefix optional, exactly as the SDK validates them; the drained pages merge into one contents output.

# .opschain/actions/list_releases.yaml
action: list_releases
description: List the staged release objects
steps:
- name: listing
uses: aws:s3:list_objects_v2
with:
bucket: acme-releases
prefix: web/
max_pages: "3"

Pre-warm the tool (aws:ensure)​

Every other aws:* action's steps resolve awsu the same way themselves (a step may land on any runner of the group); call it directly to pre-warm a runner. For source: auto the channels resolve in order — an awsu already on PATH, the runner cache, the pack's own bundled tools/ directory (a pack installed from its signed bundle carries the release matrix, digest-checked against the pack's pin before install), then a configured mirror URL. Every install verifies against the pack-pinned sha256, and a public URL is never a default.

# .opschain/actions/prewarm.yaml
action: prewarm
description: Pre-warm the pinned awsu binary from the site mirror
steps:
- name: ensure
uses: aws:ensure
with:
source: url
url: https://mirror.example.internal/awsu
- name: report
run: echo "awsu ${{ steps.ensure.outputs.awsu_version }} ready at ${{ steps.ensure.outputs.awsu_path }}"

The gated escape hatch (aws:cli)​

For a genuine preview-surface gap only — here a service outside the aws:api allowlist. It drives an aws binary the runner image already carries (the pack never installs one), announces the lane in the step log, and publishes exit_code plus a size-capped stdout. Prefer a native action: a gap worth this lane is a gap worth reporting.

# .opschain/actions/preview_gap.yaml
action: preview_gap
description: Reach a surface the native actions do not cover yet
steps:
- name: preview
uses: aws:cli
with:
command: sqs list-queues
- name: record
run: echo "lane exited ${{ steps.preview.outputs.exit_code }}"

Credentials custody​

Nothing secret is ever an action input, node property or argv. Credentials are custodied as vault references; the platform resolves them and hands the values to the runner as masked environment variables, or the runner carries an ambient role (instance profile / IRSA) and no variables at all. Each dial logs one value-free line naming which credential channels were present (kind and length only), and an unresolved vault reference refuses the dial by name — it is never echoed. Every action resolves region from the node's aws.region property unless overridden in with: (falling back to the SDK's ambient resolution).

Entitlement and lapse behaviour​

The signed-pack gate, uniformly: a LimePoint-signed pack whose verified manifest requires packs:aws is gated on that feature at install and at every action run — the refusal names the pack, the mount and the missing entitlement, and lands a customer-visible licence warning event. The grant is layered by a pack-tier licence document beside your base licence and stops contributing once that document expires past its grace window: installed content and stored configuration are never removed, reads stay up, and everything lights back up when a renewed licence is applied.

Composition with other packs​

  • The infrastructure pack closes the loop: aws:ec2_run_instance publishes host_target, which infrastructure:host_register (packs:infrastructure) turns into a registered, governed infrastructure host node — provision on AWS, then operate over the brokered transport family.
  • The Azure sibling is the Azure pack (packs:azure): the same shape — a governed api lane, a generated typed catalog and provisioning hand-over — for Microsoft Azure.

Where the rest lives​

  • Entitlements — the pack vocabulary and refusal shapes.
  • The pack store — installing signed bundles.
  • The infrastructure pack — registering provisioned machines as governed hosts.
  • opschain docs getting-started/2-4-using-packs — mounting a pack onto a template.