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

# Multiple Resources with Dependencies

> Wire resolved references between unit blocks to express resource dependencies.

Real infrastructure is never a single resource in isolation. An EC2 instance lives in a VPC. An RDS database needs a subnet group. A security group references a VPC ID. Every resource depends on something else existing first, and the order those things get created matters enormously. In raw Pulumi, you manage this by passing output references between resources — Pulumi infers the dependency graph from which outputs are consumed as inputs. ubx preserves exactly this model, but expresses it in the `.iac` DSL instead of TypeScript or Python. When you reference one resource's output inside another resource's attributes, ubx records that as a dependency edge, builds the graph, topologically sorts the resources, and generates code that creates them in the right order. You don't declare the order — you declare the relationships, and the order follows.

## What you'll learn

* How to reference one resource's output attribute from another
* How ubx infers the dependency order automatically from resource output references
* When to use explicit `depends_on` vs letting ubx infer it

***

## Why this matters

<Info>
  The topological sort means you can declare resources in any order in your `.iac` file. ubx determines the correct apply order from the dependency graph — not from file order. This makes large stacks easier to organise without worrying about declaration order.
</Info>

***

## The source code

<CodeGroup>
  ```hcl main.iac theme={"theme":"css-variables"}
  # Resource 1: the VPC
  unit "aws_vpc" "main" {
    cidr_block           = "10.0.0.0/16"
    enable_dns_hostnames = true
    tags = { Name = "vpc-${input.env}" }
  }

  # Resource 2: DHCP options — no dependency on the VPC yet
  unit "aws_vpc_dhcp_options" "main" {
    domain_name         = "${input.env}.internal"
    domain_name_servers = ["AmazonProvidedDNS"]
  }

  # Resource 3: associate them — depends on both resources above.
  # ubx infers this from the output references and creates the association last.
  unit "aws_vpc_dhcp_options_association" "main" {
    vpc_id          = unit.aws_vpc.main.id
    dhcp_options_id = unit.aws_vpc_dhcp_options.main.id
  }
  ```

  ```hcl inputs.iac theme={"theme":"css-variables"}
  input "env" {
    type    = "string"
    default = "dev"
  }
  ```

  ```hcl outputs.iac theme={"theme":"css-variables"}
  output "vpc_id" {
    value = unit.aws_vpc.main.id
  }
  ```

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

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

***

## How it works

<Steps>
  <Step title="The IR pass builds a dependency graph">
    Every `unit.x.y.attr` reference is recorded as an `IRRef` with `Pending: true`. The IR builder constructs a directed graph: the DHCP association depends on both the VPC and the DHCP options object.
  </Step>

  <Step title="Topological sort determines apply order">
    Resources are sorted so that dependencies are always created before dependents. The VPC and DHCP options can be created in parallel; the association waits for both.
  </Step>

  <Step title="Pulumi executes in the correct order">
    The generated code uses `pulumi.all([...]).apply(...)` to ensure the association resource only runs after both the VPC and DHCP options exist and their IDs are available.
  </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 main_vpc = new aws.ec2.Vpc("main_vpc", {
      cidrBlock: "10.0.0.0/16",
      enableDnsHostnames: true,
      tags: { Name: `vpc-${env}` },
  });

  const main_dhcp = new aws.ec2.VpcDhcpOptions("main_dhcp", {
      domainName: `${env}.internal`,
      domainNameServers: ["AmazonProvidedDNS"],
  });

  const main_assoc = pulumi.all([main_vpc.id, main_dhcp.id])
      .apply(([vpcId, dhcpOptionsId]) =>
          new aws.ec2.VpcDhcpOptionsAssociation("main_assoc", {
              vpcId,
              dhcpOptionsId,
          })
      );

  export const vpcId = main_vpc.id;
  ```

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

  main_vpc = aws.ec2.Vpc("main_vpc",
      cidr_block="10.0.0.0/16",
      enable_dns_hostnames=True,
      tags={"Name": f"vpc-{env}"},
  )

  main_dhcp = aws.ec2.VpcDhcpOptions("main_dhcp",
      domain_name=f"{env}.internal",
      domain_name_servers=["AmazonProvidedDNS"],
  )

  main_assoc = pulumi.Output.all(
      vpc_id=main_vpc.id,
      dhcp_options_id=main_dhcp.id,
  ).apply(lambda args: aws.ec2.VpcDhcpOptionsAssociation("main_assoc",
      vpc_id=args["vpc_id"],
      dhcp_options_id=args["dhcp_options_id"],
  ))

  pulumi.export("vpc_id", main_vpc.id)
  ```

  ```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/ec2"
      "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"
          }

          mainVpc, err := ec2.NewVpc(ctx, "main_vpc", &ec2.VpcArgs{
              CidrBlock:          pulumi.String("10.0.0.0/16"),
              EnableDnsHostnames: pulumi.Bool(true),
              Tags:               pulumi.StringMap{"Name": pulumi.Sprintf("vpc-%s", env)},
          })
          if err != nil {
              return err
          }

          mainDhcp, err := ec2.NewVpcDhcpOptions(ctx, "main_dhcp", &ec2.VpcDhcpOptionsArgs{
              DomainName:        pulumi.Sprintf("%s.internal", env),
              DomainNameServers: pulumi.StringArray{pulumi.String("AmazonProvidedDNS")},
          })
          if err != nil {
              return err
          }

          _, err = pulumi.All(mainVpc.ID(), mainDhcp.ID()).ApplyT(
              func(args []interface{}) (*ec2.VpcDhcpOptionsAssociation, error) {
                  return ec2.NewVpcDhcpOptionsAssociation(ctx, "main_assoc",
                      &ec2.VpcDhcpOptionsAssociationArgs{
                          VpcId:         pulumi.String(args[0].(string)),
                          DhcpOptionsId: pulumi.String(args[1].(string)),
                      })
              })
          if err != nil {
              return err
          }

          ctx.Export("vpcId", mainVpc.ID())
          return nil
      })
  }
  ```
</CodeGroup>

***

## Common mistakes

<Warning>
  Adding explicit `depends_on` for relationships already expressed by direct attribute references is unnecessary — ubx infers those dependencies automatically. Reserve `depends_on` for side-effect dependencies that aren't captured by a direct attribute reference, such as an IAM policy that must exist before a resource is created even though the resource doesn't directly reference any of the policy's outputs.
</Warning>

<Tip>
  When two resources have no dependency between them — like the VPC and DHCP options in this example — Pulumi creates them in parallel. ubx's generated `.apply()` chains express exactly the minimum required ordering, letting Pulumi parallelise everything it safely can.
</Tip>

***

## Run it

```bash theme={"theme":"css-variables"}
ubx validate
ubx plan    # requires AWS credentials
ubx apply   # creates VPC and DHCP options in parallel, then the association
```

***

## What you learned

<Check>ubx builds a dependency graph from resource output references and topologically sorts resources</Check>
<Check>`depends_on` is only needed for side-effect dependencies not expressed by direct references</Check>
<Check>Resources can be declared in any order in `.iac` — apply order is computed, not declared</Check>

***

## Next steps

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

  <Card title="Pending type concept" href="/v1/concepts/pending-type">
    Deep dive into Resolved vs Pending values
  </Card>
</CardGroup>

***

<Info>
  Full runnable example: [github.com/ubiquex/ubx-examples/03-multiple-resources-with-deps](https://github.com/ubiquex/ubx-examples/tree/main/03-multiple-resources-with-deps)
</Info>
