Pulumi AWS Provider 7.0: Multi-Region Support, IAM Role Chaining, and S3 Resource Simplification

Posted on

Pulumi AWS provider 7.0 is here with powerful new capabilities that simplify and scale infrastructure as code on AWS. As the most widely used provider in the Pulumi ecosystem, it offers access to the full surface area of the upstream Terraform AWS Provider in Pulumi projects in all supported languages, like TypeScript, Python, Go, C#, Java, and YAML.

The 7.0 release brings fixes and improvements to the provider, including several breaking changes as part of the major version release.

What’s New in Pulumi AWS Provider 7.0

Multi-Region AWS Deployments with a Single Provider

Previously the Pulumi AWS provider each provider configuration could target a single AWS region. This meant that users would need to create a separate provider configuration for each region they wanted to deploy resources to. As the number of providers configured in an application grew, it could sometimes lead to increased memory usage.

With the 7.0 release, the AWS provider now supports multiple regions with a single provider. Each resource now has a region input property that allows the user to specify the region at the resource level, while still utilizing the same provider.

Some key highlights of this feature include:

  • Single provider configuration: This reduces the need to load multiple instances of the AWS provider, lowering memory usage.
  • Region input property: The region input property is added to all resources (except global resources)
  • Resource import enhancements: A new @<regionID> suffix allows importing resources from different regions

This example shows how to use the new region input property to configure cross-region VPC peering.

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

const provider = new aws.Provider("provider", {region: "us-east-1"});
const mainVpc = new aws.ec2.Vpc("mainVpc", {cidrBlock: "10.0.0.0/16"}, {
    provider: provider,
});
const peerVpc = new aws.ec2.Vpc("peerVpc", {cidrBlock: "10.1.0.0/16"}, {
    provider: provider,
});
const peerConnection = new aws.ec2.VpcPeeringConnection("peerConnection", {
    vpcId: mainVpc.id,
    peerVpcId: peerVpc.id,
    peerRegion: "us-west-2",
    autoAccept: false,
}, {
    provider: provider,
});
const peerAcceptor = new aws.ec2.VpcPeeringConnectionAccepter("peerAcceptor", {
    region: "us-west-2",
    vpcPeeringConnectionId: peerConnection.id,
    autoAccept: true,
}, {
    provider: provider,
});
import pulumi
import pulumi_aws as aws

provider = aws.Provider("provider", region="us-east-1")
main_vpc = aws.ec2.Vpc("mainVpc", cidr_block="10.0.0.0/16",
opts = pulumi.ResourceOptions(provider=provider))
peer_vpc = aws.ec2.Vpc("peerVpc", cidr_block="10.1.0.0/16",
opts = pulumi.ResourceOptions(provider=provider))
peer_connection = aws.ec2.VpcPeeringConnection("peerConnection",
    vpc_id=main_vpc.id,
    peer_vpc_id=peer_vpc.id,
    peer_region="us-west-2",
    auto_accept=False,
    opts = pulumi.ResourceOptions(provider=provider))
peer_acceptor = aws.ec2.VpcPeeringConnectionAccepter("peerAcceptor",
    region="us-west-2",
    vpc_peering_connection_id=peer_connection.id,
    auto_accept=True,
    opts = pulumi.ResourceOptions(provider=provider))
package main

import (
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws"
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/ec2"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		provider, err := aws.NewProvider(ctx, "provider", &aws.ProviderArgs{
			Region: pulumi.String("us-east-1"),
		})
		if err != nil {
			return err
		}
		mainVpc, err := ec2.NewVpc(ctx, "mainVpc", &ec2.VpcArgs{
			CidrBlock: pulumi.String("10.0.0.0/16"),
		}, pulumi.Provider(provider))
		if err != nil {
			return err
		}
		peerVpc, err := ec2.NewVpc(ctx, "peerVpc", &ec2.VpcArgs{
			CidrBlock: pulumi.String("10.1.0.0/16"),
		}, pulumi.Provider(provider))
		if err != nil {
			return err
		}
		peerConnection, err := ec2.NewVpcPeeringConnection(ctx, "peerConnection", &ec2.VpcPeeringConnectionArgs{
			VpcId:      mainVpc.ID(),
			PeerVpcId:  peerVpc.ID(),
			PeerRegion: pulumi.String("us-west-2"),
			AutoAccept: pulumi.Bool(false),
		}, pulumi.Provider(provider))
		if err != nil {
			return err
		}
		_, err = ec2.NewVpcPeeringConnectionAccepter(ctx, "peerAcceptor", &ec2.VpcPeeringConnectionAccepterArgs{
			Region:                 pulumi.String("us-west-2"),
			VpcPeeringConnectionId: peerConnection.ID(),
			AutoAccept:             pulumi.Bool(true),
		}, pulumi.Provider(provider))
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;

return await Deployment.RunAsync(() =>
{
    var provider = new Aws.Provider("provider", new()
    {
        Region = "us-east-1",
    });

    var mainVpc = new Aws.Ec2.Vpc("mainVpc", new()
    {
        CidrBlock = "10.0.0.0/16",
    }, new CustomResourceOptions
    {
        Provider = provider,
    });

    var peerVpc = new Aws.Ec2.Vpc("peerVpc", new()
    {
        CidrBlock = "10.1.0.0/16",
    }, new CustomResourceOptions
    {
        Provider = provider,
    });

    var peerConnection = new Aws.Ec2.VpcPeeringConnection("peerConnection", new()
    {
        VpcId = mainVpc.Id,
        PeerVpcId = peerVpc.Id,
        PeerRegion = "us-west-2",
        AutoAccept = false,
    }, new CustomResourceOptions
    {
        Provider = provider,
    });

    var peerAcceptor = new Aws.Ec2.VpcPeeringConnectionAccepter("peerAcceptor", new()
    {
        Region = "us-west-2",
        VpcPeeringConnectionId = peerConnection.Id,
        AutoAccept = true,
    }, new CustomResourceOptions
    {
        Provider = provider,
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.Provider;
import com.pulumi.aws.ProviderArgs;
import com.pulumi.aws.ec2.Vpc;
import com.pulumi.aws.ec2.VpcArgs;
import com.pulumi.aws.ec2.VpcPeeringConnection;
import com.pulumi.aws.ec2.VpcPeeringConnectionArgs;
import com.pulumi.aws.ec2.VpcPeeringConnectionAccepter;
import com.pulumi.aws.ec2.VpcPeeringConnectionAccepterArgs;
import com.pulumi.resources.CustomResourceOptions;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var provider = new Provider("provider", ProviderArgs.builder()
            .region("us-east-1")
            .build());

        var mainVpc = new Vpc("mainVpc", VpcArgs.builder()
            .cidrBlock("10.0.0.0/16")
            .build(), CustomResourceOptions.builder()
                .provider(provider)
                .build());

        var peerVpc = new Vpc("peerVpc", VpcArgs.builder()
            .cidrBlock("10.1.0.0/16")
            .build(), CustomResourceOptions.builder()
                .provider(provider)
                .build());

        var peerConnection = new VpcPeeringConnection("peerConnection", VpcPeeringConnectionArgs.builder()
            .vpcId(mainVpc.id())
            .peerVpcId(peerVpc.id())
            .peerRegion("us-west-2")
            .autoAccept(false)
            .build(), CustomResourceOptions.builder()
                .provider(provider)
                .build());

        var peerAcceptor = new VpcPeeringConnectionAccepter("peerAcceptor", VpcPeeringConnectionAccepterArgs.builder()
            .region("us-west-2")
            .vpcPeeringConnectionId(peerConnection.id())
            .autoAccept(true)
            .build(), CustomResourceOptions.builder()
                .provider(provider)
                .build());

    }
}
name: cross-region-peering
runtime: yaml

resources:
  provider:
    type: pulumi:providers:aws
    properties:
      region: us-east-1
  mainVpc:
    type: aws:ec2:Vpc
    properties:
      cidrBlock: 10.0.0.0/16
    options:
      provider: ${provider}
  peerVpc:
    type: aws:ec2:Vpc
    properties:
      cidrBlock: 10.1.0.0/16
    options:
      provider: ${provider}
  peerConnection:
    type: aws:ec2:VpcPeeringConnection
    properties:
      vpcId: ${mainVpc.id}
      peerVpcId: ${peerVpc.id}
      peerRegion: us-west-2
      autoAccept: false
    options:
      provider: ${provider}
  peerAcceptor:
    type: aws:ec2:VpcPeeringConnectionAccepter
    properties:
      region: us-west-2
      vpcPeeringConnectionId: ${peerConnection.id}
      autoAccept: true
    options:
      provider: ${provider}

See the Enhanced Region Support guide for more details.

IAM Role Chaining for Secure AWS IaC Workflows

One of the most requested features, IAM role chaining, is now supported. This allows the Pulumi AWS Provider to assume multiple IAM roles in sequence to obtain credentials, streamlining secure AWS access across accounts and teams.

To assume a role with role chaining, you can now do the following:

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

const provider = new aws.Provider("provider", {assumeRoles: [
    {
        roleArn: "arn:aws:iam::123456789012:role/INITIAL_ROLE_NAME",
    },
    {
        roleArn: "arn:aws:iam::123456789012:role/FINAL_ROLE_NAME",
    },
]});
import pulumi
import pulumi_aws as aws

provider = aws.Provider("provider", assume_roles=[
    {
        "role_arn": "arn:aws:iam::123456789012:role/INITIAL_ROLE_NAME",
    },
    {
        "role_arn": "arn:aws:iam::123456789012:role/FINAL_ROLE_NAME",
    },
])
package main

import (
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := aws.NewProvider(ctx, "provider", &aws.ProviderArgs{
			AssumeRoles: aws.ProviderAssumeRoleArray{
				&aws.ProviderAssumeRoleArgs{
					RoleArn: pulumi.String("arn:aws:iam::123456789012:role/INITIAL_ROLE_NAME"),
				},
				&aws.ProviderAssumeRoleArgs{
					RoleArn: pulumi.String("arn:aws:iam::123456789012:role/FINAL_ROLE_NAME"),
				},
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;

return await Deployment.RunAsync(() =>
{
    var provider = new Aws.Provider("provider", new()
    {
        AssumeRoles = new[]
        {
            new Aws.Inputs.ProviderAssumeRoleArgs
            {
                RoleArn = "arn:aws:iam::123456789012:role/INITIAL_ROLE_NAME",
            },
            new Aws.Inputs.ProviderAssumeRoleArgs
            {
                RoleArn = "arn:aws:iam::123456789012:role/FINAL_ROLE_NAME",
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.Provider;
import com.pulumi.aws.ProviderArgs;
import com.pulumi.aws.inputs.ProviderAssumeRoleArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var provider = new Provider("provider", ProviderArgs.builder()
            .assumeRoles(
                ProviderAssumeRoleArgs.builder()
                    .roleArn("arn:aws:iam::123456789012:role/INITIAL_ROLE")
                    .build(),
                ProviderAssumeRoleArgs.builder()
                    .roleArn("arn:aws:iam::123456789012:role/FINAL_ROLE")
                    .build())
            .build());

    }
}
name: example
runtime: yaml
resources:
  provider:
    type: pulumi:providers:aws
    properties:
      assumeRoles:
        - roleArn: arn:aws:iam::123456789012:role/INITIAL_ROLE_NAME
        - roleArn: arn:aws:iam::123456789012:role/FINAL_ROLE_NAME

With this configuration the provider will use the INITIAL_ROLE to assume the FINAL_ROLE which will then be used as the credentials for the provider.

Simplified S3 Bucket Resource Model

In v6 of the Pulumi AWS Provider the S3 Bucket and BucketV2 resources represented different resource implementations. BucketV2 represented the latest version of the upstream Terraform resource, while Bucket was a separate resource maintained by Pulumi to keep backwards compatibility with the v4 release of the upstream Terraform Provider.

In v7 we are taking the first step in unifying these two resources by moving the S3 Bucket resource to the latest upstream implementation. As a result, in v7 both Bucket and BucketV2 will represent the latest version of the upstream Terraform S3 Bucket resource. As part of this work BucketV2 has been deprecated and will be removed in v8 of the Pulumi AWS Provider.

We have also introduced new S3 Bucket configuration resources that are alternatives to their V2 counterparts. The V2 versions will be removed in v8 of the Pulumi AWS Provider.

  • BucketAccelerateConfigurationV2 => BucketAccelerateConfiguration
  • BucketRequestPaymentConfigurationV2 => BucketRequestPaymentConfiguration
  • BucketAclV2 => BucketAcl
  • BucketCorsConfigurationV2 => BucketCorsConfiguration
  • BucketLifecycleConfigurationV2 => BucketLifecycleConfiguration
  • BucketLoggingV2 => BucketLogging
  • BucketObjectLockConfigurationV2 => BucketObjectLockConfiguration
  • BucketServerSideEncryptionConfigurationV2 => BucketServerSideEncryptionConfiguration
  • BucketVersioningV2 => BucketVersioning
  • BucketWebsiteConfigurationV2 => BucketWebsiteConfiguration

See the Migration Guide for more details on this change.

Upstream Improvements and Breaking Changes

Pulumi AWS 7.0 includes all improvements and bug fixes from the upstream (terraform-provider-aws) versions from 6.0.0 to 6.3.0. It also contains a number of upstream breaking changes. Please refer to the changelog to navigate the entire list.

Migration Guide

You can see a full list of changes and learn more about migrating your existing programs in our Migration Guide.

Getting Started with Pulumi AWS 7.0

Pulumi makes it easy to define, deploy, and manage modern AWS infrastructure with code: