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:
- The customer selects a
dbInstanceClasswhen creating a deployment - The value is passed through the
parameterDependencyMapto thedbInfraresource - The
variablesValuesFileOverrideinjects 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.
Exporting Selected Terraform Outputs¶
All Terraform outputs are available to dependent resources through {{ $<resource>.out.<key> }}. If you also want a selected output to surface as an exported field on the Terraform resource itself, declare it under requiredOutputs.
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
requiredOutputs:
- key: database_endpoint
exported: true
- key: cache_endpoint
exported: true
Use this when you want the output to be visible as part of the Terraform resource details in Omnistrate, not just consumed by another resource in the DAG.
Note
requiredOutputs does not replace normal output mapping. Dependent resources still consume Terraform outputs using {{ $<resourceName>.out.<outputKey> }}.
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:
GCP outputs:
Azure outputs:
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:
cloudInfrais an internal Terraform resource that provisions an RDS database and S3 bucketWebAppis the customer-facing Helm chart that depends oncloudInfra- API parameters (
dbInstanceClass,storageSize) are defined onWebAppand mapped tocloudInfrathroughparameterDependencyMap - Terraform outputs (
database_endpoint,database_port,s3_bucket_arn) are injected into the Helm chart values - System parameters provide the VPC, region, and subnet context to Terraform
Direct vs Transitive Parameter Mapping¶
parameterDependencyMap is direct between a parent resource and its immediate child. It is not transitive.
If App depends on both Infra and SecureInfra, and SecureInfra also depends on Infra, then:
- Parameters that
Infraneeds must be mapped directly fromApptoInfra - Parameters mapped from
ApptoSecureInfraare not automatically forwarded toInfra - Outputs from
Infracan still be consumed bySecureInfrathrough{{ $Infra.out.<key> }}
Example:
services:
- name: Infra
internal: true
apiParameters:
- key: access_type
name: Access Type
type: String
export: false
required: false
modifiable: true
defaultValue: public
- name: SecureInfra
internal: true
dependsOn:
- Infra
apiParameters:
- key: tenant_name
name: Tenant Name
type: String
export: false
required: false
modifiable: true
defaultValue: tenant-a
- name: App
dependsOn:
- Infra
- SecureInfra
apiParameters:
- key: accessType
name: Access Type
type: String
export: true
required: false
modifiable: true
defaultValue: public
parameterDependencyMap:
Infra: access_type
- key: tenantName
name: Tenant Name
type: String
export: true
required: false
modifiable: true
defaultValue: tenant-a
parameterDependencyMap:
SecureInfra: tenant_name
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:
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:
Next Steps¶
- Terraform Overview: Understand how Terraform integrates with the Omnistrate platform
- Multi-Cloud Configuration: Configure per-cloud-provider Terraform stacks
- Helm and Terraform: Complete walkthrough combining Helm with Terraform
- API Parameters: Full guide on defining customer-facing parameters
- Resource Dependencies: How resource ordering and parameter mapping works