The aws:opensearch/serverlessAccessPolicy:ServerlessAccessPolicy resource, part of the Pulumi AWS provider, defines data access policies that control who can read, write, and manage 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 OpenSearch Serverless collections that must exist separately and may integrate with SAML identity providers. The examples are intentionally small. Combine them with your own collection definitions and identity infrastructure.
Grant full access to collections and indexes
During development, teams often grant broad permissions to a single principal for complete data access.
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 document contains Rules that specify ResourceType (collection or index), Resource patterns, and Permission arrays. Setting Permission to ["aoss:*"] grants all operations. The Principal array identifies who receives these permissions; here, it’s the current AWS caller’s ARN retrieved via getCallerIdentity.
Restrict access to read-only operations
Production environments limit principals to specific operations, preventing accidental modification.
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 read operations: aoss:ReadDocument for querying index data and aoss:DescribeIndex for metadata inspection. The collection-level permission aoss:DescribeCollectionItems allows listing indexes without modification rights. This pattern extends the full-access example by replacing aoss:* with explicit operation names.
Authorize SAML-federated users and groups
Organizations using SAML federation can grant access based on identity provider attributes.
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 references in the format saml/{account-id}/{provider-name}/user/{username} or saml/{account-id}/{provider-name}/group/{groupname}. This enables centralized access control through existing identity systems rather than managing individual IAM principals. The policy structure remains identical to IAM-based examples; only the Principal format changes.
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 solutions.
The examples reference pre-existing infrastructure such as OpenSearch Serverless collections and SAML identity providers (for federated access). They focus on configuring access policies rather than provisioning collections or identity infrastructure.
To keep things focused, common access policy patterns are omitted, including:
- Multiple collections in a single policy
- Wildcard resource patterns beyond single collection
- Mixed permission levels (some read-only, some full access)
- Policy versioning and updates
These omissions are intentional: the goal is to illustrate how each access policy feature is wired, not provide drop-in access control 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
Permissions & Access Control
aoss:DescribeIndex, aoss:ReadDocument, and aoss:DescribeCollectionItems, or use aoss:* to grant all permissions. The AWS documentation lists all supported data access policy permissions.aoss:DescribeIndex and aoss:ReadDocument for indexes, and aoss:DescribeCollectionItems for collections. This prevents write operations while allowing data access.Permission to ["aoss:*"] for both index and collection resource types. This grants all available permissions for the specified resources.Policy Structure & Configuration
index/example-collection/* to match all indexes within a collection.ResourceType: "index") control access to specific indexes or index patterns, while collection resources (ResourceType: "collection") control access to collection-level operations. Both can be specified in the same policy’s Rules array.Identity & Principals
Principal array, such as the ARN returned by aws.getCallerIdentity().saml/account-id/provider/user/username for users or saml/account-id/provider/group/groupname for groups in the Principal array.