Create AWS FSx for NetApp ONTAP File Systems

The aws:fsx/ontapFileSystem:OntapFileSystem resource, part of the Pulumi AWS provider, provisions FSx for NetApp ONTAP file systems: their deployment architecture, capacity, throughput, and network placement. This guide focuses on three capabilities: multi-AZ high availability, HA pair scaling, and Gen1 vs Gen2 deployment types.

FSx ONTAP file systems run in VPC subnets and may reference route tables, security groups, and KMS keys. The examples are intentionally small. Combine them with your own VPC infrastructure, security groups, and backup configuration.

Deploy a multi-AZ file system with automatic failover

Enterprise workloads requiring high availability deploy FSx ONTAP across multiple availability zones, ensuring continuous access even during zone failures.

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

const test = new aws.fsx.OntapFileSystem("test", {
    storageCapacity: 1024,
    subnetIds: [
        test1.id,
        test2.id,
    ],
    deploymentType: "MULTI_AZ_1",
    throughputCapacity: 512,
    preferredSubnetId: test1.id,
});
import pulumi
import pulumi_aws as aws

test = aws.fsx.OntapFileSystem("test",
    storage_capacity=1024,
    subnet_ids=[
        test1["id"],
        test2["id"],
    ],
    deployment_type="MULTI_AZ_1",
    throughput_capacity=512,
    preferred_subnet_id=test1["id"])
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := fsx.NewOntapFileSystem(ctx, "test", &fsx.OntapFileSystemArgs{
			StorageCapacity: pulumi.Int(1024),
			SubnetIds: pulumi.StringArray{
				test1.Id,
				test2.Id,
			},
			DeploymentType:     pulumi.String("MULTI_AZ_1"),
			ThroughputCapacity: pulumi.Int(512),
			PreferredSubnetId:  pulumi.Any(test1.Id),
		})
		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 test = new Aws.Fsx.OntapFileSystem("test", new()
    {
        StorageCapacity = 1024,
        SubnetIds = new[]
        {
            test1.Id,
            test2.Id,
        },
        DeploymentType = "MULTI_AZ_1",
        ThroughputCapacity = 512,
        PreferredSubnetId = test1.Id,
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.fsx.OntapFileSystem;
import com.pulumi.aws.fsx.OntapFileSystemArgs;
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 test = new OntapFileSystem("test", OntapFileSystemArgs.builder()
            .storageCapacity(1024)
            .subnetIds(            
                test1.id(),
                test2.id())
            .deploymentType("MULTI_AZ_1")
            .throughputCapacity(512)
            .preferredSubnetId(test1.id())
            .build());

    }
}
resources:
  test:
    type: aws:fsx:OntapFileSystem
    properties:
      storageCapacity: 1024
      subnetIds:
        - ${test1.id}
        - ${test2.id}
      deploymentType: MULTI_AZ_1
      throughputCapacity: 512
      preferredSubnetId: ${test1.id}

The deploymentType property set to MULTI_AZ_1 creates a file system that spans two availability zones. You provide two subnet IDs (one per zone) via subnetIds, and specify which subnet hosts the preferred file server with preferredSubnetId. The throughputCapacity property sets total throughput in MBps; this approach is used when not specifying HA pairs explicitly.

Scale capacity with multiple HA pairs in single-AZ

Applications needing higher capacity or throughput within a single availability zone can deploy multiple HA pairs to scale beyond single-pair limits.

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

const testhapairs = new aws.fsx.OntapFileSystem("testhapairs", {
    storageCapacity: 2048,
    subnetIds: [test1.id],
    deploymentType: "SINGLE_AZ_1",
    haPairs: 2,
    throughputCapacityPerHaPair: 128,
    preferredSubnetId: test1.id,
});
import pulumi
import pulumi_aws as aws

testhapairs = aws.fsx.OntapFileSystem("testhapairs",
    storage_capacity=2048,
    subnet_ids=[test1["id"]],
    deployment_type="SINGLE_AZ_1",
    ha_pairs=2,
    throughput_capacity_per_ha_pair=128,
    preferred_subnet_id=test1["id"])
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := fsx.NewOntapFileSystem(ctx, "testhapairs", &fsx.OntapFileSystemArgs{
			StorageCapacity: pulumi.Int(2048),
			SubnetIds: pulumi.StringArray{
				test1.Id,
			},
			DeploymentType:              pulumi.String("SINGLE_AZ_1"),
			HaPairs:                     pulumi.Int(2),
			ThroughputCapacityPerHaPair: pulumi.Int(128),
			PreferredSubnetId:           pulumi.Any(test1.Id),
		})
		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 testhapairs = new Aws.Fsx.OntapFileSystem("testhapairs", new()
    {
        StorageCapacity = 2048,
        SubnetIds = new[]
        {
            test1.Id,
        },
        DeploymentType = "SINGLE_AZ_1",
        HaPairs = 2,
        ThroughputCapacityPerHaPair = 128,
        PreferredSubnetId = test1.Id,
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.fsx.OntapFileSystem;
import com.pulumi.aws.fsx.OntapFileSystemArgs;
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 testhapairs = new OntapFileSystem("testhapairs", OntapFileSystemArgs.builder()
            .storageCapacity(2048)
            .subnetIds(test1.id())
            .deploymentType("SINGLE_AZ_1")
            .haPairs(2)
            .throughputCapacityPerHaPair(128)
            .preferredSubnetId(test1.id())
            .build());

    }
}
resources:
  testhapairs:
    type: aws:fsx:OntapFileSystem
    properties:
      storageCapacity: 2048
      subnetIds:
        - ${test1.id}
      deploymentType: SINGLE_AZ_1
      haPairs: 2
      throughputCapacityPerHaPair: 128
      preferredSubnetId: ${test1.id}

The haPairs property controls how many high-availability pairs to deploy. When using haPairs, you configure throughput per pair via throughputCapacityPerHaPair rather than total throughput. Here, 2 HA pairs with 128 MBps each provide 256 MBps total throughput. The SINGLE_AZ_1 deployment type places all pairs in one zone.

Deploy Gen2 single-AZ with high HA pair count

The SINGLE_AZ_2 deployment type supports up to 12 HA pairs, enabling petabyte-scale storage within a single availability zone for maximum throughput.

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

const testsingleazgen2 = new aws.fsx.OntapFileSystem("testsingleazgen2", {
    storageCapacity: 4096,
    subnetIds: [test1.id],
    deploymentType: "SINGLE_AZ_2",
    haPairs: 4,
    throughputCapacityPerHaPair: 384,
    preferredSubnetId: test1.id,
});
import pulumi
import pulumi_aws as aws

testsingleazgen2 = aws.fsx.OntapFileSystem("testsingleazgen2",
    storage_capacity=4096,
    subnet_ids=[test1["id"]],
    deployment_type="SINGLE_AZ_2",
    ha_pairs=4,
    throughput_capacity_per_ha_pair=384,
    preferred_subnet_id=test1["id"])
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := fsx.NewOntapFileSystem(ctx, "testsingleazgen2", &fsx.OntapFileSystemArgs{
			StorageCapacity: pulumi.Int(4096),
			SubnetIds: pulumi.StringArray{
				test1.Id,
			},
			DeploymentType:              pulumi.String("SINGLE_AZ_2"),
			HaPairs:                     pulumi.Int(4),
			ThroughputCapacityPerHaPair: pulumi.Int(384),
			PreferredSubnetId:           pulumi.Any(test1.Id),
		})
		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 testsingleazgen2 = new Aws.Fsx.OntapFileSystem("testsingleazgen2", new()
    {
        StorageCapacity = 4096,
        SubnetIds = new[]
        {
            test1.Id,
        },
        DeploymentType = "SINGLE_AZ_2",
        HaPairs = 4,
        ThroughputCapacityPerHaPair = 384,
        PreferredSubnetId = test1.Id,
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.fsx.OntapFileSystem;
import com.pulumi.aws.fsx.OntapFileSystemArgs;
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 testsingleazgen2 = new OntapFileSystem("testsingleazgen2", OntapFileSystemArgs.builder()
            .storageCapacity(4096)
            .subnetIds(test1.id())
            .deploymentType("SINGLE_AZ_2")
            .haPairs(4)
            .throughputCapacityPerHaPair(384)
            .preferredSubnetId(test1.id())
            .build());

    }
}
resources:
  testsingleazgen2:
    type: aws:fsx:OntapFileSystem
    properties:
      storageCapacity: 4096
      subnetIds:
        - ${test1.id}
      deploymentType: SINGLE_AZ_2
      haPairs: 4
      throughputCapacityPerHaPair: 384
      preferredSubnetId: ${test1.id}

Gen2 deployments (SINGLE_AZ_2) support higher HA pair counts and different throughput tiers than Gen1. With 4 HA pairs at 384 MBps each, this configuration delivers 1536 MBps total throughput. Gen2 also supports larger storage capacity ranges, up to 1 PB when using 2 or more HA pairs.

Beyond these examples

These snippets focus on specific file system features: multi-AZ and single-AZ deployment types, HA pair scaling and throughput configuration, and Gen1 and Gen2 architecture options. They’re intentionally minimal rather than full storage solutions.

The examples reference pre-existing infrastructure such as VPC subnets in appropriate availability zones, and route tables for endpoint creation. They focus on file system configuration rather than provisioning the surrounding network infrastructure.

To keep things focused, common file system patterns are omitted, including:

  • Security groups and network access control (securityGroupIds)
  • Encryption configuration (kmsKeyArn)
  • Backup scheduling (dailyAutomaticBackupStartTime, automaticBackupRetentionDays)
  • Maintenance windows (weeklyMaintenanceStartTime)
  • IOPS configuration (diskIopsConfiguration)

These omissions are intentional: the goal is to illustrate how each file system feature is wired, not provide drop-in storage modules. See the FSx ONTAP File System resource reference for all available configuration options.

Let's create AWS FSx for NetApp ONTAP File Systems

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

Try Pulumi Cloud for FREE

Frequently Asked Questions

Import & State Management
Why am I seeing a perpetual diff on security_group_ids after importing my file system?
The security_group_ids argument can’t be read from the FSx API after creation. Either omit security_group_ids from your Pulumi program or use ignoreChanges to suppress the diff.
Deployment Types & Configuration
What properties are immutable after creation?
You can’t change deploymentType, endpointIpAddressRange, kmsKeyId, preferredSubnetId, subnetIds, securityGroupIds, or storageType after creating the file system.
What's the difference between throughputCapacity and throughputCapacityPerHaPair?
These parameters are mutually exclusive. Use throughputCapacity when NOT specifying haPairs (it sets total throughput). Use throughputCapacityPerHaPair when you ARE specifying haPairs (it sets per-pair throughput). One must be specified.
How many HA pairs can I configure?
For SINGLE_AZ_1, MULTI_AZ_1, and MULTI_AZ_2, you can only configure 1 HA pair. For SINGLE_AZ_2, you can configure 1 to 12 HA pairs.
What are the valid throughput values for each deployment type?
For MULTI_AZ_1 and SINGLE_AZ_1 using throughputCapacityPerHaPair: 128, 256, 512, 1024, 2048, 4096 MBps. For MULTI_AZ_2 and SINGLE_AZ_2 with haPairs=1: 384, 768, 1536, 3072, 6144 MBps. For SINGLE_AZ_2 with haPairs>1: 1536, 3072, 6144 MBps.
Storage & Capacity
What are the storage capacity limits for FSx ONTAP?

Limits vary by deployment type:

  • SINGLE_AZ_1 and MULTI_AZ_1: 1024 to 196608 GiB
  • MULTI_AZ_2: 1024 to 524288 GiB
  • SINGLE_AZ_2: 1024 to 1048576 GiB (1PB), but the 1PB maximum requires 2 or more HA pairs; with 1 HA pair, the maximum is 524288 GiB (512TB)
Networking & IP Addressing
What IP address range does FSx ONTAP use for endpoints?
By default, FSx selects an unused IP address range from 198.19.*. Note that this range is also used by AWS WorkSpaces and AppStream 2.0 for management network interfaces, which may cause conflicts.
How many subnets do I need for each deployment type?
MULTI_AZ_1 and MULTI_AZ_2 require 2 subnet IDs (for high availability across availability zones). SINGLE_AZ_1 and SINGLE_AZ_2 require 1 subnet ID.
Backups & Maintenance
How do I configure automatic backups?
Set automaticBackupRetentionDays (0-90 days) and dailyAutomaticBackupStartTime (HH:MM format). The backup start time requires the retention days to be set.
What format should I use for the maintenance window?
Use d:HH:MM format in UTC time zone for weeklyMaintenanceStartTime, where d is the day of the week (1-7).
Deployment Examples
How do I configure a multi-AZ deployment?
Set deploymentType to MULTI_AZ_1 or MULTI_AZ_2, provide 2 subnet IDs in different availability zones, specify preferredSubnetId, and use either throughputCapacity (for gen 1) or haPairs with throughputCapacityPerHaPair (for gen 2).
How do I configure a SINGLE_AZ_2 deployment with multiple HA pairs?
Set deploymentType to SINGLE_AZ_2, specify haPairs (1-12), and use throughputCapacityPerHaPair instead of throughputCapacity. Storage capacity scales with HA pairs (1024 GiB per HA pair minimum).

Using a different cloud?

Explore storage guides for other cloud providers: