Skip to content

Getting Started

This guide matches the README quickstart for the 0.28.x line. TerraDart is alpha — breaking changes land only on minor bumps; see Status & versioning for the change policy and the path to beta.

  • Dart SDK ≥ 3.6
  • Terraform CLI ≥ 1.11.0
  • Credentials for your target provider (e.g. Google Cloud Application Default Credentials gcloud auth application-default login, or provider environment variables)

Choose terradart_core along with the provider factory package(s) your stack requires:

pubspec.yaml
dependencies:
terradart_core: ^0.28.x
terradart_google: ^0.28.x # for Google Cloud (GA)
# terradart_google_beta: ^0.28.x # for Google Cloud beta-only resources
# terradart_appwrite: ^0.28.x # for Appwrite
# terradart_cloudflare: ^0.28.x # for Cloudflare edge infrastructure

Check pub.dev for the latest patch, then run:

Terminal window
dart pub get

The GA hashicorp/google catalog is filled. Beta-only types live in terradart_google_beta (128 resource factories). terradart_appwrite is filled at the current pin (38 resource factories + 24 data sources). terradart_cloudflare is filled at cloudflare/cloudflare 5.23.0 (257 resource factories + 446 data sources).

Create lib/orders_stack.dart (or follow the pubsub quickstart):

import 'package:terradart_core/terradart_core.dart';
import 'package:terradart_google/provider.dart';
import 'package:terradart_google/pubsub.dart';
final class OrdersStack extends Stack {
OrdersStack({required String projectId})
: super(providers: [GoogleProvider(project: projectId)]) {
final topic = add(GooglePubsubTopic(
localName: 'orders',
name: TfArg.literal('orders-prod'),
));
addExport('ORDERS_TOPIC_NAME', ResourceIdExport(topic.nameRef));
setAppExportsOutputPath('lib/generated/orders_stack.app.dart');
}
}

From bin/infra.dart:

import 'package:my_pkg/orders_stack.dart';
Future<void> main() async {
final stack = OrdersStack(projectId: 'YOUR-PROJECT-ID');
await stack.writeTo('tf-out');
}
Terminal window
dart run bin/infra.dart

This writes tf-out/main.tf.json and, when exports are literal-resolvable, lib/generated/orders_stack.app.dart.

Terminal window
cd tf-out
terraform init
terraform plan
terraform apply

Your existing remote state backend and modules stay unchanged — TerraDart only replaces HCL/JSON authoring.

Import generated constants in app code instead of string literals:

import 'generated/orders_stack.app.dart';
bool acceptsTopic(String eventTopic) =>
eventTopic == OrdersStackExports.ORDERS_TOPIC_NAME;

Rename orders-prod in the Stack without updating the subscriber and dart analyze fails. See Architecture — AppExport and the runnable pubsub quickstart (lib/subscriber_stub.dart).

6. Composing GA and Beta providers (Firebase + Google Cloud)

Section titled “6. Composing GA and Beta providers (Firebase + Google Cloud)”

You can seamlessly combine Google Cloud GA resources (terradart_google) with beta-only resources (terradart_google_beta, such as Firebase project configuration and Web App registration) in a single Stack:

graph TB
  subgraph Client["Firebase App (Beta)"]
    WebApp["GoogleFirebaseWebApp<br/>(Frontend Client)"]
  end

  subgraph GCP["Google Cloud Infrastructure (GA)"]
    CloudRun["GoogleCloudRunV2Service<br/>(Backend API)"]
    Firestore["GoogleFirestoreDatabase<br/>(Native Mode / (default))"]
    Storage["GoogleStorageBucket<br/>(User Uploads)"]

    CloudRun -->|Read/Write Data| Firestore
    CloudRun -->|Store Assets| Storage
  end

  WebApp -.REST API Calls.-> CloudRun
pubspec.yaml
dependencies:
terradart_core: ^0.28.x
terradart_google: ^0.28.x
terradart_google_beta: ^0.28.x
import 'package:terradart_core/terradart_core.dart';
// GA: Google Cloud backend infrastructure
import 'package:terradart_google/cloud_run.dart';
import 'package:terradart_google/firestore.dart';
import 'package:terradart_google/provider.dart';
import 'package:terradart_google/storage.dart';
// Beta: Firebase project and app registration
import 'package:terradart_google_beta/firebase.dart';
import 'package:terradart_google_beta/provider.dart';
final class MobileAppBackendStack extends Stack {
MobileAppBackendStack({required String projectId})
: super(
providers: [
GoogleProvider(project: projectId, region: 'asia-northeast1'),
GoogleBetaProvider(project: projectId, region: 'asia-northeast1'),
],
) {
// 1. [Beta] Enable Firebase on the project
final fb = add(GoogleFirebaseProject(
localName: 'firebase',
project: TfArg.literal(projectId),
));
// 2. [Beta] Register Firebase client app
add(GoogleFirebaseWebApp(
localName: 'web_client',
displayName: TfArg.literal('Web Client'),
project: TfArg.literal(projectId),
dependsOn: [fb],
));
// 3. [GA] Firestore Database (Native mode)
final db = add(GoogleFirestoreDatabase(
localName: 'db',
name: TfArg.literal('(default)'),
locationId: TfArg.literal('asia-northeast1'),
type: TfArg.literal(FirestoreDatabaseType.firestoreNative),
dependsOn: [fb],
));
// 4. [GA] Cloud Storage for user uploads
final uploadsBucket = add(GoogleStorageBucket(
localName: 'uploads',
name: TfArg.literal('$projectId-uploads'),
location: TfArg.literal('ASIA-NORTHEAST1'),
storageClass: TfArg.literal(BucketStorageClass.standard),
uniformBucketLevelAccess: TfArg.literal(true),
));
// 5. [GA] Cloud Run v2 backend service
add(GoogleCloudRunV2Service(
localName: 'api',
name: TfArg.literal('api-server'),
location: TfArg.literal('asia-northeast1'),
template: CloudRunV2ServiceTemplate(
containers: [
CloudRunV2ServiceServiceContainer(
name: TfArg.literal('server'),
image: TfArg.literal(
'us-docker.pkg.dev/cloudrun/container/hello',
),
env: [
CloudRunV2ServiceEnvVar(
name: TfArg.literal('UPLOAD_BUCKET'),
source: CloudRunV2ServiceEnvVarFromLiteral(
TfArg.ref(uploadsBucket.nameRef),
),
),
],
),
],
),
dependsOn: [db],
));
}
}

Wrappers from terradart_google_beta automatically attach provider = "google-beta" in the synthesized Terraform JSON. See the complete runnable recipe in cookbook/firebase-app-backend.

Every factory also takes a provider: parameter — Terraform’s provider meta-argument. Register a second configuration of a provider with alias: and select it per resource; everything else keeps using the default configuration:

final class MultiRegionStack extends Stack {
MultiRegionStack({required String projectId})
: super(providers: [
GoogleProvider(project: projectId, region: 'asia-northeast1'),
GoogleProvider(alias: 'eu', project: projectId, region: 'europe-west1'),
]) {
add(GoogleStorageBucket(
localName: 'assets_eu',
name: TfArg.literal('my-app-assets-eu'),
location: TfArg.literal('EUROPE-WEST1'),
provider: 'google.eu', // provider = google.eu
));
}
}

Synth emits provider.google as a list when a name has more than one configuration, and rejects a provider: that matches no registered configuration. provider: 'google-beta' on a GA-catalog factory puts that one resource on the beta provider.