Skip to content

Input Parameters and Output Mapping

Overview

Terraform resources in Omnistrate support two key integration mechanisms:

  • Input parameters: Pass dynamic values into your Terraform configuration through variables — including system parameters, API parameters, and inline variable overrides
  • Output mapping: Export values from your Terraform state (endpoints, ARNs, IDs) and inject them into dependent resources like Helm charts, Operators, or other Terraform stacks

Together, these let you build fully wired, multi-resource SaaS Products where infrastructure provisioned by Terraform is seamlessly connected to application layers.

Input Parameters

System Parameters in Terraform Templates

Omnistrate injects system parameters directly into your .tf files at deployment time. Wrap system parameter references in {{ }} within your Terraform configuration:

provider "aws" {
  region = "{{ $sys.deploymentCell.region }}"
}

resource "aws_security_group" "app_sg" {
  name   = "app-sg-{{ $sys.id }}"
  vpc_id = "{{ $sys.deploymentCell.cloudProviderNetworkID }}"

  ingress {
    from_port   = 5432
    to_port     = 5432
    protocol    = "tcp"
    cidr_blocks = ["{{ $sys.deploymentCell.cidrRange }}"]
  }
}

resource "aws_db_subnet_group" "main" {
  name = "db-subnet-{{ $sys.id }}"
  subnet_ids = [
    "{{ $sys.deploymentCell.privateSubnetIDs[0].id }}",
    "{{ $sys.deploymentCell.privateSubnetIDs[1].id }}"
  ]
}

Commonly used system parameters in Terraform templates:

Parameter Description
$sys.id Unique deployment identifier — use for naming resources to avoid collisions
$sys.deploymentCell.region Target deployment region
$sys.deploymentCell.cloudProviderNetworkID VPC / VNet / Network ID
$sys.deploymentCell.cidrRange CIDR range of the deployment cell
$sys.deploymentCell.publicSubnetIDs[i].id Public subnet IDs
$sys.deploymentCell.privateSubnetIDs[i].id Private subnet IDs

For the complete list, see System Parameters.

Variable Overrides with Inline Values

Use the variablesValuesFileOverride property to pass Terraform variable values directly from your Plan specification — no separate .tfvars file needed in your repository. This is useful for injecting system parameters into Terraform variables.

Define variables in your Terraform stack:

# variables.tf
variable "vpc_id" {
  type        = string
  description = "VPC ID for the deployment"
}

variable "region" {
  type        = string
  description = "AWS region"
}

variable "instance_type" {
  type        = string
  default     = "db.t3.medium"
  description = "RDS instance class"
}

variable "subnet_ids" {
  type        = list(string)
  description = "Subnet IDs for the database subnet group"
}

Then override them in your Plan specification:

services:
  - name: dbInfra
    internal: true
    terraformConfigurations:
      configurationPerCloudProvider:
        aws:
          terraformPath: /terraform/aws
          variablesValuesFileOverride: |
            vpc_id = "{{ $sys.deploymentCell.cloudProviderNetworkID }}"
            region = "{{ $sys.deploymentCell.region }}"
            instance_type = "db.t3.medium"
            subnet_ids = [
              "{{ $sys.deploymentCell.privateSubnetIDs[0].id }}",
              "{{ $sys.deploymentCell.privateSubnetIDs[1].id }}"
            ]
          gitConfiguration:
            reference: refs/heads/main
            repositoryUrl: https://github.com/your-org/infra-repo.git

Tip

The variablesValuesFileOverride is useful when you want to customize Terraform behavior on the Omnistrate platform without modifying the underlying Terraform code. Your Terraform stack stays generic and reusable while environment-specific values are injected from the Plan specification.

API Parameters as Terraform Inputs

You can connect API parameters — values your customers provide at deployment time — to Terraform variables through the same mechanism. Define an API parameter on the parent resource and reference it in the Terraform variable override:

services:
  - name: MyApp
    dependsOn:
      - dbInfra
    apiParameters:
      - key: dbInstanceClass
        description: Database instance size
        name: Database Instance Class
        type: String
        modifiable: true
        required: false
        export: true
        defaultValue: "db.t3.medium"
        options:
          - "db.t3.micro"
          - "db.t3.medium"
          - "db.t3.large"
        parameterDependencyMap:
          dbInfra: dbInstanceClass

  - name: dbInfra
    internal: true
    apiParameters:
      - key: dbInstanceClass
        description: Database instance class
        name: Database Instance Class
        type: String
        modifiable: true
        required: false
        export: false
        defaultValue: "db.t3.medium"
    terraformConfigurations:
      configurationPerCloudProvider:
        aws:
          terraformPath: /terraform/aws
          variablesValuesFileOverride: |
            instance_type = "{{ $var.dbInstanceClass }}"
            vpc_id = "{{ $sys.deploymentCell.cloudProviderNetworkID }}"
            region = "{{ $sys.deploymentCell.region }}"
          gitConfiguration:
            reference: refs/heads/main
            repositoryUrl: https://github.com/your-org/infra-repo.git

In this pattern:

  1. The customer selects a dbInstanceClass when creating a deployment
  2. The value is passed through the parameterDependencyMap to the dbInfra resource
  3. The variablesValuesFileOverride injects the value into the Terraform variable

Output Mapping

Defining Terraform Outputs

In your Terraform stack, define outputs for any values you want to expose to other resources:

# outputs.tf

output "database_endpoint" {
  value       = aws_db_instance.main.endpoint
  description = "RDS instance endpoint"
}

output "database_port" {
  value       = aws_db_instance.main.port
  description = "RDS instance port"
}

output "s3_bucket_arn" {
  value       = aws_s3_bucket.data.arn
  description = "S3 bucket ARN for data storage"
}

output "connection_details" {
  value = {
    endpoint = aws_db_instance.main.endpoint
    port     = aws_db_instance.main.port
    database = aws_db_instance.main.db_name
  }
  description = "Structured connection details"
}

output "cache_endpoint" {
  value     = aws_elasticache_cluster.cache.cache_nodes[0].address
  sensitive = true
}

Omnistrate automatically captures all values from the Terraform output block after each apply.

Referencing Outputs in Dependent Resources

Use the {{ $<resourceName>.out.<outputKey> }} syntax to inject Terraform outputs into other resources. The <resourceName> is the name of the Terraform resource as defined in your Plan specification.

In Helm Chart Values

services:
  - name: infraTerraform
    internal: true
    terraformConfigurations:
      configurationPerCloudProvider:
        aws:
          terraformPath: /terraform/aws
          gitConfiguration:
            reference: refs/heads/main
            repositoryUrl: https://github.com/your-org/infra-repo.git

  - name: MyApp
    dependsOn:
      - infraTerraform
    helmChartConfiguration:
      chartName: my-app
      chartVersion: 1.0.0
      chartRepoName: my-charts
      chartRepoURL: https://charts.example.com
      chartValues:
        database:
          host: "{{ $infraTerraform.out.database_endpoint }}"
          port: "{{ $infraTerraform.out.database_port }}"
        storage:
          bucketArn: "{{ $infraTerraform.out.s3_bucket_arn }}"
        cache:
          endpoint: "{{ $infraTerraform.out.cache_endpoint }}"

Accessing Nested Output Values

When a Terraform output returns a structured object, you can access nested properties with dot notation:

# Terraform output
output "connection_details" {
  value = {
    endpoint = aws_db_instance.main.endpoint
    port     = aws_db_instance.main.port
  }
}
# Plan specification reference
chartValues:
  dbHost: "{{ $infraTerraform.out.connection_details.endpoint }}"
  dbPort: "{{ $infraTerraform.out.connection_details.port }}"

In Operator CRD Configuration

services:
  - name: infraTerraform
    internal: true
    terraformConfigurations:
      configurationPerCloudProvider:
        aws:
          terraformPath: /terraform/aws
          gitConfiguration:
            reference: refs/heads/main
            repositoryUrl: https://github.com/your-org/infra-repo.git

  - name: DatabaseOperator
    dependsOn:
      - infraTerraform
    operatorCRDConfiguration:
      template: |
        apiVersion: db.example.com/v1
        kind: Database
        spec:
          externalEndpoint: "{{ $infraTerraform.out.database_endpoint }}"
          bucketArn: "{{ $infraTerraform.out.s3_bucket_arn }}"

Outputs Across Multi-Cloud Stacks

When you define Terraform stacks for multiple cloud providers, keep output names consistent so dependent resources work without cloud-specific logic:

AWS outputs:

output "database_endpoint" {
  value = aws_db_instance.main.endpoint
}

GCP outputs:

output "database_endpoint" {
  value = google_sql_database_instance.main.private_ip_address
}

Azure outputs:

output "database_endpoint" {
  value = azurerm_postgresql_flexible_server.main.fqdn
}

The dependent resource references {{ $infraTerraform.out.database_endpoint }} regardless of which cloud provider is in use.

End-to-End Example

This complete example demonstrates a multi-resource Plan with a Terraform stack provisioning cloud infrastructure, API parameters exposed to customers, and output mapping to a Helm chart application:

name: Full Stack SaaS
deployment:
  hostedDeployment:
    awsAccountId: "<AWS_ACCOUNT_ID>"
    awsBootstrapRoleAccountArn: arn:aws:iam::<AWS_ACCOUNT_ID>:role/omnistrate-bootstrap-role

services:
  - name: cloudInfra
    internal: true
    apiParameters:
      - key: dbInstanceClass
        description: Database instance class
        name: Database Instance Class
        type: String
        modifiable: true
        required: false
        export: false
        defaultValue: "db.t3.medium"
      - key: storageSize
        description: Database storage size in GB
        name: Storage Size (GB)
        type: String
        modifiable: true
        required: false
        export: false
        defaultValue: "20"
    terraformConfigurations:
      configurationPerCloudProvider:
        aws:
          terraformPath: /terraform/aws
          variablesValuesFileOverride: |
            vpc_id = "{{ $sys.deploymentCell.cloudProviderNetworkID }}"
            region = "{{ $sys.deploymentCell.region }}"
            instance_class = "{{ $var.dbInstanceClass }}"
            storage_size = {{ $var.storageSize }}
            subnet_ids = [
              "{{ $sys.deploymentCell.privateSubnetIDs[0].id }}",
              "{{ $sys.deploymentCell.privateSubnetIDs[1].id }}"
            ]
            resource_prefix = "saas-{{ $sys.id }}"
          gitConfiguration:
            reference: refs/tags/v1.0.0
            repositoryUrl: https://github.com/your-org/infra-repo.git

  - name: WebApp
    dependsOn:
      - cloudInfra
    network:
      ports:
        - 8080
    compute:
      instanceTypes:
        - apiParam: instanceType
          cloudProvider: aws
    helmChartConfiguration:
      chartName: web-app
      chartVersion: 2.0.0
      chartRepoName: my-charts
      chartRepoURL: https://charts.example.com
      chartValues:
        config:
          databaseUrl: "{{ $cloudInfra.out.database_endpoint }}"
          databasePort: "{{ $cloudInfra.out.database_port }}"
          s3BucketArn: "{{ $cloudInfra.out.s3_bucket_arn }}"
    apiParameters:
      - key: instanceType
        description: Compute instance type
        name: Instance Type
        type: String
        modifiable: true
        required: false
        export: true
        defaultValue: "t4g.small"
      - key: dbInstanceClass
        description: Database instance size
        name: Database Instance Class
        type: String
        modifiable: true
        required: false
        export: true
        defaultValue: "db.t3.medium"
        options:
          - "db.t3.micro"
          - "db.t3.medium"
          - "db.t3.large"
        parameterDependencyMap:
          cloudInfra: dbInstanceClass
      - key: storageSize
        description: Database storage in GB
        name: Storage Size (GB)
        type: String
        modifiable: true
        required: false
        export: true
        defaultValue: "20"
        parameterDependencyMap:
          cloudInfra: storageSize

In this example:

  1. cloudInfra is an internal Terraform resource that provisions an RDS database and S3 bucket
  2. WebApp is the customer-facing Helm chart that depends on cloudInfra
  3. API parameters (dbInstanceClass, storageSize) are defined on WebApp and mapped to cloudInfra through parameterDependencyMap
  4. Terraform outputs (database_endpoint, database_port, s3_bucket_arn) are injected into the Helm chart values
  5. System parameters provide the VPC, region, and subnet context to Terraform

Best Practices

Consistent Output Names

Use the same output key names across your AWS, GCP, and Azure Terraform stacks. This ensures dependent resources work without cloud-specific branching.

Use Git Tags for Versioning

Reference specific Git tags (refs/tags/v1.0.0) instead of branches for production Plans. This ensures deployments are reproducible and upgrades are intentional.

Unique Resource Names

Always include $sys.id in cloud resource names to prevent naming collisions between customer deployments:

resource "aws_db_instance" "main" {
  identifier = "db-{{ $sys.id }}"
  ...
}

Mark Terraform Resources as Internal

Terraform resources are infrastructure dependencies — mark them as internal: true so they are not directly exposed in your customer-facing API. Customers interact with the parent resource that depends on the Terraform stack.

Sensitive Outputs

Mark Terraform outputs as sensitive = true for values like passwords or connection strings that should not appear in logs:

output "db_password" {
  value     = random_password.db.result
  sensitive = true
}

Next Steps