The aws:opensearch/serverlessAccessPolicy:ServerlessAccessPolicy resource, part of the Pulumi AWS provider, defines data access policies that control which principals can perform operations on OpenSearch Serverless collections and indexes. This guide focuses on three capabilities: IAM and SAML principal authorization, collection and index-level permissions, and read-only vs full-access patterns.
Access policies reference existing OpenSearch Serverless collections and IAM or SAML principals. The examples are intentionally small. Combine them with your own collection infrastructure and identity management.
Grant full access to collections and indexes
Most deployments start by granting a principal full access to collections and indexes for development or administrative workflows.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const current = aws.getCallerIdentity({});
const example = new aws.opensearch.ServerlessAccessPolicy("example", {
name: "example",
type: "data",
description: "read and write permissions",
policy: JSON.stringify([{
Rules: [
{
ResourceType: "index",
Resource: ["index/example-collection/*"],
Permission: ["aoss:*"],
},
{
ResourceType: "collection",
Resource: ["collection/example-collection"],
Permission: ["aoss:*"],
},
],
Principal: [current.then(current => current.arn)],
}]),
});
import pulumi
import json
import pulumi_aws as aws
current = aws.get_caller_identity()
example = aws.opensearch.ServerlessAccessPolicy("example",
name="example",
type="data",
description="read and write permissions",
policy=json.dumps([{
"Rules": [
{
"ResourceType": "index",
"Resource": ["index/example-collection/*"],
"Permission": ["aoss:*"],
},
{
"ResourceType": "collection",
"Resource": ["collection/example-collection"],
"Permission": ["aoss:*"],
},
],
"Principal": [current.arn],
}]))
package main
import (
"encoding/json"
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws"
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/opensearch"
"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
}
tmpJSON0, err := json.Marshal([]map[string]interface{}{
map[string]interface{}{
"Rules": []map[string]interface{}{
map[string]interface{}{
"ResourceType": "index",
"Resource": []string{
"index/example-collection/*",
},
"Permission": []string{
"aoss:*",
},
},
map[string]interface{}{
"ResourceType": "collection",
"Resource": []string{
"collection/example-collection",
},
"Permission": []string{
"aoss:*",
},
},
},
"Principal": []*string{
current.Arn,
},
},
})
if err != nil {
return err
}
json0 := string(tmpJSON0)
_, err = opensearch.NewServerlessAccessPolicy(ctx, "example", &opensearch.ServerlessAccessPolicyArgs{
Name: pulumi.String("example"),
Type: pulumi.String("data"),
Description: pulumi.String("read and write permissions"),
Policy: 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 current = Aws.GetCallerIdentity.Invoke();
var example = new Aws.OpenSearch.ServerlessAccessPolicy("example", new()
{
Name = "example",
Type = "data",
Description = "read and write permissions",
Policy = JsonSerializer.Serialize(new[]
{
new Dictionary<string, object?>
{
["Rules"] = new[]
{
new Dictionary<string, object?>
{
["ResourceType"] = "index",
["Resource"] = new[]
{
"index/example-collection/*",
},
["Permission"] = new[]
{
"aoss:*",
},
},
new Dictionary<string, object?>
{
["ResourceType"] = "collection",
["Resource"] = new[]
{
"collection/example-collection",
},
["Permission"] = new[]
{
"aoss:*",
},
},
},
["Principal"] = new[]
{
current.Apply(getCallerIdentityResult => getCallerIdentityResult.Arn),
},
},
}),
});
});
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.opensearch.ServerlessAccessPolicy;
import com.pulumi.aws.opensearch.ServerlessAccessPolicyArgs;
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());
var example = new ServerlessAccessPolicy("example", ServerlessAccessPolicyArgs.builder()
.name("example")
.type("data")
.description("read and write permissions")
.policy(serializeJson(
jsonArray(jsonObject(
jsonProperty("Rules", jsonArray(
jsonObject(
jsonProperty("ResourceType", "index"),
jsonProperty("Resource", jsonArray("index/example-collection/*")),
jsonProperty("Permission", jsonArray("aoss:*"))
),
jsonObject(
jsonProperty("ResourceType", "collection"),
jsonProperty("Resource", jsonArray("collection/example-collection")),
jsonProperty("Permission", jsonArray("aoss:*"))
)
)),
jsonProperty("Principal", jsonArray(current.arn()))
))))
.build());
}
}
resources:
example:
type: aws:opensearch:ServerlessAccessPolicy
properties:
name: example
type: data
description: read and write permissions
policy:
fn::toJSON:
- Rules:
- ResourceType: index
Resource:
- index/example-collection/*
Permission:
- aoss:*
- ResourceType: collection
Resource:
- collection/example-collection
Permission:
- aoss:*
Principal:
- ${current.arn}
variables:
current:
fn::invoke:
function: aws:getCallerIdentity
arguments: {}
The policy property contains a JSON document with Rules that specify ResourceType (collection or index), Resource patterns, and Permission arrays. The aoss:* permission grants all operations. Principal references the current caller’s ARN, allowing that identity to read, write, and manage the collection and its indexes.
Restrict access to read-only operations
Applications that only query data benefit from read-only permissions that prevent accidental modifications.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const current = aws.getCallerIdentity({});
const example = new aws.opensearch.ServerlessAccessPolicy("example", {
name: "example",
type: "data",
description: "read-only permissions",
policy: JSON.stringify([{
Rules: [
{
ResourceType: "index",
Resource: ["index/example-collection/*"],
Permission: [
"aoss:DescribeIndex",
"aoss:ReadDocument",
],
},
{
ResourceType: "collection",
Resource: ["collection/example-collection"],
Permission: ["aoss:DescribeCollectionItems"],
},
],
Principal: [current.then(current => current.arn)],
}]),
});
import pulumi
import json
import pulumi_aws as aws
current = aws.get_caller_identity()
example = aws.opensearch.ServerlessAccessPolicy("example",
name="example",
type="data",
description="read-only permissions",
policy=json.dumps([{
"Rules": [
{
"ResourceType": "index",
"Resource": ["index/example-collection/*"],
"Permission": [
"aoss:DescribeIndex",
"aoss:ReadDocument",
],
},
{
"ResourceType": "collection",
"Resource": ["collection/example-collection"],
"Permission": ["aoss:DescribeCollectionItems"],
},
],
"Principal": [current.arn],
}]))
package main
import (
"encoding/json"
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws"
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/opensearch"
"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
}
tmpJSON0, err := json.Marshal([]map[string]interface{}{
map[string]interface{}{
"Rules": []interface{}{
map[string]interface{}{
"ResourceType": "index",
"Resource": []string{
"index/example-collection/*",
},
"Permission": []string{
"aoss:DescribeIndex",
"aoss:ReadDocument",
},
},
map[string]interface{}{
"ResourceType": "collection",
"Resource": []string{
"collection/example-collection",
},
"Permission": []string{
"aoss:DescribeCollectionItems",
},
},
},
"Principal": []*string{
current.Arn,
},
},
})
if err != nil {
return err
}
json0 := string(tmpJSON0)
_, err = opensearch.NewServerlessAccessPolicy(ctx, "example", &opensearch.ServerlessAccessPolicyArgs{
Name: pulumi.String("example"),
Type: pulumi.String("data"),
Description: pulumi.String("read-only permissions"),
Policy: 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 current = Aws.GetCallerIdentity.Invoke();
var example = new Aws.OpenSearch.ServerlessAccessPolicy("example", new()
{
Name = "example",
Type = "data",
Description = "read-only permissions",
Policy = JsonSerializer.Serialize(new[]
{
new Dictionary<string, object?>
{
["Rules"] = new[]
{
new Dictionary<string, object?>
{
["ResourceType"] = "index",
["Resource"] = new[]
{
"index/example-collection/*",
},
["Permission"] = new[]
{
"aoss:DescribeIndex",
"aoss:ReadDocument",
},
},
new Dictionary<string, object?>
{
["ResourceType"] = "collection",
["Resource"] = new[]
{
"collection/example-collection",
},
["Permission"] = new[]
{
"aoss:DescribeCollectionItems",
},
},
},
["Principal"] = new[]
{
current.Apply(getCallerIdentityResult => getCallerIdentityResult.Arn),
},
},
}),
});
});
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.opensearch.ServerlessAccessPolicy;
import com.pulumi.aws.opensearch.ServerlessAccessPolicyArgs;
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());
var example = new ServerlessAccessPolicy("example", ServerlessAccessPolicyArgs.builder()
.name("example")
.type("data")
.description("read-only permissions")
.policy(serializeJson(
jsonArray(jsonObject(
jsonProperty("Rules", jsonArray(
jsonObject(
jsonProperty("ResourceType", "index"),
jsonProperty("Resource", jsonArray("index/example-collection/*")),
jsonProperty("Permission", jsonArray(
"aoss:DescribeIndex",
"aoss:ReadDocument"
))
),
jsonObject(
jsonProperty("ResourceType", "collection"),
jsonProperty("Resource", jsonArray("collection/example-collection")),
jsonProperty("Permission", jsonArray("aoss:DescribeCollectionItems"))
)
)),
jsonProperty("Principal", jsonArray(current.arn()))
))))
.build());
}
}
resources:
example:
type: aws:opensearch:ServerlessAccessPolicy
properties:
name: example
type: data
description: read-only permissions
policy:
fn::toJSON:
- Rules:
- ResourceType: index
Resource:
- index/example-collection/*
Permission:
- aoss:DescribeIndex
- aoss:ReadDocument
- ResourceType: collection
Resource:
- collection/example-collection
Permission:
- aoss:DescribeCollectionItems
Principal:
- ${current.arn}
variables:
current:
fn::invoke:
function: aws:getCallerIdentity
arguments: {}
This configuration restricts permissions to describe and read operations. For indexes, aoss:DescribeIndex and aoss:ReadDocument allow querying without writes. For collections, aoss:DescribeCollectionItems permits listing contents. This pattern extends the full-access example by replacing aoss:* with specific operation permissions.
Grant access to SAML-federated identities
Organizations using SAML federation can grant access to federated users and groups without creating individual IAM principals.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const example = new aws.opensearch.ServerlessAccessPolicy("example", {
name: "example",
type: "data",
description: "saml permissions",
policy: JSON.stringify([{
Rules: [
{
ResourceType: "index",
Resource: ["index/example-collection/*"],
Permission: ["aoss:*"],
},
{
ResourceType: "collection",
Resource: ["collection/example-collection"],
Permission: ["aoss:*"],
},
],
Principal: [
"saml/123456789012/myprovider/user/Annie",
"saml/123456789012/anotherprovider/group/Accounting",
],
}]),
});
import pulumi
import json
import pulumi_aws as aws
example = aws.opensearch.ServerlessAccessPolicy("example",
name="example",
type="data",
description="saml permissions",
policy=json.dumps([{
"Rules": [
{
"ResourceType": "index",
"Resource": ["index/example-collection/*"],
"Permission": ["aoss:*"],
},
{
"ResourceType": "collection",
"Resource": ["collection/example-collection"],
"Permission": ["aoss:*"],
},
],
"Principal": [
"saml/123456789012/myprovider/user/Annie",
"saml/123456789012/anotherprovider/group/Accounting",
],
}]))
package main
import (
"encoding/json"
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/opensearch"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
tmpJSON0, err := json.Marshal([]map[string]interface{}{
map[string]interface{}{
"Rules": []map[string]interface{}{
map[string]interface{}{
"ResourceType": "index",
"Resource": []string{
"index/example-collection/*",
},
"Permission": []string{
"aoss:*",
},
},
map[string]interface{}{
"ResourceType": "collection",
"Resource": []string{
"collection/example-collection",
},
"Permission": []string{
"aoss:*",
},
},
},
"Principal": []string{
"saml/123456789012/myprovider/user/Annie",
"saml/123456789012/anotherprovider/group/Accounting",
},
},
})
if err != nil {
return err
}
json0 := string(tmpJSON0)
_, err = opensearch.NewServerlessAccessPolicy(ctx, "example", &opensearch.ServerlessAccessPolicyArgs{
Name: pulumi.String("example"),
Type: pulumi.String("data"),
Description: pulumi.String("saml permissions"),
Policy: 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 example = new Aws.OpenSearch.ServerlessAccessPolicy("example", new()
{
Name = "example",
Type = "data",
Description = "saml permissions",
Policy = JsonSerializer.Serialize(new[]
{
new Dictionary<string, object?>
{
["Rules"] = new[]
{
new Dictionary<string, object?>
{
["ResourceType"] = "index",
["Resource"] = new[]
{
"index/example-collection/*",
},
["Permission"] = new[]
{
"aoss:*",
},
},
new Dictionary<string, object?>
{
["ResourceType"] = "collection",
["Resource"] = new[]
{
"collection/example-collection",
},
["Permission"] = new[]
{
"aoss:*",
},
},
},
["Principal"] = new[]
{
"saml/123456789012/myprovider/user/Annie",
"saml/123456789012/anotherprovider/group/Accounting",
},
},
}),
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.opensearch.ServerlessAccessPolicy;
import com.pulumi.aws.opensearch.ServerlessAccessPolicyArgs;
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 example = new ServerlessAccessPolicy("example", ServerlessAccessPolicyArgs.builder()
.name("example")
.type("data")
.description("saml permissions")
.policy(serializeJson(
jsonArray(jsonObject(
jsonProperty("Rules", jsonArray(
jsonObject(
jsonProperty("ResourceType", "index"),
jsonProperty("Resource", jsonArray("index/example-collection/*")),
jsonProperty("Permission", jsonArray("aoss:*"))
),
jsonObject(
jsonProperty("ResourceType", "collection"),
jsonProperty("Resource", jsonArray("collection/example-collection")),
jsonProperty("Permission", jsonArray("aoss:*"))
)
)),
jsonProperty("Principal", jsonArray(
"saml/123456789012/myprovider/user/Annie",
"saml/123456789012/anotherprovider/group/Accounting"
))
))))
.build());
}
}
resources:
example:
type: aws:opensearch:ServerlessAccessPolicy
properties:
name: example
type: data
description: saml permissions
policy:
fn::toJSON:
- Rules:
- ResourceType: index
Resource:
- index/example-collection/*
Permission:
- aoss:*
- ResourceType: collection
Resource:
- collection/example-collection
Permission:
- aoss:*
Principal:
- saml/123456789012/myprovider/user/Annie
- saml/123456789012/anotherprovider/group/Accounting
The Principal array accepts SAML identity paths in the format saml/{account-id}/{provider-name}/user/{username} or saml/{account-id}/{provider-name}/group/{groupname}. This allows centralized identity management through your SAML provider rather than managing IAM users directly.
Beyond these examples
These snippets focus on specific access policy features: IAM and SAML principal authorization, collection and index-level permissions, and read-only vs full-access patterns. They’re intentionally minimal rather than full access control systems.
The examples reference pre-existing infrastructure such as OpenSearch Serverless collections and SAML identity providers (for federation example). They focus on configuring access policies rather than provisioning collections or identity infrastructure.
To keep things focused, common access policy patterns are omitted, including:
- Policy versioning and updates (policyVersion)
- Cross-region access policies
- Wildcard resource patterns beyond single collection
- Time-based or conditional access rules
These omissions are intentional: the goal is to illustrate how each access policy feature is wired, not provide drop-in security modules. See the OpenSearch Serverless Access Policy resource reference for all available configuration options.
Let's configure AWS OpenSearch Serverless Access Policies
Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.
Try Pulumi Cloud for FREEFrequently Asked Questions
Policy Structure & Configuration
Rules and Principal fields. Each rule specifies ResourceType (either index or collection), Resource (the resource path), and Permission (an array of allowed actions).aoss:DescribeCollectionItems) apply to the collection itself, while index permissions (e.g., aoss:ReadDocument) apply to indices within the collection. Most policies need both types to function properly.type field must be set to data, which is currently the only valid value for OpenSearch Serverless access policies.Permissions & Access Control
aoss:*. For indices, use aoss:DescribeIndex and aoss:ReadDocument. For collections, use aoss:DescribeCollectionItems.index/example-collection/* to match all indices within a collection. This is the standard pattern for granting index-level permissions.Principal array, use the format saml/account-id/provider/user/username for individual users or saml/account-id/provider/group/groupname for groups.