App & Context

Every Anvil program starts the same way, in every language.

TypeScript programs start with new App(). Python programs call anvil.App(run=...). Go programs call anvil.Run() with an AppConfig. In every case, a run callback receives a Context that carries the information every component needs to deploy correctly.

App configuration

Everything except run is optional. With no config, Anvil deploys to the stage's region from anvil.yaml using your environment's AWS credentials, and keeps the last 3 deploys' state.

import { App } from '@anvil-cloud/sdk';
import * as anvil from '@anvil-cloud/sdk';

export default new App({
  providers: {
    aws: { region: 'ap-southeast-2' },
    'aws.us': { region: 'us-east-1' },
  },
  defaults: {
    tags: { team: 'platform' },
    state: { retainVersions: 3, compressState: true },
  },
  compliance: { frameworks: ['soc2', 'cis'], schedule: 'daily' },
  run(ctx) {
    new anvil.aws.Bucket('logs', { /* ... */ });
    new anvil.aws.Bucket('logs-us', { /* ... */ }, { provider: ctx.providers['aws.us'] });
  },
});
import anvil_cloud as anvil

def infra(ctx: anvil.Context):
    anvil.aws.Bucket("logs")
    anvil.aws.Bucket("logs-us", opts=anvil.ResourceOptions(provider=ctx.provider("aws.us")))

anvil.App(
    aws_providers={
        "aws": anvil.AwsProviderConfig(region="ap-southeast-2"),
        "aws.us": anvil.AwsProviderConfig(region="us-east-1"),
    },
    defaults=anvil.DefaultsConfig(
        tags={"team": "platform"},
        state=anvil.StateConfig(retain_versions=3, compress_state=True),
    ),
    compliance=anvil.ComplianceConfig(frameworks=["soc2", "cis"], schedule="daily"),
    run=infra,
)
compress := true
anvil.Run(anvil.AppConfig{
    AwsProviders: map[string]*anvil.AwsProviderConfig{
        "aws":    {Region: "ap-southeast-2"},
        "aws.us": {Region: "us-east-1"},
    },
    Defaults: &anvil.DefaultsConfig{
        Tags:  map[string]string{"team": "platform"},
        State: &anvil.StateConfig{RetainVersions: 3, CompressState: &compress},
    },
    Compliance: &anvil.ComplianceConfig{Frameworks: []string{"soc2", "cis"}, Schedule: "daily"},
    Run: func(ctx *anvil.Context) error {
        _, err := anvilaws.NewBucket(ctx.PulumiCtx(), "logs-us", &anvilaws.BucketArgs{}, ctx.Provider("aws.us"))
        return err
    },
})

Names below are TypeScript (camelCase). In Python use snake_case (retainVersions → retain_versions); in Go use PascalCase (RetainVersions).

run required
function

Your infrastructure. Called with a Context (see below) once providers are set up.

providers
map<string, provider config>

Configuration for the cloud providers Anvil deploys with: which region, account and credentials to use. See Providers below. Python and Go split these into aws_providers / gcp_providers (AwsProviders / GcpProviders).

defaults
object (DefaultsConfig)

Settings applied to the whole app: tags on every resource, and how the stage's state is stored.

compliance
object (ComplianceConfig)

Scheduled compliance scans of the deployed resources. See Compliance scanning.

Providers

A provider is what Anvil uses to talk to a cloud: it holds the region, the account and the credentials every resource is created with. Anvil uses Pulumi's AWS and GCP providers, which are built on the official Terraform providers, so provider settings mean the same thing as they do in Pulumi or Terraform.

  • aws / gcp configure the default provider for that cloud. Every resource uses it unless told otherwise. With no aws entry, Anvil uses the stage's region from anvil.yaml and your environment's credentials, as the AWS CLI finds them.
  • aws.<name> / gcp.<name> add extra named providers, for example another region or account. Pass one to a resource from ctx.providers (TypeScript), ctx.provider(name) (Python) or ctx.Provider(name) (Go), as in the example above.

Anvil supports the settings most apps need: region, credentials or profile, and account, project or role. It adds your defaults.tags to every provider itself. Each SDK's types list the exact settings available.

defaults

tags
map<string, string>

Tags added to every taggable resource (labels on GCP), along with stage and project. Your tags override those two, and a resource's own tags override these. Applies to raw Pulumi resources too, unless you give one its own provider. In Go, pass ctx.Provider("aws") to each resource to get them.

state
object (StateConfig)

How the stage's Pulumi state is stored. See the fields below and the State guide.

defaults.state

retainVersions
integer

How many deploys' state is kept, counting the latest: 2 keeps the latest deploy and the one before, 3 also keeps the deploy before that. Older deploys' state is deleted after each deploy. Counts deploys, not saves. Default: 3. Min 2, max 101.

compressState
boolean

Save state gzipped, which makes every save during a deploy much smaller and faster. Switching either way is safe: existing state is read in either format. Default: true.

The State guide explains where state lives, what's kept, and how to recover from a stuck or interrupted deploy.

compliance

frameworks required
string[]

Frameworks to scan against, e.g. ["soc2", "iso27001", "cis"]. No default.

schedule
string | object

"daily", "weekly", "none", or { cron, timezone? }. Default: "daily".

retention
string

How long results are kept: 30d, 90d, 180d, 1y, 2y or 7y. Default: 1y.

scanOnDeploy
boolean

Also start a scan after each successful deploy. Default: false.

The first deploy with compliance set creates a scanner shared by every Anvil app in the account and region. See Compliance scanning for frameworks, results and costs.

Context

MemberDescription
ctx.stageCurrent deployment stage. Defaults to your OS username for dev isolation.
ctx.projectProject name, read from anvil.yaml.
ctx.export(name, value)Exports a stack output.
ctx.providersNamed cloud providers, for multi-region / multi-account setups.
ctx.ref(id)References a resource declared elsewhere in the program — including later (TypeScript and Python only).

Forward references

Normally you reference a resource through the variable it's assigned to, which means it must be declared first. ctx.ref(id) lets you reference a resource by its logical name — even one declared later in the program:

import { App } from '@anvil-cloud/sdk';
import * as anvil from '@anvil-cloud/sdk';

export default new App({
  run(ctx) {
    const fn = new anvil.aws.Lambda('processor', {
      runtime: 'nodejs24.x',
      handler: 'functions/processor/index.handler',
      environment: {
        // forward reference — `db` is declared below
        DSQL_ENDPOINT: ctx.ref<anvil.aws.DSQL>('db').endpoint,
      },
    });

    const db = new anvil.aws.DSQL('db', { /* ... */ });
  },
});
def infra(ctx: anvil.Context):
    fn = anvil.aws.Lambda("processor",
        runtime=LambdaRuntime.NODEJS24_X,
        handler="functions/processor/index.handler",
        environment={
            # forward reference — `db` is declared below
            "DSQL_ENDPOINT": ctx.ref("db").endpoint,
        },
    )

    db = anvil.aws.DSQL("db", schemas=[...])

anvil.App(run=infra)
anvil.Run(anvil.AppConfig{
    Run: func(ctx *anvil.Context) error {
        // Go does not yet support forward references (ctx.Ref) —
        // declare resources in dependency order.
        db, err := anvilaws.NewDSQL(ctx.PulumiCtx(), "db", nil)
        if err != nil {
            return err
        }

        _, err = anvilaws.NewLambda(ctx.PulumiCtx(), "processor", &anvilaws.LambdaArgs{
            Runtime: anvilaws.LambdaRuntime_Nodejs24_x,
            Handler: pulumi.String("functions/processor/index.handler"),
        })
        return err
    },
})

ctx.ref('db').endpoint resolves to the same output as db.endpoint — they're interchangeable. This is an escape hatch for cases where declaration order is awkward; most code references resources directly. See Forward References for how it works, how to avoid cycles, and its limits.