> ## 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.

# Hello World

> Write your first .iac file and validate it with ubx.

When you sit down to write infrastructure for the first time with ubx, you don't start with providers, backends, or environment configs. You start with a single block in a single file. The `unit` block is ubx's fundamental primitive — it declares one cloud resource, gives it a name, and sets its attributes. Everything else in the language builds on top of this. Before you wire outputs into Helm charts, define multi-environment hierarchies, or author reusable components, you need to be comfortable with what a `unit` block looks like, how ubx validates it, and what it compiles to. That's what this tutorial covers — and it's deliberately small, because the goal is clarity, not completeness.

## What you'll learn

* The anatomy of a `.iac` file
* The `unit` block — the fundamental building block of ubx
* How `ubx validate` type-checks your infrastructure without touching the cloud

***

## Why this matters

<Info>
  `ubx validate` never contacts the cloud — it reads your `.iac` files and the locally cached provider schema only. This means you can type-check infrastructure changes in CI without cloud credentials, and catch errors before they reach a real environment. It's the first command you'll run on every change.
</Info>

***

## The source code

<CodeGroup>
  ```hcl main.iac theme={"theme":"css-variables"}
  # A unit block declares a single cloud resource.
  # "aws_s3_bucket_v2" is the Pulumi resource type name.
  # "assets" is the label — how you'll reference this resource elsewhere.
  unit "aws_s3_bucket_v2" "assets" {
    bucket = "my-assets-bucket"
  }
  ```

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

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

***

## How it works

<Steps>
  <Step title="ubx parses the .iac file">
    The lexer tokenises the file and the parser builds an AST. Block type `unit`, resource type `aws_s3_bucket_v2`, label `assets`, and attribute `bucket` are recorded.
  </Step>

  <Step title="The type checker validates the schema">
    ubx looks up `aws_s3_bucket_v2` in the cached AWS provider schema (`~/.ubx/schemas/aws.json`). It confirms `bucket` is a valid string attribute. Unknown attributes produce a warning; missing required fields produce an error.
  </Step>

  <Step title="ubx reports the result">
    No errors, no warnings → `✓ valid`. The cloud is never contacted. No credentials needed.
  </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 assets = new aws.s3.BucketV2("assets", {
      bucket: "my-assets-bucket",
  });
  ```

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

  assets = aws.s3.BucketV2("assets",
      bucket="my-assets-bucket",
  )
  ```

  ```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"
  )

  func main() {
      pulumi.Run(func(ctx *pulumi.Context) error {
          _, err := s3.NewBucketV2(ctx, "assets", &s3.BucketV2Args{
              Bucket: pulumi.String("my-assets-bucket"),
          })
          return err
      })
  }
  ```
</CodeGroup>

***

## Common mistakes

<Warning>
  Using a Terraform resource type name (`aws_s3_bucket`) instead of the Pulumi name (`aws_s3_bucket_v2`) results in a schema-not-found warning. Run `ubx schema list --filter s3` to find the correct type name.
</Warning>

<Tip>
  Resource type names follow Pulumi's naming convention, which often differs slightly from Terraform's. Use `ubx docs aws_s3 --list` to search for matching types in the cached schema without having to guess.
</Tip>

***

## Run it

```bash theme={"theme":"css-variables"}
ubx validate   # type-checks without cloud credentials
ubx plan       # requires AWS credentials
ubx apply      # provisions the S3 bucket
```

***

## What you learned

<Check>A `unit` block declares a single cloud resource with a type and a label</Check>
<Check>`ubx validate` type-checks your .iac files locally — no cloud credentials required</Check>
<Check>Project config lives in `ubx.iac` — name, runtime, backend, and stacks in one place</Check>

***

## Next steps

<CardGroup cols={2}>
  <Card title="Inputs and Outputs" href="/v1/tutorials/01-inputs-and-outputs">
    Add typed variables and stack exports
  </Card>

  <Card title="unit block reference" href="/v1/language/unit">
    Full unit block syntax and all supported meta-arguments
  </Card>
</CardGroup>

***

<Info>
  Full runnable example: [github.com/ubiquex/ubx-examples/00-hello-world](https://github.com/ubiquex/ubx-examples/tree/main/00-hello-world)
</Info>
