> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ubiquex.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Inputs and Outputs

> Add typed stack variables and export values for use by other stacks.

The moment you want to deploy the same infrastructure to more than one environment, hard-coding values stops working. A bucket named `my-assets-dev` can't become `my-assets-prod` without editing the source file — and editing source files per environment is exactly the kind of fragile, error-prone pattern that infrastructure-as-code exists to eliminate. `input` blocks solve this by turning any value into a configurable parameter with a type, an optional default, and a name you can pass at apply time. On the other side of the same problem, once Pulumi provisions a resource, you need to get its runtime attributes — the endpoint, the ARN, the URL — out of the stack and into the hands of operators, CI pipelines, and other stacks that depend on them. That's what `output` blocks are for. Together, inputs and outputs are what turn a collection of resource declarations into a genuinely reusable, composable stack.

## What you'll learn

* How `input` blocks declare typed, configurable variables
* How `output` blocks export stack values
* Why inputs are `Resolved<T>` — available at compile time, not pending
* How to organise your project into separate `.iac` files by block type

***

## Why this matters

<Info>
  A stack with well-defined inputs and outputs becomes a reusable building block. The same `.iac` files can deploy to dev, staging, and prod by passing different values at apply time — no branching, no duplication, no environment-specific source files.
</Info>

***

## The source code

Splitting your stack into separate files by block type keeps each file focused and easy to navigate. `inputs.iac` holds your variables, `main.iac` holds your resources, `outputs.iac` holds your exports, and `ubx.iac` holds your project config.

<CodeGroup>
  ```hcl main.iac theme={"theme":"css-variables"}
  unit "aws_s3_bucket_v2" "assets" {
    # Interpolate input values into strings with ${ }
    bucket = "assets-${input.env}-${input.bucket_suffix}"
  }
  ```

  ```hcl inputs.iac theme={"theme":"css-variables"}
  # input declares a typed variable with an optional default.
  # If no default is set, the value must be supplied at apply time.
  input "env" {
    type    = "string"
    default = "dev"
  }

  input "bucket_suffix" {
    type = "string"
    # No default — must be provided via ubx apply --var bucket_suffix=myapp
  }
  ```

  ```hcl outputs.iac theme={"theme":"css-variables"}
  # output exports a value from this stack.
  # Other stacks can reference it via cross-stack refs.
  output "bucket_name" {
    value = unit.aws_s3_bucket_v2.assets.bucket
  }

  output "bucket_arn" {
    value     = unit.aws_s3_bucket_v2.assets.arn
    sensitive = false
  }
  ```

  ```hcl ubx.iac theme={"theme":"css-variables"}
  ubx {
    name    = "inputs-and-outputs"
    runtime = "typescript"
    stacks  = ["dev", "staging", "prod"]
  }

  backend "local" {
    path = ".ubx/state"
  }
  ```
</CodeGroup>

***

## How it works

<Steps>
  <Step title="Inputs are resolved before the stack runs">
    Inputs with defaults are `Resolved<T>` — their value is known at compile time. Inputs without defaults are also `Resolved<T>`, their value passed before the stack runs via `--var` or Pulumi config. Neither type is `Pending<T>` — that's reserved for resource outputs that only exist after Pulumi creates a resource.
  </Step>

  <Step title="String interpolation is evaluated at compile time">
    `"assets-${input.env}-${input.bucket_suffix}"` resolves into a plain string expression. No `.apply()` chain is generated — both inputs are resolved values, so the compiler can inline them directly.
  </Step>

  <Step title="Outputs are registered with Pulumi">
    The `output` blocks compile to `export const` statements in TypeScript or `pulumi.export()` in Python/Go. Pulumi registers these as stack outputs visible via `ubx output` and accessible by other stacks via cross-stack references.
  </Step>
</Steps>

***

## What ubx generates

<CodeGroup>
  ```typescript index.ts theme={"theme":"css-variables"}
  // Generated by ubx — do not edit
  import * as pulumi from "@pulumi/pulumi";
  import * as aws from "@pulumi/aws";

  const config = new pulumi.Config();
  const env = config.get("env") || "dev";
  const bucketSuffix = config.require("bucketSuffix");

  const assets = new aws.s3.BucketV2("assets", {
      bucket: `assets-${env}-${bucketSuffix}`,
  });

  export const bucketName = assets.bucket;
  export const bucketArn = assets.arn;
  ```

  ```python __main__.py theme={"theme":"css-variables"}
  # Generated by ubx — do not edit
  import pulumi
  import pulumi_aws as aws

  config = pulumi.Config()
  env = config.get("env") or "dev"
  bucket_suffix = config.require("bucket_suffix")

  assets = aws.s3.BucketV2("assets",
      bucket=f"assets-{env}-{bucket_suffix}",
  )

  pulumi.export("bucket_name", assets.bucket)
  pulumi.export("bucket_arn", assets.arn)
  ```

  ```go main.go theme={"theme":"css-variables"}
  // Generated by ubx — do not edit
  package main

  import (
      "github.com/pulumi/pulumi-aws/sdk/v6/go/aws/s3"
      "github.com/pulumi/pulumi/sdk/v3/go/pulumi"
      "github.com/pulumi/pulumi/sdk/v3/go/pulumi/config"
  )

  func main() {
      pulumi.Run(func(ctx *pulumi.Context) error {
          cfg := config.New(ctx, "")
          env := cfg.Get("env")
          if env == "" {
              env = "dev"
          }
          bucketSuffix := cfg.Require("bucketSuffix")

          assets, err := s3.NewBucketV2(ctx, "assets", &s3.BucketV2Args{
              Bucket: pulumi.Sprintf("assets-%s-%s", env, bucketSuffix),
          })
          if err != nil {
              return err
          }

          ctx.Export("bucketName", assets.Bucket)
          ctx.Export("bucketArn", assets.Arn)
          return nil
      })
  }
  ```
</CodeGroup>

***

## Common mistakes

<Warning>
  `input.*` references are `Resolved<T>` — they don't need any special syntax. Only resource outputs like `unit.x.y.endpoint` are `Pending<T>` (resolved at apply time).
</Warning>

<Tip>
  Use `sensitive = true` on `output` blocks for passwords, tokens, or connection strings. ubx wraps the value in `pulumi.secret()`, encrypting it in state and masking it in `ubx output` display.
</Tip>

***

## Run it

```bash theme={"theme":"css-variables"}
ubx validate
ubx plan   # requires AWS credentials
ubx apply --var bucket_suffix=myapp   # pass the required input
ubx output # shows bucket_name and bucket_arn after apply
```

***

## What you learned

<Check>`input` blocks declare typed, configurable variables with optional defaults</Check>
<Check>Inputs are `Resolved<T>` — available at compile time, no pending chain required</Check>
<Check>`output` blocks export stack values; `sensitive = true` encrypts them in state</Check>
<Check>Splitting blocks into `main.iac`, `inputs.iac`, and `outputs.iac` keeps large stacks navigable</Check>

***

## Next steps

<CardGroup cols={2}>
  <Card title="Locals" href="/v1/tutorials/02-locals">
    Compute reusable values from inputs and other expressions
  </Card>

  <Card title="Pending refs: RDS to Helm" href="/v1/tutorials/04-pending-refs-rds-to-helm">
    The Pending\<T> type system in depth
  </Card>
</CardGroup>

***

<Info>
  Full runnable example: [github.com/ubiquex/ubx-examples/01-inputs-and-outputs](https://github.com/ubiquex/ubx-examples/tree/main/01-inputs-and-outputs)
</Info>
