From 0c751511c5b135444f3961afff185fa43b8ba9e8 Mon Sep 17 00:00:00 2001 From: Anton Tayanovskyy Date: Wed, 25 Jun 2025 13:35:34 -0400 Subject: [PATCH 01/12] Introduce choosables --- .../extending-pulumi/use-terraform-module.md | 52 +++++++++++++------ 1 file changed, 36 insertions(+), 16 deletions(-) diff --git a/content/docs/iac/extending-pulumi/use-terraform-module.md b/content/docs/iac/extending-pulumi/use-terraform-module.md index d4991a5aef0a..2ce5855936b9 100644 --- a/content/docs/iac/extending-pulumi/use-terraform-module.md +++ b/content/docs/iac/extending-pulumi/use-terraform-module.md @@ -117,6 +117,10 @@ packages: - rdsmod ``` +{{% chooser language "typescript,yaml" %}} + +{{% choosable language typescript %}} + Since this was a TypeScript project, Pulumi generated a TypeScript SDK for the modules, making those available to use as `@pulumi/vpcmod` and `@pulumi/rdsmod` respectively. We can now use the Terraform modules directly in our TypeScript code: ***Example:** index.ts - Using the Terraform VPC and RDS module in a Pulumi program* @@ -204,12 +208,41 @@ function getCidrSubnet(cidr: string, netnum: number): pulumi.Output { } ``` +{{% /choosable %}} + +{{% choosable language yaml %}} + +When authoring in YAML, there's no need for Pulumi to generate a SDK. In the YAML you can reference the Terraform module by its schema token, which takes the format `:index:Module`: + +**Example:** Pulumi.yaml - Using an imported Terraform module in a Pulumi YAML program* + +TODO expand this example to match TypeScript above. + +```yaml +resources: + my-rds: + type: rdsmod:index:Module + properties: + engine: mysql + identifier: my-rds-instance + manage_master_user_password: true + # other properties... +``` + +{{% /choosable %}} + +{{% /chooser %}} + In the above code, the imported Terraform module works the same as any other Pulumi code. Outputs are returned, and resource state is stored in your Pulumi state storage, alongside all your other Pulumi-native resources. This also means that resource dependencies work as expected between Pulumi-native resources and resources created by Terraform modules. ## Configuring Terraform Providers Some modules require Terraform providers to be configured with specific settings. You can configure these providers from within Pulumi: +{{% chooser language "typescript" %}} + +{{% choosable language typescript %}} + **Example:** index.ts - Configuring the imported Terraform bucket module* ```typescript @@ -228,24 +261,11 @@ const testBucket = new bucket.Module("test-bucket", { }, { provider: provider }); ``` -Provider configuration is module-specific, so refer to the module's documentation for available configuration options. - -## Using Modules with Pulumi YAML +{{% /choosable %}} -When authoring in YAML, there's no need for Pulumi to generate a SDK. In the YAML you can reference the Terraform module by its schema token, which takes the format `:index:Module`: +{{% /chooser %}} -**Example:** Pulumi.yaml - Using an imported Terraform module in a Pulumi YAML program* - -```yaml -resources: - my-rds: - type: rdsmod:index:Module - properties: - engine: mysql - identifier: my-rds-instance - manage_master_user_password: true - # other properties... -``` +Provider configuration is module-specific, so refer to the module's documentation for available configuration options. ## Troubleshooting From d81b4ca6fa8868071968d5f148a9cb0f7c1075c6 Mon Sep 17 00:00:00 2001 From: Anton Tayanovskyy Date: Wed, 25 Jun 2025 14:01:19 -0400 Subject: [PATCH 02/12] Add Python sources --- .../extending-pulumi/use-terraform-module.md | 84 ++++++++++++++++++- 1 file changed, 83 insertions(+), 1 deletion(-) diff --git a/content/docs/iac/extending-pulumi/use-terraform-module.md b/content/docs/iac/extending-pulumi/use-terraform-module.md index 2ce5855936b9..2248d8938e0d 100644 --- a/content/docs/iac/extending-pulumi/use-terraform-module.md +++ b/content/docs/iac/extending-pulumi/use-terraform-module.md @@ -117,7 +117,7 @@ packages: - rdsmod ``` -{{% chooser language "typescript,yaml" %}} +{{% chooser language "typescript,python,yaml" %}} {{% choosable language typescript %}} @@ -210,6 +210,88 @@ function getCidrSubnet(cidr: string, netnum: number): pulumi.Output { {{% /choosable %}} +{{% choosable language python %}} + +Since this was a Python project, Pulumi generated a Python SDK for the modules, making those available to use as `pulumi_vpcmod` and `pulumi_rdsmod` respectively. We can now use the Terraform modules directly in our code: + +```python +import pulumi +import pulumi_aws as aws +import pulumi_vpcmod as vpcmod +import pulumi_rdsmod as rdsmod +import pulumi_std as std + +# Get available availability zones +azs = aws.get_availability_zones_output( + filters=[{ + "name": "opt-in-status", + "values": ["opt-in-not-required"], + }] +).names.apply(lambda names: names[:3]) + +cidr = "10.0.0.0/16" + +cfg = pulumi.Config() +prefix = cfg.get("prefix") or pulumi.get_stack() + +# Utility function to calculate subnet CIDRs +def get_cidr_subnet(cidr, netnum): + return std.cidrsubnet_output( + input=cidr, + newbits=8, + netnum=netnum + ).result + +# Create a VPC using the terraform-aws-modules/vpc module +vpc = vpcmod.Module("test-vpc", + azs=azs, + name=f"test-vpc-{prefix}", + cidr=cidr, + public_subnets=azs.apply(lambda azs: [get_cidr_subnet(cidr, i+1) for i in range(len(azs))]), + private_subnets=azs.apply(lambda azs: [get_cidr_subnet(cidr, i+1+4) for i in range(len(azs))]), + database_subnets=azs.apply(lambda azs: [get_cidr_subnet(cidr, i+1+8) for i in range(len(azs))]), + create_database_subnet_group=True +) + +# Create a security group for the RDS instance +rds_security_group = aws.ec2.SecurityGroup('test-rds-sg', + vpc_id=vpc.vpc_id +) + +aws.vpc.SecurityGroupIngressRule('test-rds-sg-ingress', + ip_protocol='tcp', + security_group_id=rds_security_group.id, + cidr_ipv4=vpc.vpc_cidr_block, + from_port=3306, + to_port=3306 +) + +# Create an RDS instance using the terraform-aws-modules/rds module +rdsmod.Module("test-rds", + engine="mysql", + identifier=f"test-rds-{prefix}", + manage_master_user_password=True, + publicly_accessible=False, + allocated_storage=20, + max_allocated_storage=100, + instance_class="db.t4g.large", + engine_version="8.0", + family="mysql8.0", + db_name="completeMysql", + username="complete_mysql", + port='3306', + multi_az=True, + db_subnet_group_name=vpc.database_subnet_group_name, + vpc_security_group_ids=[rds_security_group.id], + skip_final_snapshot=True, + deletion_protection=False, + create_db_option_group=False, + create_db_parameter_group=False +) +``` + +{{% /choosable %}} + {{% choosable language yaml %}} When authoring in YAML, there's no need for Pulumi to generate a SDK. In the YAML you can reference the Terraform module by its schema token, which takes the format `:index:Module`: From c15d47c53246703e4c66c7f19cc24264e26b08d8 Mon Sep 17 00:00:00 2001 From: Anton Tayanovskyy Date: Wed, 25 Jun 2025 14:32:03 -0400 Subject: [PATCH 03/12] Go version --- .../extending-pulumi/use-terraform-module.md | 132 +++++++++++++++++- 1 file changed, 131 insertions(+), 1 deletion(-) diff --git a/content/docs/iac/extending-pulumi/use-terraform-module.md b/content/docs/iac/extending-pulumi/use-terraform-module.md index 2248d8938e0d..d42b7f33053d 100644 --- a/content/docs/iac/extending-pulumi/use-terraform-module.md +++ b/content/docs/iac/extending-pulumi/use-terraform-module.md @@ -121,7 +121,7 @@ packages: {{% choosable language typescript %}} -Since this was a TypeScript project, Pulumi generated a TypeScript SDK for the modules, making those available to use as `@pulumi/vpcmod` and `@pulumi/rdsmod` respectively. We can now use the Terraform modules directly in our TypeScript code: +Since this was a TypeScript project, Pulumi generated a TypeScript SDK for the modules, making those available to use as `@pulumi/vpcmod` and `@pulumi/rdsmod` respectively. We can now use the Terraform modules directly in our TypeScript code. ***Example:** index.ts - Using the Terraform VPC and RDS module in a Pulumi program* @@ -292,6 +292,136 @@ rdsmod.Module("test-rds", {{% /choosable %}} +{{% choosable language go %}} + +Since this was a Go project, Pulumi generated a Go SDK for the modules, making those available to use as `github.com/pulumi/pulumi-terraform-module/sdks/go/rdsmod/v6/rdsmod` and `github.com/pulumi/pulumi-terraform-module/sdks/go/vpcmod/v5/vpcmod`. We can now use the Terraform modules directly in our code: + +```go +package main + +import ( + "github.com/pulumi/pulumi-aws/sdk/v6/go/aws" + "github.com/pulumi/pulumi-aws/sdk/v6/go/aws/ec2" + "github.com/pulumi/pulumi-aws/sdk/v6/go/aws/vpc" + "github.com/pulumi/pulumi-std/sdk/go/std" + rdsmod "github.com/pulumi/pulumi-terraform-module/sdks/go/rdsmod/v6/rdsmod" + vpcmod "github.com/pulumi/pulumi-terraform-module/sdks/go/vpcmod/v5/vpcmod" + "github.com/pulumi/pulumi/sdk/v3/go/pulumi" + "github.com/pulumi/pulumi/sdk/v3/go/pulumi/config" +) + +func run(ctx *pulumi.Context) error { + // Get available availability zones + azs := aws.GetAvailabilityZonesOutput(ctx, aws.GetAvailabilityZonesOutputArgs{ + Filters: aws.GetAvailabilityZonesFilterArray{ + aws.GetAvailabilityZonesFilterArgs{ + Name: pulumi.String("opt-in-status"), + Values: pulumi.StringArray{pulumi.String("opt-in-not-required")}, + }, + }, + }) + + azNames := azs.Names().ApplyT(func(names []string) []string { + if len(names) > 3 { + return names[:3] + } + return names + }).(pulumi.StringArrayOutput) + + cidr := "10.0.0.0/16" + cfg := config.New(ctx, "") + prefix := cfg.Get("prefix") + if prefix == "" { + prefix = ctx.Stack() + } + + // Create a VPC using the terraform-aws-modules/vpc module + vpcInstance, err := vpcmod.NewModule(ctx, "test-vpc", &vpcmod.ModuleArgs{ + Azs: azNames, + Name: pulumi.Sprintf("test-vpc-%s", prefix), + Cidr: pulumi.String(cidr), + Public_subnets: applyAznamesForSubnet(ctx, azNames, cidr, 1), + Private_subnets: applyAznamesForSubnet(ctx, azNames, cidr, 5), + Database_subnets: applyAznamesForSubnet(ctx, azNames, cidr, 9), + Create_database_subnet_group: pulumi.Bool(true), + }) + if err != nil { + return err + } + + // Create a security group for the RDS instance + rdsSecurityGroup, err := ec2.NewSecurityGroup(ctx, "test-rds-sg", &ec2.SecurityGroupArgs{ + VpcId: vpcInstance.Vpc_id, + }) + if err != nil { + return err + } + _, err = vpc.NewSecurityGroupIngressRule(ctx, "test-rds-sg-ingress", &vpc.SecurityGroupIngressRuleArgs{ + IpProtocol: pulumi.String("tcp"), + SecurityGroupId: rdsSecurityGroup.ID(), + CidrIpv4: vpcInstance.Vpc_cidr_block, + FromPort: pulumi.Int(3306), + ToPort: pulumi.Int(3306), + }) + if err != nil { + return err + } + + // Create an RDS instance using the terraform-aws-modules/rds module + _, err = rdsmod.NewModule(ctx, "test-rds", &rdsmod.ModuleArgs{ + Engine: pulumi.String("mysql"), + Identifier: pulumi.Sprintf("test-rds-%s", prefix), + Manage_master_user_password: pulumi.Bool(true), + Publicly_accessible: pulumi.Bool(false), + Allocated_storage: pulumi.Float64(20), + Max_allocated_storage: pulumi.Float64(100), + Instance_class: pulumi.String("db.t4g.large"), + Engine_version: pulumi.String("8.0"), + Family: pulumi.String("mysql8.0"), + Db_name: pulumi.String("completeMysql"), + Username: pulumi.String("complete_mysql"), + Port: pulumi.String("3306"), + Multi_az: pulumi.Bool(true), + Db_subnet_group_name: vpcInstance.Database_subnet_group_name, + Vpc_security_group_ids: pulumi.StringArray{rdsSecurityGroup.ID()}, + Skip_final_snapshot: pulumi.Bool(true), + Deletion_protection: pulumi.Bool(false), + Create_db_option_group: pulumi.Bool(false), + Create_db_parameter_group: pulumi.Bool(false), + }) + return err +} + +func applyAznamesForSubnet( + ctx *pulumi.Context, + azNames pulumi.StringArrayOutput, + cidr string, + offset int, +) pulumi.StringArrayOutput { + return azNames.ApplyT(func(azs []string) ([]string, error) { + subnets := make([]string, len(azs)) + for i := range azs { + netnum := offset + i + r, err := std.Cidrsubnet(ctx, &std.CidrsubnetArgs{ + Input: cidr, + Newbits: 8, + Netnum: netnum, + }) + if err != nil { + return nil, err + } + subnets[i] = r.Result + } + return subnets, nil + }).(pulumi.StringArrayOutput) +} + +func main() { + pulumi.Run(run) +} +``` +{{% /choosable %}} + {{% choosable language yaml %}} When authoring in YAML, there's no need for Pulumi to generate a SDK. In the YAML you can reference the Terraform module by its schema token, which takes the format `:index:Module`: From ff26a54afec1738aab7ee5efc12c2690c59b21d1 Mon Sep 17 00:00:00 2001 From: Anton Tayanovskyy Date: Wed, 25 Jun 2025 15:08:23 -0400 Subject: [PATCH 04/12] C# translation --- .../extending-pulumi/use-terraform-module.md | 109 +++++++++++++++++- 1 file changed, 108 insertions(+), 1 deletion(-) diff --git a/content/docs/iac/extending-pulumi/use-terraform-module.md b/content/docs/iac/extending-pulumi/use-terraform-module.md index d42b7f33053d..e7683a688a67 100644 --- a/content/docs/iac/extending-pulumi/use-terraform-module.md +++ b/content/docs/iac/extending-pulumi/use-terraform-module.md @@ -117,7 +117,7 @@ packages: - rdsmod ``` -{{% chooser language "typescript,python,yaml" %}} +{{% chooser language "typescript,python,csharp,yaml" %}} {{% choosable language typescript %}} @@ -422,6 +422,113 @@ func main() { ``` {{% /choosable %}} +{{% choosable language csharp %}} + +Since this was a C# project, Pulumi generated a C# SDK for the modules, making those available to use as `Pulumi.Rdsmod` and `Pulumi.Vpcmod`. We can now use the Terraform modules directly in our code: + +```csharp +using System; +using System.Linq; +using System.Threading.Tasks; +using System.Collections.Generic; +using System.Collections.Immutable; +using Pulumi; +using Aws = Pulumi.Aws; +using Rdsmod = Pulumi.Rdsmod; +using Std = Pulumi.Std; +using Vpcmod = Pulumi.Vpcmod; + +return await Deployment.RunAsync(() => +{ +// Get available availability zones + var azs = Aws.GetAvailabilityZones.Invoke(new Aws.GetAvailabilityZonesInvokeArgs + { + Filters = + { + new Aws.Inputs.GetAvailabilityZonesFilterInputArgs + { + Name = "opt-in-status", + Values = { "opt-in-not-required" } + } + } + }).Apply(result => result.Names.Take(3).ToArray()); + + var cidr = "10.0.0.0/16"; + + var config = new Pulumi.Config(); + var prefix = config.Get("prefix") ?? Deployment.Instance.StackName; + + // Create a VPC using the terraform-aws-modules/vpc module + var vpc = new Vpcmod.Module("test-vpc", new Vpcmod.ModuleArgs + { + Azs = azs, + Name = Output.Format($"test-vpc-{prefix}"), + Cidr = cidr, + Public_subnets = Utils.Subnets(cidr, azs, 1), + Private_subnets = Utils.Subnets(cidr, azs, 5), + Database_subnets = Utils.Subnets(cidr, azs, 9), + Create_database_subnet_group = true, + }); + + // Create a security group for the RDS instance + var rdsSecurityGroup = new Aws.Ec2.SecurityGroup("test-rds-sg", new Aws.Ec2.SecurityGroupArgs + { + VpcId = vpc.Vpc_id.Apply(id => id ?? string.Empty), + }); + + _ = new Aws.Vpc.SecurityGroupIngressRule("test-rds-sg-ingress", new Aws.Vpc.SecurityGroupIngressRuleArgs + { + IpProtocol = "tcp", + SecurityGroupId = rdsSecurityGroup.Id, + CidrIpv4 = vpc.Vpc_cidr_block.Apply(x => x!), + FromPort = 3306, + ToPort = 3306, + }); + + // Create an RDS instance using the terraform-aws-modules/rds module + _ = new Rdsmod.Module("test-rds", new Rdsmod.ModuleArgs + { + Engine = "mysql", + Identifier = Output.Format($"test-rds-{prefix}"), + Manage_master_user_password = true, + Publicly_accessible = false, + Allocated_storage = 20, + Max_allocated_storage = 100, + Instance_class = "db.t4g.large", + Engine_version = "8.0", + Family = "mysql8.0", + Db_name = "completeMysql", + Username = "complete_mysql", + Port = "3306", + Multi_az = true, + Db_subnet_group_name = vpc.Database_subnet_group_name, + Vpc_security_group_ids = { rdsSecurityGroup.Id }, + Skip_final_snapshot = true, + Deletion_protection = false, + Create_db_option_group = false, + Create_db_parameter_group = false, + }); +}); + +// Utilities to calculate subnet CIDRs +internal class Utils { + public static Output> Subnets(string cidr, Output azs, int offset) { + return azs.Apply(names => Pulumi.Output.All(names.Select((_, i) => Utils.GetCidrSubnet(cidr, i + 1)))); + } + + public static Output GetCidrSubnet(string cidr, int netnum) + { + return Std.Cidrsubnet.Invoke(new Std.CidrsubnetInvokeArgs + { + Input = cidr, + Newbits = 8, + Netnum = netnum + }).Apply(result => result.Result); + } +} +``` +{{% /choosable %}} + {{% choosable language yaml %}} When authoring in YAML, there's no need for Pulumi to generate a SDK. In the YAML you can reference the Terraform module by its schema token, which takes the format `:index:Module`: From 2483c55578564795e549dae24ad96a81066ddd7d Mon Sep 17 00:00:00 2001 From: Anton Tayanovskyy Date: Wed, 25 Jun 2025 16:20:02 -0400 Subject: [PATCH 05/12] Java sources --- .../extending-pulumi/use-terraform-module.md | 107 ++++++++++++++++++ 1 file changed, 107 insertions(+) diff --git a/content/docs/iac/extending-pulumi/use-terraform-module.md b/content/docs/iac/extending-pulumi/use-terraform-module.md index e7683a688a67..1877f5bb9d09 100644 --- a/content/docs/iac/extending-pulumi/use-terraform-module.md +++ b/content/docs/iac/extending-pulumi/use-terraform-module.md @@ -529,6 +529,113 @@ internal class Utils { ``` {{% /choosable %}} +{{% choosable language java %}} + +Since this was a Java project, Pulumi generated a Java SDK for the modules, making those available to use as `com.pulumi.rdsmod` and `com.pulumi.vpcmod`. We can now use the Terraform modules directly in our code: + +```java +package myproject; + +import com.pulumi.Context; +import com.pulumi.Pulumi; +import com.pulumi.core.Output; +import com.pulumi.aws.AwsFunctions; +import com.pulumi.aws.inputs.GetAvailabilityZonesArgs; +import com.pulumi.aws.inputs.GetAvailabilityZonesFilterArgs; +import com.pulumi.std.StdFunctions; +import com.pulumi.aws.ec2.SecurityGroup; +import com.pulumi.aws.ec2.SecurityGroupArgs; +import com.pulumi.aws.vpc.SecurityGroupIngressRule; +import com.pulumi.aws.vpc.SecurityGroupIngressRuleArgs; +import com.pulumi.Config; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import java.util.List; +import java.util.Collections; + +public class App { + public static void stack(Context ctx) { + + // Get available availability zones + final var azNames = AwsFunctions.getAvailabilityZones(GetAvailabilityZonesArgs.builder() + .filters(GetAvailabilityZonesFilterArgs.builder() + .name("opt-in-status") + .values("opt-in-not-required") + .build()) + .build()) + .applyValue(result -> result.names().subList(0, 3)); + + final var cidr = "10.0.0.0/16"; + final var prefix = ctx.config().get("prefix").orElse(ctx.stackName()); + + // Create a VPC using the terraform-aws-modules/vpc module + final var vpc = new com.pulumi.vpcmod.Module("test-vpc", com.pulumi.vpcmod.ModuleArgs.builder() + .azs(azNames) + .name("test-vpc-" + prefix) + .cidr(cidr) + .public_subnets(subnets(cidr, azNames, 1)) + .private_subnets(subnets(cidr, azNames, 5)) + .database_subnets(subnets(cidr, azNames, 9)) + .create_database_subnet_group(true) + .build()); + + final var rdsSecurityGroup = new SecurityGroup("test-rds-sg", SecurityGroupArgs.builder() + .vpcId(vpc.vpc_id().applyValue(x -> x.get())) + .build()); + + new SecurityGroupIngressRule("test-rds-sg-ingress", SecurityGroupIngressRuleArgs.builder() + .ipProtocol("tcp") + .securityGroupId(rdsSecurityGroup.id()) + .cidrIpv4(vpc.vpc_cidr_block().applyValue(x -> x.get())) + .fromPort(3306) + .toPort(3306) + .build()); + + new com.pulumi.rdsmod.Module("test-rds", com.pulumi.rdsmod.ModuleArgs.builder() + .engine("mysql") + .identifier("test-rds-" + prefix) + .manage_master_user_password(true) + .publicly_accessible(false) + .allocated_storage(20.0) + .max_allocated_storage(100.0) + .instance_class("db.t4g.large") + .engine_version("8.0") + .family("mysql8.0") + .db_name("completeMysql") + .username("complete_mysql") + .port("3306") + .multi_az(true) + .db_subnet_group_name(vpc.database_subnet_group_name().applyValue(x -> x.get())) + .vpc_security_group_ids(rdsSecurityGroup.id().applyValue(x -> Collections.singletonList(x))) + .skip_final_snapshot(true) + .deletion_protection(false) + .create_db_option_group(false) + .create_db_parameter_group(false) + .build()); + } + + private static Output> subnets(String cidr, Output> azNames, int offset) { + return azNames.apply(names -> Output.all(IntStream.range(0, names.size()) + .mapToObj(i -> getCidrSubnet(cidr, i+offset)) + .collect(Collectors.toList()))); + } + + private static Output getCidrSubnet(String cidr, int netnum) { + return StdFunctions.cidrsubnet(com.pulumi.std.inputs.CidrsubnetArgs.builder() + .input(cidr) + .newbits(8) + .netnum(netnum) + .build()).applyValue(x -> x.result()); + } + + public static void main(String[] args) { + Pulumi.run(App::stack); + } +} +``` + +{{% /choosable %}} + {{% choosable language yaml %}} When authoring in YAML, there's no need for Pulumi to generate a SDK. In the YAML you can reference the Terraform module by its schema token, which takes the format `:index:Module`: From 5fd89b934d024949bd3aebcade3040e722e3efc9 Mon Sep 17 00:00:00 2001 From: Anton Tayanovskyy Date: Wed, 25 Jun 2025 16:22:06 -0400 Subject: [PATCH 06/12] Fix lines around ``` blocks --- content/docs/iac/extending-pulumi/use-terraform-module.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/content/docs/iac/extending-pulumi/use-terraform-module.md b/content/docs/iac/extending-pulumi/use-terraform-module.md index 1877f5bb9d09..8993fd8aca28 100644 --- a/content/docs/iac/extending-pulumi/use-terraform-module.md +++ b/content/docs/iac/extending-pulumi/use-terraform-module.md @@ -420,6 +420,7 @@ func main() { pulumi.Run(run) } ``` + {{% /choosable %}} {{% choosable language csharp %}} @@ -527,6 +528,7 @@ internal class Utils { } } ``` + {{% /choosable %}} {{% choosable language java %}} From a4de396dc02ea05b5f0d7e6eb8bda1a013f4119c Mon Sep 17 00:00:00 2001 From: Anton Tayanovskyy Date: Wed, 25 Jun 2025 16:46:57 -0400 Subject: [PATCH 07/12] YAML sources --- .../extending-pulumi/use-terraform-module.md | 92 +++++++++++++++++-- 1 file changed, 83 insertions(+), 9 deletions(-) diff --git a/content/docs/iac/extending-pulumi/use-terraform-module.md b/content/docs/iac/extending-pulumi/use-terraform-module.md index 8993fd8aca28..4db00442e95b 100644 --- a/content/docs/iac/extending-pulumi/use-terraform-module.md +++ b/content/docs/iac/extending-pulumi/use-terraform-module.md @@ -92,7 +92,7 @@ $ pulumi package add terraform-module terraform-aws-modules/rds/aws 6.10.0 rdsmo After adding the packages, your `Pulumi.yaml` will be updated, and any necessary dependencies will be added to your project. -***Example:** Pulumi.yaml* +**Example:** Pulumi.yaml* ```yaml name: rds-example @@ -117,13 +117,13 @@ packages: - rdsmod ``` -{{% chooser language "typescript,python,csharp,yaml" %}} +{{% chooser language "typescript,python,csharp,java,yaml" %}} {{% choosable language typescript %}} Since this was a TypeScript project, Pulumi generated a TypeScript SDK for the modules, making those available to use as `@pulumi/vpcmod` and `@pulumi/rdsmod` respectively. We can now use the Terraform modules directly in our TypeScript code. -***Example:** index.ts - Using the Terraform VPC and RDS module in a Pulumi program* +**Example:** index.ts - Using the Terraform VPC and RDS module in a Pulumi program* ```typescript import * as vpcmod from '@pulumi/vpcmod'; @@ -214,6 +214,8 @@ function getCidrSubnet(cidr: string, netnum: number): pulumi.Output { Since this was a Python project, Pulumi generated a Python SDK for the modules, making those available to use as `pulumi_vpcmod` and `pulumi_rdsmod` respectively. We can now use the Terraform modules directly in our code: +**Example:** `__main__.py` - Using the Terraform VPC and RDS module in a Pulumi program + ```python import pulumi import pulumi_aws as aws @@ -294,6 +296,8 @@ rdsmod.Module("test-rds", {{% choosable language go %}} +**Example:** `main.go` - Using the Terraform VPC and RDS module in a Pulumi program + Since this was a Go project, Pulumi generated a Go SDK for the modules, making those available to use as `github.com/pulumi/pulumi-terraform-module/sdks/go/rdsmod/v6/rdsmod` and `github.com/pulumi/pulumi-terraform-module/sdks/go/vpcmod/v5/vpcmod`. We can now use the Terraform modules directly in our code: ```go @@ -427,6 +431,8 @@ func main() { Since this was a C# project, Pulumi generated a C# SDK for the modules, making those available to use as `Pulumi.Rdsmod` and `Pulumi.Vpcmod`. We can now use the Terraform modules directly in our code: +**Example:** `Program.cs` - Using the Terraform VPC and RDS module in a Pulumi program + ```csharp using System; using System.Linq; @@ -535,6 +541,8 @@ internal class Utils { Since this was a Java project, Pulumi generated a Java SDK for the modules, making those available to use as `com.pulumi.rdsmod` and `com.pulumi.vpcmod`. We can now use the Terraform modules directly in our code: +**Example:** `App.java` - Using the Terraform VPC and RDS module in a Pulumi program + ```java package myproject; @@ -642,19 +650,85 @@ public class App { When authoring in YAML, there's no need for Pulumi to generate a SDK. In the YAML you can reference the Terraform module by its schema token, which takes the format `:index:Module`: -**Example:** Pulumi.yaml - Using an imported Terraform module in a Pulumi YAML program* - -TODO expand this example to match TypeScript above. +**Example:** `Pulumi.yaml` - Using the Terraform VPC and RDS module in a Pulumi program ```yaml +name: testproj-yaml +description: testproj-yaml +runtime: yaml resources: - my-rds: + testVpc: + type: vpcmod:index:Module + properties: + name: test-vpc-${pulumi.stack} + azs: + - us-west-2a + - us-west-2b + - us-west-2c + cidr: 10.0.0.0/16 + public_subnets: + - 10.0.1.0/24 + - 10.0.2.0/24 + - 10.0.3.0/24 + private_subnets: + - 10.0.5.0/24 + - 10.0.6.0/24 + - 10.0.7.0/24 + database_subnets: + - 10.0.9.0/24 + - 10.0.10.0/24 + - 10.0.11.0/24 + create_database_subnet_group: true + testRdsSg: + type: aws:ec2:SecurityGroup + properties: + vpcId: ${testVpc.vpc_id} + testRdsSgIngress: + type: aws:vpc:SecurityGroupIngressRule + properties: + ipProtocol: tcp + securityGroupId: ${testRdsSg.id} + cidrIpv4: ${testVpc.vpc_cidr_block} + fromPort: 3306 + toPort: 3306 + testRds: type: rdsmod:index:Module properties: engine: mysql - identifier: my-rds-instance + identifier: test-rds-${pulumi.stack} manage_master_user_password: true - # other properties... + publicly_accessible: false + allocated_storage: 20 + max_allocated_storage: 100 + instance_class: db.t4g.large + engine_version: 8 + family: mysql8.0 + db_name: completeMysql + username: complete_mysql + port: '3306' + multi_az: true + db_subnet_group_name: ${testVpc.database_subnet_group_name} + vpc_security_group_ids: + - ${testRdsSg.id} + skip_final_snapshot: true + deletion_protection: false + create_db_option_group: false + create_db_parameter_group: false +packages: + rdsmod: + source: terraform-module + version: 0.1.7 + parameters: + - terraform-aws-modules/rds/aws + - 6.10.0 + - rdsmod + vpcmod: + source: terraform-module + version: 0.1.7 + parameters: + - terraform-aws-modules/vpc/aws + - 5.19.0 + - vpcmod ``` {{% /choosable %}} From 42bcbc1ad29d0a3ea3026b3d46cf9c1a6803ee6a Mon Sep 17 00:00:00 2001 From: Anton Tayanovskyy Date: Wed, 25 Jun 2025 16:47:50 -0400 Subject: [PATCH 08/12] Fix indent --- content/docs/iac/extending-pulumi/use-terraform-module.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/docs/iac/extending-pulumi/use-terraform-module.md b/content/docs/iac/extending-pulumi/use-terraform-module.md index 4db00442e95b..3cf8257a7e94 100644 --- a/content/docs/iac/extending-pulumi/use-terraform-module.md +++ b/content/docs/iac/extending-pulumi/use-terraform-module.md @@ -447,7 +447,7 @@ using Vpcmod = Pulumi.Vpcmod; return await Deployment.RunAsync(() => { -// Get available availability zones + // Get available availability zones var azs = Aws.GetAvailabilityZones.Invoke(new Aws.GetAvailabilityZonesInvokeArgs { Filters = From 3e63643a6b141445bb28f8a09ecaa22d167fd0e5 Mon Sep 17 00:00:00 2001 From: Anton Tayanovskyy Date: Wed, 25 Jun 2025 19:42:32 -0400 Subject: [PATCH 09/12] More Python, Go, C#, Java --- .../extending-pulumi/use-terraform-module.md | 128 +++++++++++++++++- 1 file changed, 126 insertions(+), 2 deletions(-) diff --git a/content/docs/iac/extending-pulumi/use-terraform-module.md b/content/docs/iac/extending-pulumi/use-terraform-module.md index 3cf8257a7e94..4a1b5b68ddbc 100644 --- a/content/docs/iac/extending-pulumi/use-terraform-module.md +++ b/content/docs/iac/extending-pulumi/use-terraform-module.md @@ -741,11 +741,11 @@ In the above code, the imported Terraform module works the same as any other Pul Some modules require Terraform providers to be configured with specific settings. You can configure these providers from within Pulumi: -{{% chooser language "typescript" %}} +{{% chooser language "typescript,python,go,csharp,java" %}} {{% choosable language typescript %}} -**Example:** index.ts - Configuring the imported Terraform bucket module* +**Example:** `index.ts` - Configuring the imported Terraform bucket module ```typescript import * as bucket from "@pulumi/bucket"; @@ -765,6 +765,130 @@ const testBucket = new bucket.Module("test-bucket", { {{% /choosable %}} +{{% choosable language python %}} + +**Example:** `__main__.py` - Configuring the imported Terraform bucket module + +```python +import pulumi +import pulumi_bucket as bucket + +# Configure the AWS provider for the module +provider = bucket.Provider("bucket-provider", aws={ + "region": "us-west-2" +}) + +# Use the provider with the module +test_bucket = bucket.Module("test-bucket", + bucket=f"${prefix}-test-bucket" + opts=pulumi.ResourceOptions(provider=provider) +) +``` + +{{% /choosable %}} + +{{% choosable language go %}} + +**Example:** `main.go` - Configuring the imported Terraform bucket module + +```go +package main + +import ( + bucket "github.com/pulumi/pulumi-terraform-module/sdks/go/bucket/v3/bucket" + "github.com/pulumi/pulumi/sdk/v3/go/pulumi" +) + + +func run(ctx *pulumi.Context) error { + // Configure the AWS provider for the module + prov, err := bucket.NewProvider(ctx, "provider", &bucket.ProviderArgs{ + Aws: pulumi.ToMap(map[string]any{ + "region": "us-west-2", + }), + }) + if err != nil { + return err + } + + // Use the provider with the module + bucketInstance, err := bucket.NewModule(ctx, "test-bucket", &bucket.ModuleArgs{ + Bucket: pulumi.Sprintf("test-vpc-%s", prefix), + }, pulumi.Provider(prov)) + if err != nil { + return err + } +} + +func main() { + pulumi.Run(run) +} +``` + +{{% /choosable %}} + +{{% choosable language csharp %}} + +**Example:** `Program.cs` - Configuring the imported Terraform bucket module + +```csharp +using Pulumi; +using Bucket = Pulumi.Bucket; + +return await Deployment.RunAsync(() => +{ + // Configure the AWS provider for the module + var provider = new Bucket.Provider("test-provider", new Bucket.ProviderArgs + { + Aws = {{"region", "us-west-2"}} + }); + + // Use the provider with the module + var bucket = new Bucket.Module("test-bucket", new Bucket.Args + { + Bucket = $"{prefix}-test-bucket" + }, new CustomResourceOptions + { + Provider = provider + }); +``` + +{{% /choosable %}} + +{{% choosable language java %}} + +**Example:** `App.java` - Configuring the imported Terraform bucket module + +```java +import com.pulumi.Context; +import com.pulumi.Pulumi; +import com.pulumi.resources.CustomResourceOptions; + +public class App { + public static void stack(Context ctx) { + + // Configure the AWS provider for the module + final var provider = new com.pulumi.bucket.Provider("test-provider", + com.pulumi.bucket.ProviderArgs.builder() + .aws(Collections.singletonMap("region", "us-west-2")) + .build()); + + // Use the provider with the module + final var bucket = new com.pulumi.bucket.Module("test-bucket", + com.pulumi.bucket.ModuleArgs.builder() + .bucket(prefix+"-test-bucket") + .build(), + CustomResourceOptions.builder().provider(provider).build()); + } + + public static void main(String[] args) { + Pulumi.run(App::stack); + } +} +``` + +{{% /choosable %}} + {{% /chooser %}} Provider configuration is module-specific, so refer to the module's documentation for available configuration options. From 3caf269e8914b804c479de0eecd0b576813dadb8 Mon Sep 17 00:00:00 2001 From: Anton Tayanovskyy Date: Wed, 25 Jun 2025 19:45:06 -0400 Subject: [PATCH 10/12] Restore go pane --- content/docs/iac/extending-pulumi/use-terraform-module.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/docs/iac/extending-pulumi/use-terraform-module.md b/content/docs/iac/extending-pulumi/use-terraform-module.md index 4a1b5b68ddbc..2dc4e9396490 100644 --- a/content/docs/iac/extending-pulumi/use-terraform-module.md +++ b/content/docs/iac/extending-pulumi/use-terraform-module.md @@ -117,7 +117,7 @@ packages: - rdsmod ``` -{{% chooser language "typescript,python,csharp,java,yaml" %}} +{{% chooser language "typescript,python,go,csharp,java,yaml" %}} {{% choosable language typescript %}} From 54981df2b6b2cabf2ddd2614a674b627ce690ce7 Mon Sep 17 00:00:00 2001 From: Anton Tayanovskyy Date: Thu, 26 Jun 2025 10:31:32 -0400 Subject: [PATCH 11/12] Indentation fixes --- content/docs/iac/extending-pulumi/use-terraform-module.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/content/docs/iac/extending-pulumi/use-terraform-module.md b/content/docs/iac/extending-pulumi/use-terraform-module.md index 2dc4e9396490..260d2c5f7de5 100644 --- a/content/docs/iac/extending-pulumi/use-terraform-module.md +++ b/content/docs/iac/extending-pulumi/use-terraform-module.md @@ -801,7 +801,7 @@ import ( func run(ctx *pulumi.Context) error { - // Configure the AWS provider for the module + // Configure the AWS provider for the module prov, err := bucket.NewProvider(ctx, "provider", &bucket.ProviderArgs{ Aws: pulumi.ToMap(map[string]any{ "region": "us-west-2", @@ -811,7 +811,7 @@ func run(ctx *pulumi.Context) error { return err } - // Use the provider with the module + // Use the provider with the module bucketInstance, err := bucket.NewModule(ctx, "test-bucket", &bucket.ModuleArgs{ Bucket: pulumi.Sprintf("test-vpc-%s", prefix), }, pulumi.Provider(prov)) From 8c80161579f1f0832ab1b43914ab19b6846a2eba Mon Sep 17 00:00:00 2001 From: Anton Tayanovskyy Date: Thu, 26 Jun 2025 10:37:55 -0400 Subject: [PATCH 12/12] Explain YAML generated metadata --- .../docs/iac/extending-pulumi/use-terraform-module.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/content/docs/iac/extending-pulumi/use-terraform-module.md b/content/docs/iac/extending-pulumi/use-terraform-module.md index 260d2c5f7de5..c3b4a8d95b3b 100644 --- a/content/docs/iac/extending-pulumi/use-terraform-module.md +++ b/content/docs/iac/extending-pulumi/use-terraform-module.md @@ -648,7 +648,14 @@ public class App { {{% choosable language yaml %}} -When authoring in YAML, there's no need for Pulumi to generate a SDK. In the YAML you can reference the Terraform module by its schema token, which takes the format `:index:Module`: +When authoring in YAML, there is no need for Pulumi to generate a SDK. Pulumi generates some metadata instead: + +```bash +$ ls sdks/vpcmod/ +sdks/vpcmod/vpcmod-5.19.0.yaml +``` + +In the YAML you can reference the Terraform module by its schema token, which takes the format `:index:Module`: **Example:** `Pulumi.yaml` - Using the Terraform VPC and RDS module in a Pulumi program