The aws:oam/link:Link resource, part of the Pulumi AWS provider, connects a source account to a monitoring account’s sink, defining which telemetry types flow between accounts. This guide focuses on three capabilities: basic cross-account link setup, log group filtering by name pattern, and metric filtering by namespace.
Links depend on a Sink and SinkPolicy in the monitoring account. The SinkPolicy must exist before creating the link to authorize the connection. The examples are intentionally small. Combine them with your own Sink and SinkPolicy resources in the monitoring account.
Connect a source account to a monitoring sink
Cross-account observability starts by creating a link that defines which telemetry types flow to the monitoring account and how the source account appears in dashboards.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const exampleSink = new aws.oam.Sink("example", {});
const exampleSinkPolicy = new aws.oam.SinkPolicy("example", {sinkIdentifier: exampleSink.arn});
const example = new aws.oam.Link("example", {
labelTemplate: "$AccountName",
resourceTypes: ["AWS::CloudWatch::Metric"],
sinkIdentifier: exampleSink.arn,
tags: {
Env: "prod",
},
}, {
dependsOn: [exampleSinkPolicy],
});
import pulumi
import pulumi_aws as aws
example_sink = aws.oam.Sink("example")
example_sink_policy = aws.oam.SinkPolicy("example", sink_identifier=example_sink.arn)
example = aws.oam.Link("example",
label_template="$AccountName",
resource_types=["AWS::CloudWatch::Metric"],
sink_identifier=example_sink.arn,
tags={
"Env": "prod",
},
opts = pulumi.ResourceOptions(depends_on=[example_sink_policy]))
package main
import (
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/oam"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
exampleSink, err := oam.NewSink(ctx, "example", nil)
if err != nil {
return err
}
exampleSinkPolicy, err := oam.NewSinkPolicy(ctx, "example", &oam.SinkPolicyArgs{
SinkIdentifier: exampleSink.Arn,
})
if err != nil {
return err
}
_, err = oam.NewLink(ctx, "example", &oam.LinkArgs{
LabelTemplate: pulumi.String("$AccountName"),
ResourceTypes: pulumi.StringArray{
pulumi.String("AWS::CloudWatch::Metric"),
},
SinkIdentifier: exampleSink.Arn,
Tags: pulumi.StringMap{
"Env": pulumi.String("prod"),
},
}, pulumi.DependsOn([]pulumi.Resource{
exampleSinkPolicy,
}))
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 exampleSink = new Aws.Oam.Sink("example");
var exampleSinkPolicy = new Aws.Oam.SinkPolicy("example", new()
{
SinkIdentifier = exampleSink.Arn,
});
var example = new Aws.Oam.Link("example", new()
{
LabelTemplate = "$AccountName",
ResourceTypes = new[]
{
"AWS::CloudWatch::Metric",
},
SinkIdentifier = exampleSink.Arn,
Tags =
{
{ "Env", "prod" },
},
}, new CustomResourceOptions
{
DependsOn =
{
exampleSinkPolicy,
},
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.oam.Sink;
import com.pulumi.aws.oam.SinkPolicy;
import com.pulumi.aws.oam.SinkPolicyArgs;
import com.pulumi.aws.oam.Link;
import com.pulumi.aws.oam.LinkArgs;
import com.pulumi.resources.CustomResourceOptions;
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 exampleSink = new Sink("exampleSink");
var exampleSinkPolicy = new SinkPolicy("exampleSinkPolicy", SinkPolicyArgs.builder()
.sinkIdentifier(exampleSink.arn())
.build());
var example = new Link("example", LinkArgs.builder()
.labelTemplate("$AccountName")
.resourceTypes("AWS::CloudWatch::Metric")
.sinkIdentifier(exampleSink.arn())
.tags(Map.of("Env", "prod"))
.build(), CustomResourceOptions.builder()
.dependsOn(exampleSinkPolicy)
.build());
}
}
resources:
example:
type: aws:oam:Link
properties:
labelTemplate: $AccountName
resourceTypes:
- AWS::CloudWatch::Metric
sinkIdentifier: ${exampleSink.arn}
tags:
Env: prod
options:
dependsOn:
- ${exampleSinkPolicy}
exampleSink:
type: aws:oam:Sink
name: example
exampleSinkPolicy:
type: aws:oam:SinkPolicy
name: example
properties:
sinkIdentifier: ${exampleSink.arn}
The sinkIdentifier points to the monitoring account’s Sink ARN. The resourceTypes array specifies which telemetry types to share (metrics, logs, or traces). The labelTemplate defines how this source account appears in the monitoring account’s dashboards; $AccountName is replaced with the AWS account name at runtime. The dependsOn ensures the SinkPolicy exists before creating the link, preventing authorization failures.
Filter log groups by name pattern
When sharing logs, you can limit which log groups appear in the monitoring account to reduce noise and costs.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const example = new aws.oam.Link("example", {
labelTemplate: "$AccountName",
linkConfiguration: {
logGroupConfiguration: {
filter: "LogGroupName LIKE 'aws/lambda/%' OR LogGroupName LIKE 'AWSLogs%'",
},
},
resourceTypes: ["AWS::Logs::LogGroup"],
sinkIdentifier: exampleAwsOamSink.arn,
}, {
dependsOn: [exampleAwsOamSinkPolicy],
});
import pulumi
import pulumi_aws as aws
example = aws.oam.Link("example",
label_template="$AccountName",
link_configuration={
"log_group_configuration": {
"filter": "LogGroupName LIKE 'aws/lambda/%' OR LogGroupName LIKE 'AWSLogs%'",
},
},
resource_types=["AWS::Logs::LogGroup"],
sink_identifier=example_aws_oam_sink["arn"],
opts = pulumi.ResourceOptions(depends_on=[example_aws_oam_sink_policy]))
package main
import (
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/oam"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
_, err := oam.NewLink(ctx, "example", &oam.LinkArgs{
LabelTemplate: pulumi.String("$AccountName"),
LinkConfiguration: &oam.LinkLinkConfigurationArgs{
LogGroupConfiguration: &oam.LinkLinkConfigurationLogGroupConfigurationArgs{
Filter: pulumi.String("LogGroupName LIKE 'aws/lambda/%' OR LogGroupName LIKE 'AWSLogs%'"),
},
},
ResourceTypes: pulumi.StringArray{
pulumi.String("AWS::Logs::LogGroup"),
},
SinkIdentifier: pulumi.Any(exampleAwsOamSink.Arn),
}, pulumi.DependsOn([]pulumi.Resource{
exampleAwsOamSinkPolicy,
}))
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.Oam.Link("example", new()
{
LabelTemplate = "$AccountName",
LinkConfiguration = new Aws.Oam.Inputs.LinkLinkConfigurationArgs
{
LogGroupConfiguration = new Aws.Oam.Inputs.LinkLinkConfigurationLogGroupConfigurationArgs
{
Filter = "LogGroupName LIKE 'aws/lambda/%' OR LogGroupName LIKE 'AWSLogs%'",
},
},
ResourceTypes = new[]
{
"AWS::Logs::LogGroup",
},
SinkIdentifier = exampleAwsOamSink.Arn,
}, new CustomResourceOptions
{
DependsOn =
{
exampleAwsOamSinkPolicy,
},
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.oam.Link;
import com.pulumi.aws.oam.LinkArgs;
import com.pulumi.aws.oam.inputs.LinkLinkConfigurationArgs;
import com.pulumi.aws.oam.inputs.LinkLinkConfigurationLogGroupConfigurationArgs;
import com.pulumi.resources.CustomResourceOptions;
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 Link("example", LinkArgs.builder()
.labelTemplate("$AccountName")
.linkConfiguration(LinkLinkConfigurationArgs.builder()
.logGroupConfiguration(LinkLinkConfigurationLogGroupConfigurationArgs.builder()
.filter("LogGroupName LIKE 'aws/lambda/%' OR LogGroupName LIKE 'AWSLogs%'")
.build())
.build())
.resourceTypes("AWS::Logs::LogGroup")
.sinkIdentifier(exampleAwsOamSink.arn())
.build(), CustomResourceOptions.builder()
.dependsOn(exampleAwsOamSinkPolicy)
.build());
}
}
resources:
example:
type: aws:oam:Link
properties:
labelTemplate: $AccountName
linkConfiguration:
logGroupConfiguration:
filter: LogGroupName LIKE 'aws/lambda/%' OR LogGroupName LIKE 'AWSLogs%'
resourceTypes:
- AWS::Logs::LogGroup
sinkIdentifier: ${exampleAwsOamSink.arn}
options:
dependsOn:
- ${exampleAwsOamSinkPolicy}
The linkConfiguration.logGroupConfiguration.filter property uses a SQL-like syntax to match log group names. This example shares only Lambda function logs and AWS service logs, excluding application-specific log groups. The filter evaluates against the LogGroupName attribute using LIKE patterns.
Filter metrics by namespace
Metric filtering lets you share only specific AWS service metrics rather than all metrics from the source account.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const example = new aws.oam.Link("example", {
labelTemplate: "$AccountName",
linkConfiguration: {
metricConfiguration: {
filter: "Namespace IN ('AWS/EC2', 'AWS/ELB', 'AWS/S3')",
},
},
resourceTypes: ["AWS::CloudWatch::Metric"],
sinkIdentifier: exampleAwsOamSink.arn,
}, {
dependsOn: [exampleAwsOamSinkPolicy],
});
import pulumi
import pulumi_aws as aws
example = aws.oam.Link("example",
label_template="$AccountName",
link_configuration={
"metric_configuration": {
"filter": "Namespace IN ('AWS/EC2', 'AWS/ELB', 'AWS/S3')",
},
},
resource_types=["AWS::CloudWatch::Metric"],
sink_identifier=example_aws_oam_sink["arn"],
opts = pulumi.ResourceOptions(depends_on=[example_aws_oam_sink_policy]))
package main
import (
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/oam"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
_, err := oam.NewLink(ctx, "example", &oam.LinkArgs{
LabelTemplate: pulumi.String("$AccountName"),
LinkConfiguration: &oam.LinkLinkConfigurationArgs{
MetricConfiguration: &oam.LinkLinkConfigurationMetricConfigurationArgs{
Filter: pulumi.String("Namespace IN ('AWS/EC2', 'AWS/ELB', 'AWS/S3')"),
},
},
ResourceTypes: pulumi.StringArray{
pulumi.String("AWS::CloudWatch::Metric"),
},
SinkIdentifier: pulumi.Any(exampleAwsOamSink.Arn),
}, pulumi.DependsOn([]pulumi.Resource{
exampleAwsOamSinkPolicy,
}))
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.Oam.Link("example", new()
{
LabelTemplate = "$AccountName",
LinkConfiguration = new Aws.Oam.Inputs.LinkLinkConfigurationArgs
{
MetricConfiguration = new Aws.Oam.Inputs.LinkLinkConfigurationMetricConfigurationArgs
{
Filter = "Namespace IN ('AWS/EC2', 'AWS/ELB', 'AWS/S3')",
},
},
ResourceTypes = new[]
{
"AWS::CloudWatch::Metric",
},
SinkIdentifier = exampleAwsOamSink.Arn,
}, new CustomResourceOptions
{
DependsOn =
{
exampleAwsOamSinkPolicy,
},
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.oam.Link;
import com.pulumi.aws.oam.LinkArgs;
import com.pulumi.aws.oam.inputs.LinkLinkConfigurationArgs;
import com.pulumi.aws.oam.inputs.LinkLinkConfigurationMetricConfigurationArgs;
import com.pulumi.resources.CustomResourceOptions;
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 Link("example", LinkArgs.builder()
.labelTemplate("$AccountName")
.linkConfiguration(LinkLinkConfigurationArgs.builder()
.metricConfiguration(LinkLinkConfigurationMetricConfigurationArgs.builder()
.filter("Namespace IN ('AWS/EC2', 'AWS/ELB', 'AWS/S3')")
.build())
.build())
.resourceTypes("AWS::CloudWatch::Metric")
.sinkIdentifier(exampleAwsOamSink.arn())
.build(), CustomResourceOptions.builder()
.dependsOn(exampleAwsOamSinkPolicy)
.build());
}
}
resources:
example:
type: aws:oam:Link
properties:
labelTemplate: $AccountName
linkConfiguration:
metricConfiguration:
filter: Namespace IN ('AWS/EC2', 'AWS/ELB', 'AWS/S3')
resourceTypes:
- AWS::CloudWatch::Metric
sinkIdentifier: ${exampleAwsOamSink.arn}
options:
dependsOn:
- ${exampleAwsOamSinkPolicy}
The linkConfiguration.metricConfiguration.filter property uses a SQL-like IN clause to match metric namespaces. This example shares only EC2, ELB, and S3 metrics, excluding custom CloudWatch metrics. The Namespace attribute identifies the AWS service that published each metric.
Beyond these examples
These snippets focus on specific link-level features: cross-account telemetry sharing and log group and metric filtering. They’re intentionally minimal rather than full observability deployments.
The examples rely on pre-existing infrastructure such as a Sink resource in the monitoring account and a SinkPolicy authorizing the link. They focus on configuring the link rather than provisioning the monitoring account infrastructure.
To keep things focused, common link patterns are omitted, including:
- Sink and SinkPolicy creation (monitoring account setup)
- IAM permissions for cross-account access
- Multiple resource types in a single link
- Label template variables beyond $AccountName
These omissions are intentional: the goal is to illustrate how each link feature is wired, not provide drop-in observability modules. See the CloudWatch Observability Access Manager Link resource reference for all available configuration options.
Let's configure AWS CloudWatch Observability Access Manager Links
Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.
Try Pulumi Cloud for FREEFrequently Asked Questions
Creation & Dependencies
SinkPolicy for the attached Sink isn’t created first. Prevent this by declaring an explicit dependency using dependsOn to ensure the SinkPolicy is created before the Link.Filtering & Configuration
linkConfiguration.logGroupConfiguration.filter with a filter expression. For example: LogGroupName LIKE 'aws/lambda/%' OR LogGroupName LIKE 'AWSLogs%' to share only Lambda and AWSLogs log groups.linkConfiguration.metricConfiguration.filter with a filter expression. For example: Namespace IN ('AWS/EC2', 'AWS/ELB', 'AWS/S3') to share only EC2, ELB, and S3 metrics.Immutability & Updates
labelTemplate and sinkIdentifier are immutable. Changing either property requires recreating the Link resource.Using a different cloud?
Explore monitoring guides for other cloud providers: