Skip to main content
The import block tells ubx that a resource already exists in your cloud account and should be brought under management rather than created from scratch. ubx translates it to pulumi import semantics — Pulumi reads the live state of the resource and records it in the stack’s state file without triggering a create.

Syntax

import <label> {
    <binding> = "<cloud-resource-id>"
}
<label> is an arbitrary identifier that groups related import entries. It has no semantic effect on deployment — it is organizational. <binding> must match a resource binding name declared inside the stack {} body. It is an error if it does not match a declared binding.

Value types

String — single resource

The most common case: one existing resource mapped to one binding.
import networking {
    vpc = "vpc-0abc1234def56789a"
}
The compiler emits opts=pulumi.ResourceOptions(import_="vpc-0abc1234def56789a") on the resource.

List — count-based fan-out

When a binding represents multiple resources (a count or list), provide a list of cloud IDs. ubx generates one resource per element, naming them <binding>-0, <binding>-1, etc.
import networking {
    subnets = [
        "subnet-0aaa1111",
        "subnet-0bbb2222",
        "subnet-0ccc3333",
    ]
}
Generated Python:
subnets = [aws.ec2.Subnet(f"subnets-{_i}",
    vpc_id=vpc.id,
    opts=pulumi.ResourceOptions(import_=_id),
) for _i, _id in enumerate(["subnet-0aaa1111", "subnet-0bbb2222", "subnet-0ccc3333"])]

Object — named map

When a binding represents a named collection, provide an object mapping logical keys to cloud IDs. ubx generates one resource per key, naming them <binding>-<key>.
import networking {
    subnets = {
        west = "subnet-0west1111",
        east = "subnet-0east2222",
    }
}
Generated Python:
subnets = {_k: aws.ec2.Subnet(f"subnets-{_k}",
    vpc_id=vpc.id,
    opts=pulumi.ResourceOptions(import_=_id),
) for _k, _id in {"east": "subnet-0east2222", "west": "subnet-0west1111"}.items()}

Combining with provider

When a resource also declares a provider meta-argument, the two are merged into a single ResourceOptions:
stack "networking" {
    vpc = aws.ec2.Vpc {
        provider   = aws.eu
        cidr_block = "10.0.0.0/16"
    }
}

import networking { vpc = "vpc-0eu1234" }
Generated Python:
vpc = aws.ec2.Vpc("vpc",
    cidr_block="10.0.0.0/16",
    opts=pulumi.ResourceOptions(provider=aws_eu_provider, import_="vpc-0eu1234"),
)

Multiple blocks for the same label

Multiple import blocks with the same label are merged. This lets you split a large import set across multiple files or add imports without touching an existing file.
// imports-vpc.xcl
import networking { vpc = "vpc-0abc" }

// imports-subnets.xcl
import networking { subnets = ["subnet-0a", "subnet-0b"] }
Duplicate binding names across merged blocks are a hard error — two different cloud IDs for the same binding is ambiguous.

Placement

import blocks are top-level. They can appear in any .xcl file in the compilation unit — not inside a stack {} body. The recommended pattern is a dedicated file:
networking/
  stack.xcl        ← resource declarations
  input.xcl
  output.xcl
  imports.xcl      ← import blocks only; delete this file after onboarding
Keep the import file separate so it is easy to identify and remove once the resources are fully under management.

Validation rules

RuleError
Value is not a string, list, or object literalimport "label": entry "name" value must be a string, list, or object literal
Duplicate binding name within one blockimport "label": duplicate entry "name"
Duplicate binding name across merged blocksimport "label": duplicate entry "name" across merged blocks
Value contains a non-string element in a listBuild error — list elements must be string literals
Value contains a non-string value in an objectBuild error — object values must be string literals

Relationship to Pending<T>

Import values must be string literals. Pending references (~resource.attr) are not allowed — the cloud resource ID must be known at compile time.

Relationship to import "path"

XCL also uses import for module imports — bringing names from another .xcl file into scope:
import "shared/tags.xcl" { DefaultTags }
These are syntactically distinct. A module import always starts with a quoted string path; a resource import always starts with a bare identifier followed by {. The parser distinguishes them by lookahead and there is no ambiguity.

After onboarding

Once ubx ship runs successfully with the import blocks present, the resources are recorded in Pulumi state. At that point:
  1. Delete the imports.xcl file (or remove the import blocks from wherever they live).
  2. Run ubx ship again. Pulumi will no longer attempt to import — it will manage the resources normally.
Leaving import blocks in place after the first successful apply is harmless — Pulumi will detect that the resource is already in state and skip the import — but it adds noise to plans and is best removed.