Next Steps
Congratulations!
You’ve completed the Pulumi for Terraform Users guide and learned how to:
- Reference existing Terraform state files from Pulumi
- Use any Terraform provider in Pulumi programs
- Import and use Terraform modules directly
- Convert HCL code to Pulumi when beneficial
- Orchestrate both Terraform and Pulumi deployments together
What’s next?
Based on your journey through this guide, consider these next steps:
- Start small: Begin with a simple coexistence pattern in your current environment
- Experiment: Try different integration approaches to find what works for your team
- Build expertise: Invest in learning both Terraform and Pulumi deeply
- Share knowledge: Document your integration patterns for your team
- Consider migration: When ready, plan a gradual migration to Pulumi for new projects
Our goal is to empower you to use the right tool for the job while maintaining a cohesive infrastructure management strategy.
Advanced integration patterns
Beyond the examples shown, there are some more advanced integration patterns you can employ. These will be very dependent on your particular needs, so take these recommendations as a general guide to some strategies you can use to manage more complex environments.
Multi-stack architectures
Organize complex infrastructure with multiple interconnected stacks:
// Core infrastructure stack (could be Terraform)
export const vpc = new aws.ec2.Vpc("main", {
cidrBlock: "10.0.0.0/16",
});
// Application stack (Pulumi)
const coreInfra = new pulumi.StackReference("core-infra");
const vpcId = coreInfra.getOutput("vpcId");
// Database stack (Terraform via state reference)
const dbState = new terraform.state.S3Reference("database", {
bucket: "my-terraform-state",
key: "database/terraform.tfstate",
});
Complex state referencing
Handle complex state structures and transformations:
// Reference multiple Terraform states
const networkState = new terraform.state.S3Reference("network", {
bucket: "terraform-state",
key: "network/terraform.tfstate",
});
const securityState = new terraform.state.S3Reference("security", {
bucket: "terraform-state",
key: "security/terraform.tfstate",
});
// Transform and combine outputs
const subnetIds = networkState.getOutput("private_subnet_ids");
const securityGroupIds = securityState.getOutput("security_group_ids");
// Create resources using combined state
const cluster = new aws.ecs.Cluster("app-cluster", {
// Configure using multiple state outputs
configuration: {
executeCommandConfiguration: {
kmsKeyId: securityState.getOutput("kms_key_id"),
logging: "DEFAULT",
},
},
});
Migration strategies
Gradual migration approaches
When ready to migrate from Terraform to Pulumi:
- Import existing resources: Use
pulumi import
to bring Terraform-managed resources under Pulumi management - Parallel management: Run both tools temporarily while migrating
- State migration: Transfer state ownership gradually, resource-by-resource
- Validation: Ensure identical infrastructure before switching
Import existing resources
# Import existing Terraform resources into Pulumi
$ pulumi import aws:ec2/vpc:Vpc main-vpc vpc-12345
$ pulumi import aws:ec2/subnet:Subnet private-subnet-1 subnet-67890
$ pulumi import aws:ecs/cluster:Cluster app-cluster my-cluster
State migration utilities
Create utilities to help with migration:
// Migration helper utility
export class TerraformMigrationHelper {
constructor(private terraformStateRef: terraform.state.S3Reference) {}
// Import all resources of a given type
async importResourceType(resourceType: string, pulumiType: string) {
const resources = await this.terraformStateRef.getOutput("resources");
const filtered = resources.filter(r => r.type === resourceType);
for (const resource of filtered) {
console.log(`Importing ${resourceType} ${resource.name}`);
// Use Pulumi import API to import resources
}
}
// Validate that imported resources match Terraform state
async validateImports() {
// Compare Terraform state with Pulumi state
// Report any differences
}
}
Advanced Pulumi features
CrossGuard for policy enforcement
Implement infrastructure policies that work across both Terraform and Pulumi:
// crossguard/index.ts
import * as aws from "@pulumi/aws";
import { PolicyPack, validateResourceOfType } from "@pulumi/policy";
new PolicyPack("terraform-integration-policies", {
policies: [
{
name: "require-tags",
description: "All resources must have required tags",
enforcementLevel: "mandatory",
validateResource: validateResourceOfType(aws.ec2.Instance, (instance, args, reportViolation) => {
const requiredTags = ["Environment", "Owner", "Project"];
const tags = instance.tags || {};
for (const tag of requiredTags) {
if (!tags[tag]) {
reportViolation(`Missing required tag: ${tag}`);
}
}
}),
},
{
name: "no-public-buckets",
description: "S3 buckets cannot be publicly accessible",
enforcementLevel: "mandatory",
validateResource: validateResourceOfType(aws.s3.Bucket, (bucket, args, reportViolation) => {
if (bucket.acl === "public-read" || bucket.acl === "public-read-write") {
reportViolation("S3 bucket cannot have public ACL");
}
}),
},
],
});
Automation API for programmatic control
Embed Pulumi in applications for dynamic infrastructure management:
// automation-api-example.ts
import * as pulumi from "@pulumi/pulumi/automation";
import * as aws from "@pulumi/aws";
async function createEnvironmentStack(environmentName: string) {
const stackName = `${environmentName}-app`;
// Create or select stack
const stack = await pulumi.LocalWorkspace.createOrSelectStack({
stackName,
projectName: "dynamic-environments",
program: async () => {
// Reference shared Terraform infrastructure
const tfState = new pulumi.terraform.state.S3Reference("shared-infra", {
bucket: "terraform-state",
key: "shared/terraform.tfstate",
});
// Create environment-specific resources
const app = new aws.ecs.Service(`${environmentName}-app`, {
cluster: tfState.getOutput("cluster_name"),
taskDefinition: tfState.getOutput("task_definition_arn"),
desiredCount: environmentName === "production" ? 3 : 1,
});
return {
appArn: app.arn,
appName: app.name,
};
},
});
// Configure stack
await stack.setConfig("aws:region", { value: "us-west-2" });
// Deploy stack
const upResult = await stack.up({ onOutput: console.log });
return upResult.outputs;
}
// Usage
createEnvironmentStack("staging").then(outputs => {
console.log("Staging environment created:", outputs);
});
Component resources for reusability
Create reusable components that integrate with Terraform, behind the scenes, leaving the complexity of multi-tool management to your platform team, not your development teams:
// components/WebApplication.ts
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
import * as terraform from "@pulumi/terraform";
export interface WebApplicationArgs {
terraformInfrastructure: terraform.state.S3Reference;
containerImage: string;
environment: string;
desiredCount?: number;
}
export class WebApplication extends pulumi.ComponentResource {
public readonly service: aws.ecs.Service;
public readonly loadBalancer: aws.lb.LoadBalancer;
public readonly url: pulumi.Output<string>;
constructor(name: string, args: WebApplicationArgs, opts?: pulumi.ComponentResourceOptions) {
super("custom:WebApplication", name, {}, opts);
const defaultParent = { parent: this };
// Get infrastructure from Terraform
const clusterName = args.terraformInfrastructure.getOutput("cluster_name");
const vpcId = args.terraformInfrastructure.getOutput("vpc_id");
const subnetIds = args.terraformInfrastructure.getOutput("subnet_ids");
// Create load balancer
this.loadBalancer = new aws.lb.LoadBalancer(`${name}-alb`, {
name: `${name}-alb`,
loadBalancerType: "application",
subnets: subnetIds,
internal: false,
}, defaultParent);
// Create ECS service
const taskDefinition = new aws.ecs.TaskDefinition(`${name}-task`, {
family: name,
networkMode: "awsvpc",
requiresCompatibilities: ["FARGATE"],
cpu: "256",
memory: "512",
containerDefinitions: pulumi.interpolate`[{
"name": "${name}",
"image": "${args.containerImage}",
"portMappings": [{"containerPort": 80}],
"environment": [{"name": "ENV", "value": "${args.environment}"}]
}]`,
}, defaultParent);
this.service = new aws.ecs.Service(`${name}-service`, {
cluster: clusterName,
taskDefinition: taskDefinition.arn,
desiredCount: args.desiredCount || 1,
launchType: "FARGATE",
networkConfiguration: {
subnets: subnetIds,
assignPublicIp: true,
},
}, defaultParent);
this.url = pulumi.interpolate`http://${this.loadBalancer.dnsName}`;
this.registerOutputs({
service: this.service,
loadBalancer: this.loadBalancer,
url: this.url,
});
}
}
// Usage: developers create WebApplications in Pulumi, without knowing they depend on Terraform-managed resources
const app = new WebApplication("my-app", {
terraformInfrastructure: infrastructureState,
containerImage: "nginx:latest",
environment: "production",
desiredCount: 3,
});
Learning resources
Documentation and guides
- Pulumi Architecture & Concepts: Deep dive into Pulumi’s architecture
- Adopting Pulumi: Comprehensive migration strategies
- Pulumi vs Terraform: Detailed comparison of features
- Automation API: Programmatic infrastructure management
- Policy as Code: Infrastructure governance and compliance
Community resources
- Pulumi Community: Join the community slack and forum
- Examples Repository: Browse hundreds of real-world examples
- Pulumi Blog: Latest updates and case studies
- Pulumi YouTube Channel: Video tutorials and demos
Case studies and real-world implementations
- Snowflake’s Multi-Cloud Strategy: How Snowflake uses Pulumi with existing tools
- Mercedes-Benz’s Platform Engineering: Large-scale infrastructure modernization
- Lemonade’s Insurance Platform: Rapid development with Pulumi and Terraform
Getting help and Contributing to Pulumi
Reach out to us via these support channels:
- Pulumi Community Slack: Real-time community support
- GitHub Issues: Bug reports and feature requests
- Pulumi Support: Professional support for Pulumi Cloud customers
Open source contributions
We always welcome contributions, especially from our more advanced users who have practical experience building with both Terraform and Pulumi. Here are some ways you can contribute to the Pulumi ecosystem:
- Contribute to Pulumi: Help improve Pulumi
- Provider Development: Create new providers
- Examples and Templates: Share your patterns
Community engagement
- Pulumi Blog: Write about your experience
- Community Events: Speak at meetups and conferences
- User Groups: Join or start a local user group
Have questions or feedback about this guide? Join us in the Pulumi Community Slack or open an issue on GitHub.
Thank you for your feedback!
If you have a question about how to use Pulumi, reach out in Community Slack.
Open an issue on GitHub to report a problem or suggest an improvement.