The aws:oam/link:Link resource, part of the Pulumi AWS provider, connects a source AWS account to a monitoring account’s CloudWatch Observability Access Manager sink, controlling which telemetry types and data 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 that must exist in the monitoring account before the link is created. The examples are intentionally small. Combine them with your own sink configuration and IAM permissions for cross-account access.
Connect a source account to a monitoring sink
Cross-account observability starts by creating a link from a source account to a monitoring account’s sink, defining which telemetry flows between accounts.
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 link sends data from the source account to the monitoring account’s sink. The labelTemplate property controls how the source account appears in monitoring dashboards (here using the account name). The resourceTypes array specifies which telemetry types to share; this example shares CloudWatch metrics. The dependsOn meta-argument ensures the SinkPolicy exists before creating the link, preventing authorization failures.
Filter log groups by name pattern
When sharing logs across accounts, 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 block enables filtering. The logGroupConfiguration.filter property uses a SQL-like expression 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 specific AWS service metrics rather than all CloudWatch 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 metricConfiguration.filter property restricts which metrics flow to the monitoring account. This example shares only EC2, ELB, and S3 metrics by filtering on the Namespace attribute. Custom application metrics and other AWS service metrics remain in the source account only.
Beyond these examples
These snippets focus on specific link-level features: cross-account telemetry sharing, log group and metric filtering, and source account labeling. They’re intentionally minimal rather than complete cross-account monitoring solutions.
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’s 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
- Tag-based organization and cost tracking
These omissions are intentional: the goal is to illustrate how each link feature is wired, not provide drop-in cross-account monitoring 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
Resource Dependencies & Creation
aws.oam.SinkPolicy for the attached aws.oam.Sink isn’t created first. Use dependsOn to declare an explicit dependency on the SinkPolicy resource.dependsOn array, as shown in the basic usage example. This ensures the policy exists before the Link is created.Configuration & Resource Types
labelTemplate (human-readable name for the source account), sinkIdentifier (the sink to use), and resourceTypes (data types to share, like AWS::CloudWatch::Metric or AWS::Logs::LogGroup).labelTemplate and sinkIdentifier are immutable. You’ll need to recreate the Link to change either property.AWS::CloudWatch::Metric for metrics and AWS::Logs::LogGroup for logs. Specify these in the resourceTypes array.Filtering & Data Selection
linkConfiguration.logGroupConfiguration.filter with syntax like LogGroupName LIKE 'aws/lambda/%' OR LogGroupName LIKE 'AWSLogs%' to specify patterns.linkConfiguration.metricConfiguration.filter with syntax like Namespace IN ('AWS/EC2', 'AWS/ELB', 'AWS/S3') to specify namespaces.Using a different cloud?
Explore monitoring guides for other cloud providers: