Deploy AWS Storage Gateway

The aws:storagegateway/gateway:Gateway resource, part of the Pulumi AWS provider, activates and configures a Storage Gateway appliance: its type (file, tape, or volume), network identity, and protocol settings. This guide focuses on three capabilities: gateway type selection, Active Directory integration for SMB, and cached vs stored volume modes.

Storage Gateway requires a running VM appliance (EC2 or on-premises) and network access from Pulumi on port 80 during activation. The examples are intentionally small. Combine them with your own VM infrastructure, cache disk configuration, and maintenance schedules.

Deploy an S3 file gateway for NFS/SMB access

Teams that need to present S3 buckets as network file shares often deploy S3 file gateways, which translate NFS or SMB protocols into S3 API calls.

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

const example = new aws.storagegateway.Gateway("example", {
    gatewayIpAddress: "1.2.3.4",
    gatewayName: "example",
    gatewayTimezone: "GMT",
    gatewayType: "FILE_S3",
});
import pulumi
import pulumi_aws as aws

example = aws.storagegateway.Gateway("example",
    gateway_ip_address="1.2.3.4",
    gateway_name="example",
    gateway_timezone="GMT",
    gateway_type="FILE_S3")
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := storagegateway.NewGateway(ctx, "example", &storagegateway.GatewayArgs{
			GatewayIpAddress: pulumi.String("1.2.3.4"),
			GatewayName:      pulumi.String("example"),
			GatewayTimezone:  pulumi.String("GMT"),
			GatewayType:      pulumi.String("FILE_S3"),
		})
		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 example = new Aws.StorageGateway.Gateway("example", new()
    {
        GatewayIpAddress = "1.2.3.4",
        GatewayName = "example",
        GatewayTimezone = "GMT",
        GatewayType = "FILE_S3",
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.storagegateway.Gateway;
import com.pulumi.aws.storagegateway.GatewayArgs;
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 example = new Gateway("example", GatewayArgs.builder()
            .gatewayIpAddress("1.2.3.4")
            .gatewayName("example")
            .gatewayTimezone("GMT")
            .gatewayType("FILE_S3")
            .build());

    }
}
resources:
  example:
    type: aws:storagegateway:Gateway
    properties:
      gatewayIpAddress: 1.2.3.4
      gatewayName: example
      gatewayTimezone: GMT
      gatewayType: FILE_S3

The gatewayType property set to FILE_S3 configures the gateway to expose S3 buckets through file protocols. The gatewayIpAddress points to your running Storage Gateway VM; Pulumi connects to this IP on port 80 to retrieve an activation key. The gatewayTimezone affects snapshot scheduling and maintenance windows.

Connect to FSx for Windows with Active Directory

Organizations with FSx for Windows File Server deployments use FSx file gateways to provide low-latency local access to cloud file shares, with Active Directory authentication.

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

const example = new aws.storagegateway.Gateway("example", {
    gatewayIpAddress: "1.2.3.4",
    gatewayName: "example",
    gatewayTimezone: "GMT",
    gatewayType: "FILE_FSX_SMB",
    smbActiveDirectorySettings: {
        domainName: "corp.example.com",
        password: "avoid-plaintext-passwords",
        username: "Admin",
    },
});
import pulumi
import pulumi_aws as aws

example = aws.storagegateway.Gateway("example",
    gateway_ip_address="1.2.3.4",
    gateway_name="example",
    gateway_timezone="GMT",
    gateway_type="FILE_FSX_SMB",
    smb_active_directory_settings={
        "domain_name": "corp.example.com",
        "password": "avoid-plaintext-passwords",
        "username": "Admin",
    })
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := storagegateway.NewGateway(ctx, "example", &storagegateway.GatewayArgs{
			GatewayIpAddress: pulumi.String("1.2.3.4"),
			GatewayName:      pulumi.String("example"),
			GatewayTimezone:  pulumi.String("GMT"),
			GatewayType:      pulumi.String("FILE_FSX_SMB"),
			SmbActiveDirectorySettings: &storagegateway.GatewaySmbActiveDirectorySettingsArgs{
				DomainName: pulumi.String("corp.example.com"),
				Password:   pulumi.String("avoid-plaintext-passwords"),
				Username:   pulumi.String("Admin"),
			},
		})
		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 example = new Aws.StorageGateway.Gateway("example", new()
    {
        GatewayIpAddress = "1.2.3.4",
        GatewayName = "example",
        GatewayTimezone = "GMT",
        GatewayType = "FILE_FSX_SMB",
        SmbActiveDirectorySettings = new Aws.StorageGateway.Inputs.GatewaySmbActiveDirectorySettingsArgs
        {
            DomainName = "corp.example.com",
            Password = "avoid-plaintext-passwords",
            Username = "Admin",
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.storagegateway.Gateway;
import com.pulumi.aws.storagegateway.GatewayArgs;
import com.pulumi.aws.storagegateway.inputs.GatewaySmbActiveDirectorySettingsArgs;
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 example = new Gateway("example", GatewayArgs.builder()
            .gatewayIpAddress("1.2.3.4")
            .gatewayName("example")
            .gatewayTimezone("GMT")
            .gatewayType("FILE_FSX_SMB")
            .smbActiveDirectorySettings(GatewaySmbActiveDirectorySettingsArgs.builder()
                .domainName("corp.example.com")
                .password("avoid-plaintext-passwords")
                .username("Admin")
                .build())
            .build());

    }
}
resources:
  example:
    type: aws:storagegateway:Gateway
    properties:
      gatewayIpAddress: 1.2.3.4
      gatewayName: example
      gatewayTimezone: GMT
      gatewayType: FILE_FSX_SMB
      smbActiveDirectorySettings:
        domainName: corp.example.com
        password: avoid-plaintext-passwords
        username: Admin

The gatewayType FILE_FSX_SMB enables FSx integration. The smbActiveDirectorySettings block joins the gateway to your Active Directory domain, allowing Windows clients to authenticate with domain credentials. The domainName, username, and password properties establish the domain join; in production, use a secrets manager rather than plaintext passwords.

Archive to virtual tapes for backup software

Backup applications that write to physical tape libraries can target virtual tape libraries instead, storing data in S3 and Glacier without changing backup workflows.

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

const example = new aws.storagegateway.Gateway("example", {
    gatewayIpAddress: "1.2.3.4",
    gatewayName: "example",
    gatewayTimezone: "GMT",
    gatewayType: "VTL",
    mediumChangerType: "AWS-Gateway-VTL",
    tapeDriveType: "IBM-ULT3580-TD5",
});
import pulumi
import pulumi_aws as aws

example = aws.storagegateway.Gateway("example",
    gateway_ip_address="1.2.3.4",
    gateway_name="example",
    gateway_timezone="GMT",
    gateway_type="VTL",
    medium_changer_type="AWS-Gateway-VTL",
    tape_drive_type="IBM-ULT3580-TD5")
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := storagegateway.NewGateway(ctx, "example", &storagegateway.GatewayArgs{
			GatewayIpAddress:  pulumi.String("1.2.3.4"),
			GatewayName:       pulumi.String("example"),
			GatewayTimezone:   pulumi.String("GMT"),
			GatewayType:       pulumi.String("VTL"),
			MediumChangerType: pulumi.String("AWS-Gateway-VTL"),
			TapeDriveType:     pulumi.String("IBM-ULT3580-TD5"),
		})
		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 example = new Aws.StorageGateway.Gateway("example", new()
    {
        GatewayIpAddress = "1.2.3.4",
        GatewayName = "example",
        GatewayTimezone = "GMT",
        GatewayType = "VTL",
        MediumChangerType = "AWS-Gateway-VTL",
        TapeDriveType = "IBM-ULT3580-TD5",
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.storagegateway.Gateway;
import com.pulumi.aws.storagegateway.GatewayArgs;
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 example = new Gateway("example", GatewayArgs.builder()
            .gatewayIpAddress("1.2.3.4")
            .gatewayName("example")
            .gatewayTimezone("GMT")
            .gatewayType("VTL")
            .mediumChangerType("AWS-Gateway-VTL")
            .tapeDriveType("IBM-ULT3580-TD5")
            .build());

    }
}
resources:
  example:
    type: aws:storagegateway:Gateway
    properties:
      gatewayIpAddress: 1.2.3.4
      gatewayName: example
      gatewayTimezone: GMT
      gatewayType: VTL
      mediumChangerType: AWS-Gateway-VTL
      tapeDriveType: IBM-ULT3580-TD5

The gatewayType VTL (Virtual Tape Library) emulates physical tape infrastructure. The mediumChangerType and tapeDriveType properties define the virtual hardware your backup software sees; AWS-Gateway-VTL and IBM-ULT3580-TD5 are common choices that work with most backup applications.

Present iSCSI volumes with local cache

Applications that require block storage can use cached volume gateways, which store primary data in S3 while caching frequently accessed data locally for low-latency reads.

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

const example = new aws.storagegateway.Gateway("example", {
    gatewayIpAddress: "1.2.3.4",
    gatewayName: "example",
    gatewayTimezone: "GMT",
    gatewayType: "CACHED",
});
import pulumi
import pulumi_aws as aws

example = aws.storagegateway.Gateway("example",
    gateway_ip_address="1.2.3.4",
    gateway_name="example",
    gateway_timezone="GMT",
    gateway_type="CACHED")
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := storagegateway.NewGateway(ctx, "example", &storagegateway.GatewayArgs{
			GatewayIpAddress: pulumi.String("1.2.3.4"),
			GatewayName:      pulumi.String("example"),
			GatewayTimezone:  pulumi.String("GMT"),
			GatewayType:      pulumi.String("CACHED"),
		})
		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 example = new Aws.StorageGateway.Gateway("example", new()
    {
        GatewayIpAddress = "1.2.3.4",
        GatewayName = "example",
        GatewayTimezone = "GMT",
        GatewayType = "CACHED",
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.storagegateway.Gateway;
import com.pulumi.aws.storagegateway.GatewayArgs;
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 example = new Gateway("example", GatewayArgs.builder()
            .gatewayIpAddress("1.2.3.4")
            .gatewayName("example")
            .gatewayTimezone("GMT")
            .gatewayType("CACHED")
            .build());

    }
}
resources:
  example:
    type: aws:storagegateway:Gateway
    properties:
      gatewayIpAddress: 1.2.3.4
      gatewayName: example
      gatewayTimezone: GMT
      gatewayType: CACHED

The gatewayType CACHED creates a volume gateway that keeps frequently accessed blocks on local disks while storing the full dataset in S3. This provides low-latency access to hot data without requiring local storage for the entire volume.

Store volumes locally with S3 snapshots

When applications need the entire dataset available locally, stored volume gateways keep all data on-premises while asynchronously backing up point-in-time snapshots to S3.

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

const example = new aws.storagegateway.Gateway("example", {
    gatewayIpAddress: "1.2.3.4",
    gatewayName: "example",
    gatewayTimezone: "GMT",
    gatewayType: "STORED",
});
import pulumi
import pulumi_aws as aws

example = aws.storagegateway.Gateway("example",
    gateway_ip_address="1.2.3.4",
    gateway_name="example",
    gateway_timezone="GMT",
    gateway_type="STORED")
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := storagegateway.NewGateway(ctx, "example", &storagegateway.GatewayArgs{
			GatewayIpAddress: pulumi.String("1.2.3.4"),
			GatewayName:      pulumi.String("example"),
			GatewayTimezone:  pulumi.String("GMT"),
			GatewayType:      pulumi.String("STORED"),
		})
		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 example = new Aws.StorageGateway.Gateway("example", new()
    {
        GatewayIpAddress = "1.2.3.4",
        GatewayName = "example",
        GatewayTimezone = "GMT",
        GatewayType = "STORED",
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.storagegateway.Gateway;
import com.pulumi.aws.storagegateway.GatewayArgs;
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 example = new Gateway("example", GatewayArgs.builder()
            .gatewayIpAddress("1.2.3.4")
            .gatewayName("example")
            .gatewayTimezone("GMT")
            .gatewayType("STORED")
            .build());

    }
}
resources:
  example:
    type: aws:storagegateway:Gateway
    properties:
      gatewayIpAddress: 1.2.3.4
      gatewayName: example
      gatewayTimezone: GMT
      gatewayType: STORED

The gatewayType STORED keeps the entire volume on local disks, with asynchronous snapshots to S3. This differs from cached volumes (CACHED) by maintaining full local storage rather than caching subsets. Choose stored volumes when applications require guaranteed local access to all data.

Beyond these examples

These snippets focus on specific gateway-level features: gateway type selection, Active Directory integration for SMB, and tape library emulation. They’re intentionally minimal rather than full storage deployments.

The examples assume pre-existing infrastructure such as a Storage Gateway VM appliance (EC2 instance or on-premises), Active Directory domain (for FSx gateways), and network connectivity from Pulumi to the gateway on port 80. They focus on activating and configuring the gateway rather than provisioning the underlying VM or network.

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

  • CloudWatch logging configuration (cloudwatchLogGroupArn)
  • Maintenance windows (maintenanceStartTime)
  • Bandwidth throttling (averageUploadRateLimitInBitsPerSec, averageDownloadRateLimitInBitsPerSec)
  • SMB security strategy and guest passwords
  • Local cache disk configuration (shown in EX1 but not integrated)

These omissions are intentional: the goal is to illustrate how each gateway type is wired, not provide drop-in storage modules. See the Storage Gateway Gateway resource reference for all available configuration options.

Let's deploy AWS Storage Gateway

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

Try Pulumi Cloud for FREE

Frequently Asked Questions

Activation & Connectivity
Why am I getting 'The specified gateway is not connected' errors during activation?
This error means your gateway instance doesn’t meet Storage Gateway requirements or isn’t properly connected. The Storage Gateway API requires the gateway to be connected to return information after activation. Verify your instance meets all requirements before attempting activation.
What's the difference between activationKey and gatewayIpAddress for activation?
You must provide one (but not both) for gateway activation. Use activationKey if you already have an activation key, or use gatewayIpAddress to retrieve the key automatically. When using gatewayIpAddress, Pulumi must be able to make an HTTP request to port 80 on that IP address.
How do I activate a gateway in a private subnet?
Use gatewayVpcEndpoint to specify the VPC endpoint address for activation. This requires HTTP access from the computer running Pulumi. Check the VPC Endpoint Security group to ensure required ports are open.
Gateway Types & Configuration
What gateway types are available?

Storage Gateway supports five types:

  1. FILE_S3 - S3 File Gateway
  2. FILE_FSX_SMB - FSx File Gateway
  3. VTL - Tape Gateway (Virtual Tape Library)
  4. CACHED - Volume Gateway (Cached volumes)
  5. STORED - Volume Gateway (Stored volumes, default)
Can I set bandwidth limits for my gateway?
Yes, use averageDownloadRateLimitInBitsPerSec and averageUploadRateLimitInBitsPerSec to control bandwidth. These limits are supported for CACHED, STORED, and VTL gateway types.
SMB File Share Configuration
How do I join my SMB file gateway to Active Directory?
Configure smbActiveDirectorySettings with domainName, username, and password. This is only valid for FILE_S3 and FILE_FSX_SMB gateway types and must be set before creating ActiveDirectory authentication SMB file shares.
How do I enable guest access for SMB file shares?
Set smbGuestPassword before creating GuestAccess authentication SMB file shares. This is only valid for FILE_S3 and FILE_FSX_SMB gateway types. Note that Pulumi can detect whether a guest password exists but not its actual value.
What SMB security strategies are available?
Three options: ClientSpecified (client chooses), MandatorySigning (requires signing), and MandatoryEncryption (requires encryption). Configure using smbSecurityStrategy.
Tape Gateway Configuration
How do I configure a tape gateway?
Set gatewayType to VTL, then specify mediumChangerType (valid values: STK-L700, AWS-Gateway-VTL, IBM-03584L32-0402) and tapeDriveType (valid value: IBM-ULT3580-TD5). Both properties are immutable after creation.
Resource Management
Which properties can't I change after creating the gateway?
These properties are immutable: activationKey, gatewayIpAddress, gatewayType, gatewayVpcEndpoint, mediumChangerType, and tapeDriveType. Changing any of these requires recreating the gateway.
Why does Pulumi show drift for properties I haven't changed?
Some properties have drift detection limitations. Pulumi cannot detect drift for mediumChangerType and tapeDriveType. For smbGuestPassword, it can only detect whether a password exists, not its actual value. For gatewayIpAddress, there’s no API to read the value after creation; use ignoreChanges or omit it from your program after initial creation.
What's different about importing an existing gateway versus creating a new one?
When importing, neither activationKey nor gatewayIpAddress is required (the gateway is already activated). When creating a new gateway, you must provide one of these two properties for activation.

Using a different cloud?

Explore storage guides for other cloud providers: