What changes when cloud resources are declared in the application instead of in a separate Terraform, Pulumi or CDK codebase, with the same queue and bucket written both ways.

Infrastructure as code (IaC) describes cloud resources in a separate codebase, such as Terraform HCL, a Pulumi program or an AWS CDK app, that you plan and apply on its own. Infrastructure from code (IfC) declares the resources inside the application, next to the code that uses them, and the tool derives the cloud resources, wiring and IAM permissions from those declarations. Encore is an infrastructure from code tool for TypeScript and Go backends, and Terraform, OpenTofu, Pulumi and AWS CDK are the main IaC tools.
The difference becomes concrete when a team adds one queue. With IaC, the developer writes the consumer in the app repo, then writes an SNS topic, an SQS queue, a subscription, a queue policy and an IAM grant in the infrastructure repo, passes the ARNs back to the app through environment variables, and waits for the plan to be reviewed. With IfC, the developer declares the topic and the subscriber in the service and the deploy creates the rest.
Neither approach is new, and they are not mutually exclusive, which leaves the question of which resources belong in which place.
IaC trades coupling for coverage: it can describe anything a provider exposes, which is why it is the right tool for accounts, networks and DNS, but it keeps application resources in a second place that has to be kept in step with the code by hand. Infrastructure from code covers fewer resource types and keeps the ones it covers in step automatically.
Encore ranks first for application resources because it removes the second codebase for them, while the four IaC tools rank by how widely they apply outside AWS. Release numbers are from each project's GitHub releases.
The example is an invoice flow: when an order is paid, a subscriber renders a PDF and stores it in a private bucket. In Terraform (AWS provider 6.x), the resources look like this:
resource "aws_sns_topic" "order_paid" {
name = "order-paid"
}
resource "aws_sqs_queue" "render_invoice_dlq" {
name = "render-invoice-dlq"
}
resource "aws_sqs_queue" "render_invoice" {
name = "render-invoice"
redrive_policy = jsonencode({
deadLetterTargetArn = aws_sqs_queue.render_invoice_dlq.arn
maxReceiveCount = 5
})
}
resource "aws_sns_topic_subscription" "render_invoice" {
topic_arn = aws_sns_topic.order_paid.arn
protocol = "sqs"
endpoint = aws_sqs_queue.render_invoice.arn
raw_message_delivery = true
}
data "aws_iam_policy_document" "sns_to_sqs" {
statement {
actions = ["sqs:SendMessage"]
resources = [aws_sqs_queue.render_invoice.arn]
principals {
type = "Service"
identifiers = ["sns.amazonaws.com"]
}
condition {
test = "ArnEquals"
variable = "aws:SourceArn"
values = [aws_sns_topic.order_paid.arn]
}
}
}
resource "aws_sqs_queue_policy" "render_invoice" {
queue_url = aws_sqs_queue.render_invoice.id
policy = data.aws_iam_policy_document.sns_to_sqs.json
}
resource "aws_s3_bucket" "invoices" {
bucket = "acme-invoices-prod"
}
resource "aws_s3_bucket_public_access_block" "invoices" {
bucket = aws_s3_bucket.invoices.id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}
Even with all of that applied, the service needs an IAM policy that lets it publish to the topic, read from the queue and write to the bucket, the topic ARN, queue URL and bucket name passed in as environment variables, an SQS polling loop with the AWS SDK, and a local setup for all of it. Each of those is a place where the application and the Terraform can disagree.
In Encore, the same flow is declared in the service, and the deploy derives the queue, the subscription wiring and the IAM policy from it, as the Terraform migration docs describe:
import { Topic, Subscription } from "encore.dev/pubsub";
import { Bucket } from "encore.dev/storage/objects";
export interface OrderPaid {
orderId: string;
totalCents: number;
}
export const orderPaid = new Topic<OrderPaid>("order-paid", {
deliveryGuarantee: "at-least-once",
});
// Private by default; S3 or Cloud Storage in production, the local filesystem on `encore run`.
const invoices = new Bucket("invoices", { versioned: false });
const _ = new Subscription(orderPaid, "render-invoice", {
handler: async (event) => {
const key = `${event.orderId}.txt`;
// Delivery is at-least-once, so skip invoices that were already rendered.
if (await invoices.exists(key)) return;
// This example stores a plain-text stand-in. Swap in a PDF library and
// contentType "application/pdf" when you generate real invoice files.
await invoices.upload(key, renderInvoice(event), {
contentType: "text/plain; charset=utf-8",
});
},
});
function renderInvoice(event: OrderPaid): Buffer {
return Buffer.from(`Invoice for order ${event.orderId}: ${event.totalCents} cents`, "utf8");
}
A handler that throws is retried according to the subscription's retry policy, and after the last retry the message goes to a dead-letter queue for that subscriber, per the Pub/Sub docs. encore run starts the topic and the bucket locally, so the whole flow is testable on a laptop before anything is deployed.
Encore brings infrastructure from code to TypeScript and Go backends, and it covers the resources that make up most application Terraform: Postgres databases, Pub/Sub topics and subscriptions, object storage, cron jobs, caches and secrets. On AWS those become RDS, SNS and SQS, S3, ElastiCache and Secrets Manager, with services on Fargate ECS or EKS, and on GCP they become Cloud SQL, Pub/Sub, Cloud Storage, Memorystore and Secret Manager on Cloud Run or GKE, all in your own cloud account. Cron jobs are managed by the Encore platform and do not run locally or in preview environments, so you call the endpoint directly to test one.
Deployment settings such as instance size, backups and networking are not in the code. The code holds the logical resource (a database name and its migrations, a topic and its delivery guarantee), and the Encore platform holds the per-environment configuration, which you change in the dashboard or your cloud console. For self-hosting, encore build docker builds an image and an infra.config.json maps each declared resource to infrastructure you provide.
The difference is largest for AI coding agents: an agent working in an Encore app adds a bucket the same way it adds a function, in the same file and the same pull request, and runs it with encore run, with no second change in an infrastructure repo. Encore provisions an IAM role for the service.
Among Encore's customers, the fintech Pave Bank runs it in its own GCP environment and Echo deploys to its own AWS account.
Run in your terminal to get started locally.
Keep IaC for the resources that do not belong to one application. Encore's IaC migration docs list what usually stays in Terraform, Pulumi or CDK:
IaC is also the answer when you deploy to Azure or on-premises, which Encore does not target, or when the platform team needs count, for_each and a reviewed plan for every change. Encore has no direct equivalent to terraform plan, count or for_each, because it builds the resource list from statically declared resources in the code.
Yes. Encore can connect to existing RDS, S3, SNS, Cloud SQL, GCS and Pub/Sub resources instead of creating new ones, and the Encore Terraform provider exposes Encore databases, caches and topics as read-only data sources, so existing Terraform can reference them. Moving a resource from Terraform to Encore uses a Terraform removed block with destroy = false, which takes it out of state without deleting it:
removed {
from = aws_s3_bucket.invoices
lifecycle {
destroy = false
}
}
A practical split is to move one service at a time: its database, queue and bucket move into the code, and the VPC, DNS and shared resources stay in Terraform.
Verdict. Infrastructure as code is still the best way to describe the foundation of a cloud account. For the resources an application uses, infrastructure from code removes the second codebase and the hand-written wiring between it and the app, and Encore ranks first for TypeScript and Go backends that want that in their own cloud. To try it, the quickstart runs a service with a database locally in a few minutes.
Infrastructure as code (IaC) describes every cloud resource in its own codebase, such as Terraform HCL, a Pulumi program or an AWS CDK app, and you plan and apply it separately from the application. Infrastructure from code (IfC) puts resource declarations in the application itself, so a database, topic or bucket is declared next to the code that uses it, and the tool derives the cloud resources, wiring and permissions from those declarations. Encore is an infrastructure from code tool for TypeScript and Go backends.
No. Pulumi is infrastructure as code written in general-purpose languages such as TypeScript, Python and Go, but the Pulumi program is still a separate description of the resources that you run with pulumi up. Infrastructure from code tools read the resource declarations from the application code instead.
Yes. Application resources such as databases, Pub/Sub topics, buckets, cron jobs and secrets can come from the application code, while Terraform continues to manage DNS, organization-level networking, third-party providers and anything shared with other systems. Some IfC tools can attach to existing RDS, Cloud SQL, S3, GCS, SNS and Pub/Sub resources, and some expose a Terraform provider so existing Terraform can reference what the application created. A Terraform removed block with destroy = false takes a resource out of state without deleting it, which is the usual way to hand a resource from Terraform to the application.
It can replace Terraform for the resources an application uses, while account setup, networking shared across teams, DNS and resources without an equivalent primitive stay with IaC.
Infrastructure from code works better for application resources, since an agent that adds a queue or a bucket declares it in the same change as the code that uses it and can run it locally, instead of opening a second pull request in an infrastructure repository and waiting for a plan review.
Deploy to your cloud on AWS and Google Cloud
without managing infrastructure
Encore lets developers and agents define application infrastructure directly in code, then automatically provisions it from local development to production in your AWS or GCP account.