Create AWS EventBridge Event Archives

The aws:cloudwatch/eventArchive:EventArchive resource, part of the Pulumi AWS provider, captures and stores events from an EventBridge event bus for compliance, debugging, or replay scenarios. This guide focuses on three capabilities: basic event archival, event filtering with retention policies, and customer-managed encryption.

Archives reference existing EventBridge event buses and optionally KMS keys for encryption. The examples are intentionally small. Combine them with your own event buses, filtering logic, and encryption requirements.

Archive all events from a custom event bus

Teams building event-driven architectures often need to preserve event history for compliance, debugging, or replay scenarios.

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

const order = new aws.cloudwatch.EventBus("order", {name: "orders"});
const orderEventArchive = new aws.cloudwatch.EventArchive("order", {
    name: "order-archive",
    eventSourceArn: order.arn,
});
import pulumi
import pulumi_aws as aws

order = aws.cloudwatch.EventBus("order", name="orders")
order_event_archive = aws.cloudwatch.EventArchive("order",
    name="order-archive",
    event_source_arn=order.arn)
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		order, err := cloudwatch.NewEventBus(ctx, "order", &cloudwatch.EventBusArgs{
			Name: pulumi.String("orders"),
		})
		if err != nil {
			return err
		}
		_, err = cloudwatch.NewEventArchive(ctx, "order", &cloudwatch.EventArchiveArgs{
			Name:           pulumi.String("order-archive"),
			EventSourceArn: order.Arn,
		})
		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 order = new Aws.CloudWatch.EventBus("order", new()
    {
        Name = "orders",
    });

    var orderEventArchive = new Aws.CloudWatch.EventArchive("order", new()
    {
        Name = "order-archive",
        EventSourceArn = order.Arn,
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.cloudwatch.EventBus;
import com.pulumi.aws.cloudwatch.EventBusArgs;
import com.pulumi.aws.cloudwatch.EventArchive;
import com.pulumi.aws.cloudwatch.EventArchiveArgs;
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 order = new EventBus("order", EventBusArgs.builder()
            .name("orders")
            .build());

        var orderEventArchive = new EventArchive("orderEventArchive", EventArchiveArgs.builder()
            .name("order-archive")
            .eventSourceArn(order.arn())
            .build());

    }
}
resources:
  order:
    type: aws:cloudwatch:EventBus
    properties:
      name: orders
  orderEventArchive:
    type: aws:cloudwatch:EventArchive
    name: order
    properties:
      name: order-archive
      eventSourceArn: ${order.arn}

The eventSourceArn property specifies which event bus to archive from; only events published to that bus are captured. Without an eventPattern, the archive stores every event. Without retentionDays, events are kept indefinitely.

Filter and retain events for a limited period

Not all events need permanent storage. Applications often archive only specific event types and apply retention policies to manage storage costs.

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

const order = new aws.cloudwatch.EventBus("order", {name: "orders"});
const orderEventArchive = new aws.cloudwatch.EventArchive("order", {
    name: "order-archive",
    description: "Archived events from order service",
    eventSourceArn: order.arn,
    retentionDays: 7,
    eventPattern: JSON.stringify({
        source: ["company.team.order"],
    }),
});
import pulumi
import json
import pulumi_aws as aws

order = aws.cloudwatch.EventBus("order", name="orders")
order_event_archive = aws.cloudwatch.EventArchive("order",
    name="order-archive",
    description="Archived events from order service",
    event_source_arn=order.arn,
    retention_days=7,
    event_pattern=json.dumps({
        "source": ["company.team.order"],
    }))
package main

import (
	"encoding/json"

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		order, err := cloudwatch.NewEventBus(ctx, "order", &cloudwatch.EventBusArgs{
			Name: pulumi.String("orders"),
		})
		if err != nil {
			return err
		}
		tmpJSON0, err := json.Marshal(map[string]interface{}{
			"source": []string{
				"company.team.order",
			},
		})
		if err != nil {
			return err
		}
		json0 := string(tmpJSON0)
		_, err = cloudwatch.NewEventArchive(ctx, "order", &cloudwatch.EventArchiveArgs{
			Name:           pulumi.String("order-archive"),
			Description:    pulumi.String("Archived events from order service"),
			EventSourceArn: order.Arn,
			RetentionDays:  pulumi.Int(7),
			EventPattern:   pulumi.String(json0),
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using Pulumi;
using Aws = Pulumi.Aws;

return await Deployment.RunAsync(() => 
{
    var order = new Aws.CloudWatch.EventBus("order", new()
    {
        Name = "orders",
    });

    var orderEventArchive = new Aws.CloudWatch.EventArchive("order", new()
    {
        Name = "order-archive",
        Description = "Archived events from order service",
        EventSourceArn = order.Arn,
        RetentionDays = 7,
        EventPattern = JsonSerializer.Serialize(new Dictionary<string, object?>
        {
            ["source"] = new[]
            {
                "company.team.order",
            },
        }),
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.cloudwatch.EventBus;
import com.pulumi.aws.cloudwatch.EventBusArgs;
import com.pulumi.aws.cloudwatch.EventArchive;
import com.pulumi.aws.cloudwatch.EventArchiveArgs;
import static com.pulumi.codegen.internal.Serialization.*;
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 order = new EventBus("order", EventBusArgs.builder()
            .name("orders")
            .build());

        var orderEventArchive = new EventArchive("orderEventArchive", EventArchiveArgs.builder()
            .name("order-archive")
            .description("Archived events from order service")
            .eventSourceArn(order.arn())
            .retentionDays(7)
            .eventPattern(serializeJson(
                jsonObject(
                    jsonProperty("source", jsonArray("company.team.order"))
                )))
            .build());

    }
}
resources:
  order:
    type: aws:cloudwatch:EventBus
    properties:
      name: orders
  orderEventArchive:
    type: aws:cloudwatch:EventArchive
    name: order
    properties:
      name: order-archive
      description: Archived events from order service
      eventSourceArn: ${order.arn}
      retentionDays: 7
      eventPattern:
        fn::toJSON:
          source:
            - company.team.order

The eventPattern property filters which events to archive using the same JSON syntax as EventBridge rules. Here, only events with source “company.team.order” are stored. The retentionDays property automatically deletes events after 7 days, reducing storage costs.

Encrypt archived events with a customer-managed key

Compliance requirements often mandate encryption of archived data using organization-managed keys rather than AWS-managed encryption.

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

const current = aws.getCallerIdentity({});
const currentGetPartition = aws.getPartition({});
const example = new aws.cloudwatch.EventBus("example", {name: "example"});
const exampleKey = new aws.kms.Key("example", {
    deletionWindowInDays: 7,
    policy: pulumi.jsonStringify({
        Version: "2012-10-17",
        Id: "key-policy-example",
        Statement: [
            {
                Sid: "Enable IAM User Permissions",
                Effect: "Allow",
                Principal: {
                    AWS: Promise.all([currentGetPartition, current]).then(([currentGetPartition, current]) => `arn:${currentGetPartition.partition}:iam::${current.accountId}:root`),
                },
                Action: "kms:*",
                Resource: "*",
            },
            {
                Sid: "Allow describing of the key",
                Effect: "Allow",
                Principal: {
                    Service: "events.amazonaws.com",
                },
                Action: ["kms:DescribeKey"],
                Resource: "*",
            },
            {
                Sid: "Allow use of the key",
                Effect: "Allow",
                Principal: {
                    Service: "events.amazonaws.com",
                },
                Action: [
                    "kms:GenerateDataKey",
                    "kms:Decrypt",
                    "kms:ReEncrypt*",
                ],
                Resource: "*",
                Condition: {
                    StringEquals: {
                        "kms:EncryptionContext:aws:events:event-bus:arn": example.arn,
                    },
                },
            },
        ],
    }),
    tags: {
        EventBridgeApiDestinations: "true",
    },
});
const exampleEventArchive = new aws.cloudwatch.EventArchive("example", {
    name: "example",
    eventSourceArn: example.arn,
    kmsKeyIdentifier: exampleKey.id,
});
import pulumi
import json
import pulumi_aws as aws

current = aws.get_caller_identity()
current_get_partition = aws.get_partition()
example = aws.cloudwatch.EventBus("example", name="example")
example_key = aws.kms.Key("example",
    deletion_window_in_days=7,
    policy=pulumi.Output.json_dumps({
        "Version": "2012-10-17",
        "Id": "key-policy-example",
        "Statement": [
            {
                "Sid": "Enable IAM User Permissions",
                "Effect": "Allow",
                "Principal": {
                    "AWS": f"arn:{current_get_partition.partition}:iam::{current.account_id}:root",
                },
                "Action": "kms:*",
                "Resource": "*",
            },
            {
                "Sid": "Allow describing of the key",
                "Effect": "Allow",
                "Principal": {
                    "Service": "events.amazonaws.com",
                },
                "Action": ["kms:DescribeKey"],
                "Resource": "*",
            },
            {
                "Sid": "Allow use of the key",
                "Effect": "Allow",
                "Principal": {
                    "Service": "events.amazonaws.com",
                },
                "Action": [
                    "kms:GenerateDataKey",
                    "kms:Decrypt",
                    "kms:ReEncrypt*",
                ],
                "Resource": "*",
                "Condition": {
                    "StringEquals": {
                        "kms:EncryptionContext:aws:events:event-bus:arn": example.arn,
                    },
                },
            },
        ],
    }),
    tags={
        "EventBridgeApiDestinations": "true",
    })
example_event_archive = aws.cloudwatch.EventArchive("example",
    name="example",
    event_source_arn=example.arn,
    kms_key_identifier=example_key.id)
package main

import (
	"encoding/json"
	"fmt"

	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws"
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/cloudwatch"
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/kms"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		current, err := aws.GetCallerIdentity(ctx, &aws.GetCallerIdentityArgs{}, nil)
		if err != nil {
			return err
		}
		currentGetPartition, err := aws.GetPartition(ctx, &aws.GetPartitionArgs{}, nil)
		if err != nil {
			return err
		}
		example, err := cloudwatch.NewEventBus(ctx, "example", &cloudwatch.EventBusArgs{
			Name: pulumi.String("example"),
		})
		if err != nil {
			return err
		}
		exampleKey, err := kms.NewKey(ctx, "example", &kms.KeyArgs{
			DeletionWindowInDays: pulumi.Int(7),
			Policy: example.Arn.ApplyT(func(arn string) (pulumi.String, error) {
				var _zero pulumi.String
				tmpJSON0, err := json.Marshal(map[string]interface{}{
					"Version": "2012-10-17",
					"Id":      "key-policy-example",
					"Statement": []interface{}{
						map[string]interface{}{
							"Sid":    "Enable IAM User Permissions",
							"Effect": "Allow",
							"Principal": map[string]interface{}{
								"AWS": fmt.Sprintf("arn:%v:iam::%v:root", currentGetPartition.Partition, current.AccountId),
							},
							"Action":   "kms:*",
							"Resource": "*",
						},
						map[string]interface{}{
							"Sid":    "Allow describing of the key",
							"Effect": "Allow",
							"Principal": map[string]interface{}{
								"Service": "events.amazonaws.com",
							},
							"Action": []string{
								"kms:DescribeKey",
							},
							"Resource": "*",
						},
						map[string]interface{}{
							"Sid":    "Allow use of the key",
							"Effect": "Allow",
							"Principal": map[string]interface{}{
								"Service": "events.amazonaws.com",
							},
							"Action": []string{
								"kms:GenerateDataKey",
								"kms:Decrypt",
								"kms:ReEncrypt*",
							},
							"Resource": "*",
							"Condition": map[string]interface{}{
								"StringEquals": map[string]interface{}{
									"kms:EncryptionContext:aws:events:event-bus:arn": arn,
								},
							},
						},
					},
				})
				if err != nil {
					return _zero, err
				}
				json0 := string(tmpJSON0)
				return pulumi.String(json0), nil
			}).(pulumi.StringOutput),
			Tags: pulumi.StringMap{
				"EventBridgeApiDestinations": pulumi.String("true"),
			},
		})
		if err != nil {
			return err
		}
		_, err = cloudwatch.NewEventArchive(ctx, "example", &cloudwatch.EventArchiveArgs{
			Name:             pulumi.String("example"),
			EventSourceArn:   example.Arn,
			KmsKeyIdentifier: exampleKey.ID(),
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using Pulumi;
using Aws = Pulumi.Aws;

return await Deployment.RunAsync(() => 
{
    var current = Aws.GetCallerIdentity.Invoke();

    var currentGetPartition = Aws.GetPartition.Invoke();

    var example = new Aws.CloudWatch.EventBus("example", new()
    {
        Name = "example",
    });

    var exampleKey = new Aws.Kms.Key("example", new()
    {
        DeletionWindowInDays = 7,
        Policy = Output.JsonSerialize(Output.Create(new Dictionary<string, object?>
        {
            ["Version"] = "2012-10-17",
            ["Id"] = "key-policy-example",
            ["Statement"] = new[]
            {
                new Dictionary<string, object?>
                {
                    ["Sid"] = "Enable IAM User Permissions",
                    ["Effect"] = "Allow",
                    ["Principal"] = new Dictionary<string, object?>
                    {
                        ["AWS"] = Output.Tuple(currentGetPartition, current).Apply(values =>
                        {
                            var currentGetPartition = values.Item1;
                            var current = values.Item2;
                            return $"arn:{currentGetPartition.Apply(getPartitionResult => getPartitionResult.Partition)}:iam::{current.Apply(getCallerIdentityResult => getCallerIdentityResult.AccountId)}:root";
                        }),
                    },
                    ["Action"] = "kms:*",
                    ["Resource"] = "*",
                },
                new Dictionary<string, object?>
                {
                    ["Sid"] = "Allow describing of the key",
                    ["Effect"] = "Allow",
                    ["Principal"] = new Dictionary<string, object?>
                    {
                        ["Service"] = "events.amazonaws.com",
                    },
                    ["Action"] = new[]
                    {
                        "kms:DescribeKey",
                    },
                    ["Resource"] = "*",
                },
                new Dictionary<string, object?>
                {
                    ["Sid"] = "Allow use of the key",
                    ["Effect"] = "Allow",
                    ["Principal"] = new Dictionary<string, object?>
                    {
                        ["Service"] = "events.amazonaws.com",
                    },
                    ["Action"] = new[]
                    {
                        "kms:GenerateDataKey",
                        "kms:Decrypt",
                        "kms:ReEncrypt*",
                    },
                    ["Resource"] = "*",
                    ["Condition"] = new Dictionary<string, object?>
                    {
                        ["StringEquals"] = new Dictionary<string, object?>
                        {
                            ["kms:EncryptionContext:aws:events:event-bus:arn"] = example.Arn,
                        },
                    },
                },
            },
        })),
        Tags = 
        {
            { "EventBridgeApiDestinations", "true" },
        },
    });

    var exampleEventArchive = new Aws.CloudWatch.EventArchive("example", new()
    {
        Name = "example",
        EventSourceArn = example.Arn,
        KmsKeyIdentifier = exampleKey.Id,
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.AwsFunctions;
import com.pulumi.aws.inputs.GetCallerIdentityArgs;
import com.pulumi.aws.inputs.GetPartitionArgs;
import com.pulumi.aws.cloudwatch.EventBus;
import com.pulumi.aws.cloudwatch.EventBusArgs;
import com.pulumi.aws.kms.Key;
import com.pulumi.aws.kms.KeyArgs;
import com.pulumi.aws.cloudwatch.EventArchive;
import com.pulumi.aws.cloudwatch.EventArchiveArgs;
import static com.pulumi.codegen.internal.Serialization.*;
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) {
        final var current = AwsFunctions.getCallerIdentity(GetCallerIdentityArgs.builder()
            .build());

        final var currentGetPartition = AwsFunctions.getPartition(GetPartitionArgs.builder()
            .build());

        var example = new EventBus("example", EventBusArgs.builder()
            .name("example")
            .build());

        var exampleKey = new Key("exampleKey", KeyArgs.builder()
            .deletionWindowInDays(7)
            .policy(example.arn().applyValue(_arn -> serializeJson(
                jsonObject(
                    jsonProperty("Version", "2012-10-17"),
                    jsonProperty("Id", "key-policy-example"),
                    jsonProperty("Statement", jsonArray(
                        jsonObject(
                            jsonProperty("Sid", "Enable IAM User Permissions"),
                            jsonProperty("Effect", "Allow"),
                            jsonProperty("Principal", jsonObject(
                                jsonProperty("AWS", String.format("arn:%s:iam::%s:root", currentGetPartition.partition(),current.accountId()))
                            )),
                            jsonProperty("Action", "kms:*"),
                            jsonProperty("Resource", "*")
                        ), 
                        jsonObject(
                            jsonProperty("Sid", "Allow describing of the key"),
                            jsonProperty("Effect", "Allow"),
                            jsonProperty("Principal", jsonObject(
                                jsonProperty("Service", "events.amazonaws.com")
                            )),
                            jsonProperty("Action", jsonArray("kms:DescribeKey")),
                            jsonProperty("Resource", "*")
                        ), 
                        jsonObject(
                            jsonProperty("Sid", "Allow use of the key"),
                            jsonProperty("Effect", "Allow"),
                            jsonProperty("Principal", jsonObject(
                                jsonProperty("Service", "events.amazonaws.com")
                            )),
                            jsonProperty("Action", jsonArray(
                                "kms:GenerateDataKey", 
                                "kms:Decrypt", 
                                "kms:ReEncrypt*"
                            )),
                            jsonProperty("Resource", "*"),
                            jsonProperty("Condition", jsonObject(
                                jsonProperty("StringEquals", jsonObject(
                                    jsonProperty("kms:EncryptionContext:aws:events:event-bus:arn", _arn)
                                ))
                            ))
                        )
                    ))
                ))))
            .tags(Map.of("EventBridgeApiDestinations", "true"))
            .build());

        var exampleEventArchive = new EventArchive("exampleEventArchive", EventArchiveArgs.builder()
            .name("example")
            .eventSourceArn(example.arn())
            .kmsKeyIdentifier(exampleKey.id())
            .build());

    }
}
resources:
  example:
    type: aws:cloudwatch:EventBus
    properties:
      name: example
  exampleKey:
    type: aws:kms:Key
    name: example
    properties:
      deletionWindowInDays: 7
      policy:
        fn::toJSON:
          Version: 2012-10-17
          Id: key-policy-example
          Statement:
            - Sid: Enable IAM User Permissions
              Effect: Allow
              Principal:
                AWS: arn:${currentGetPartition.partition}:iam::${current.accountId}:root
              Action: kms:*
              Resource: '*'
            - Sid: Allow describing of the key
              Effect: Allow
              Principal:
                Service: events.amazonaws.com
              Action:
                - kms:DescribeKey
              Resource: '*'
            - Sid: Allow use of the key
              Effect: Allow
              Principal:
                Service: events.amazonaws.com
              Action:
                - kms:GenerateDataKey
                - kms:Decrypt
                - kms:ReEncrypt*
              Resource: '*'
              Condition:
                StringEquals:
                  kms:EncryptionContext:aws:events:event-bus:arn: ${example.arn}
      tags:
        EventBridgeApiDestinations: 'true'
  exampleEventArchive:
    type: aws:cloudwatch:EventArchive
    name: example
    properties:
      name: example
      eventSourceArn: ${example.arn}
      kmsKeyIdentifier: ${exampleKey.id}
variables:
  current:
    fn::invoke:
      function: aws:getCallerIdentity
      arguments: {}
  currentGetPartition:
    fn::invoke:
      function: aws:getPartition
      arguments: {}

The kmsKeyIdentifier property specifies a customer-managed KMS key for encryption. The key policy must grant EventBridge permissions for DescribeKey, GenerateDataKey, Decrypt, and ReEncrypt operations. The policy condition ties encryption to the specific event bus ARN, ensuring the key is only used for this archive.

Beyond these examples

These snippets focus on specific archive-level features: event filtering and retention policies, and customer-managed encryption. They’re intentionally minimal rather than full event-driven architectures.

The examples reference pre-existing infrastructure such as EventBridge event buses, and KMS keys with appropriate policies for the encryption example. They focus on configuring the archive rather than provisioning the surrounding event infrastructure.

To keep things focused, common archive patterns are omitted, including:

  • Archive replay to event buses
  • Cross-region archive replication
  • Archive lifecycle policies beyond retention days
  • IAM permissions for archive access

These omissions are intentional: the goal is to illustrate how each archive feature is wired, not provide drop-in event storage modules. See the EventBridge EventArchive resource reference for all available configuration options.

Let's create AWS EventBridge Event Archives

Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.

Try Pulumi Cloud for FREE

Frequently Asked Questions

Configuration & Limits
What properties can't I change after creating an archive?
The name and eventSourceArn properties are immutable and cannot be changed after creation. Modifying either requires recreating the archive.
What's the maximum length for an archive name?
Archive names cannot exceed 48 characters.
What's the difference between CloudWatch Events and EventBridge?
EventBridge was formerly known as CloudWatch Events. The functionality is identical.
Event Filtering & Retention
How long are events retained in an archive?
By default, archives retain events indefinitely. You can set retentionDays to specify a maximum retention period.
What events get archived by default?
By default, archives capture every event received from the event bus specified in eventSourceArn.
How do I filter which events are archived?
Use the eventPattern property to specify an event pattern that filters which events get sent to the archive.
Encryption & Security
How do I encrypt my archive with a customer-managed KMS key?
Set kmsKeyIdentifier to your KMS key ARN, KeyId, key alias, or key alias ARN. Ensure your KMS key policy grants EventBridge the necessary permissions.
What KMS permissions does EventBridge need to encrypt archives?
EventBridge requires three permission sets: kms:DescribeKey on all resources, plus kms:GenerateDataKey, kms:Decrypt, and kms:ReEncrypt* with a condition that matches your event bus ARN in the encryption context.
Can I use a KMS key alias instead of the key ARN?
Yes, kmsKeyIdentifier accepts the key ARN, KeyId, key alias, or key alias ARN.

Using a different cloud?

Explore messaging guides for other cloud providers: