Configure AWS IVS Chat Logging

The aws:ivschat/loggingConfiguration:LoggingConfiguration resource, part of the Pulumi AWS provider, defines where IVS Chat logs are delivered: CloudWatch Logs, Kinesis Firehose, or S3. This guide focuses on three capabilities: CloudWatch Logs integration, Kinesis Firehose streaming, and direct S3 archival.

Logging configurations reference existing log groups, Firehose streams, or S3 buckets. The examples are intentionally small. Combine them with your own storage infrastructure and access policies.

Send chat logs to CloudWatch Logs

Teams monitoring live chat activity route logs to CloudWatch for real-time analysis and alerting.

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

const example = new aws.cloudwatch.LogGroup("example", {});
const exampleLoggingConfiguration = new aws.ivschat.LoggingConfiguration("example", {destinationConfiguration: {
    cloudwatchLogs: {
        logGroupName: example.name,
    },
}});
import pulumi
import pulumi_aws as aws

example = aws.cloudwatch.LogGroup("example")
example_logging_configuration = aws.ivschat.LoggingConfiguration("example", destination_configuration={
    "cloudwatch_logs": {
        "log_group_name": example.name,
    },
})
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		example, err := cloudwatch.NewLogGroup(ctx, "example", nil)
		if err != nil {
			return err
		}
		_, err = ivschat.NewLoggingConfiguration(ctx, "example", &ivschat.LoggingConfigurationArgs{
			DestinationConfiguration: &ivschat.LoggingConfigurationDestinationConfigurationArgs{
				CloudwatchLogs: &ivschat.LoggingConfigurationDestinationConfigurationCloudwatchLogsArgs{
					LogGroupName: example.Name,
				},
			},
		})
		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.CloudWatch.LogGroup("example");

    var exampleLoggingConfiguration = new Aws.IvsChat.LoggingConfiguration("example", new()
    {
        DestinationConfiguration = new Aws.IvsChat.Inputs.LoggingConfigurationDestinationConfigurationArgs
        {
            CloudwatchLogs = new Aws.IvsChat.Inputs.LoggingConfigurationDestinationConfigurationCloudwatchLogsArgs
            {
                LogGroupName = example.Name,
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.cloudwatch.LogGroup;
import com.pulumi.aws.ivschat.LoggingConfiguration;
import com.pulumi.aws.ivschat.LoggingConfigurationArgs;
import com.pulumi.aws.ivschat.inputs.LoggingConfigurationDestinationConfigurationArgs;
import com.pulumi.aws.ivschat.inputs.LoggingConfigurationDestinationConfigurationCloudwatchLogsArgs;
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 LogGroup("example");

        var exampleLoggingConfiguration = new LoggingConfiguration("exampleLoggingConfiguration", LoggingConfigurationArgs.builder()
            .destinationConfiguration(LoggingConfigurationDestinationConfigurationArgs.builder()
                .cloudwatchLogs(LoggingConfigurationDestinationConfigurationCloudwatchLogsArgs.builder()
                    .logGroupName(example.name())
                    .build())
                .build())
            .build());

    }
}
resources:
  example:
    type: aws:cloudwatch:LogGroup
  exampleLoggingConfiguration:
    type: aws:ivschat:LoggingConfiguration
    name: example
    properties:
      destinationConfiguration:
        cloudwatchLogs:
          logGroupName: ${example.name}

The destinationConfiguration property specifies where logs go. The cloudwatchLogs block points to an existing log group by name. IVS Chat writes chat events to this log group as they occur, enabling CloudWatch Insights queries and metric filters.

Stream chat logs through Firehose to S3

Applications that archive chat logs or feed them into analytics pipelines use Firehose to buffer and deliver records.

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

const exampleBucket = new aws.s3.Bucket("example", {bucketPrefix: "tf-ivschat-logging-bucket"});
const assumeRole = aws.iam.getPolicyDocument({
    statements: [{
        effect: "Allow",
        principals: [{
            type: "Service",
            identifiers: ["firehose.amazonaws.com"],
        }],
        actions: ["sts:AssumeRole"],
    }],
});
const exampleRole = new aws.iam.Role("example", {
    name: "firehose_example_role",
    assumeRolePolicy: assumeRole.then(assumeRole => assumeRole.json),
});
const example = new aws.kinesis.FirehoseDeliveryStream("example", {
    name: "pulumi-kinesis-firehose-extended-s3-example-stream",
    destination: "extended_s3",
    extendedS3Configuration: {
        roleArn: exampleRole.arn,
        bucketArn: exampleBucket.arn,
    },
    tags: {
        LogDeliveryEnabled: "true",
    },
});
const exampleBucketAcl = new aws.s3.BucketAcl("example", {
    bucket: exampleBucket.id,
    acl: "private",
});
const exampleLoggingConfiguration = new aws.ivschat.LoggingConfiguration("example", {destinationConfiguration: {
    firehose: {
        deliveryStreamName: example.name,
    },
}});
import pulumi
import pulumi_aws as aws

example_bucket = aws.s3.Bucket("example", bucket_prefix="tf-ivschat-logging-bucket")
assume_role = aws.iam.get_policy_document(statements=[{
    "effect": "Allow",
    "principals": [{
        "type": "Service",
        "identifiers": ["firehose.amazonaws.com"],
    }],
    "actions": ["sts:AssumeRole"],
}])
example_role = aws.iam.Role("example",
    name="firehose_example_role",
    assume_role_policy=assume_role.json)
example = aws.kinesis.FirehoseDeliveryStream("example",
    name="pulumi-kinesis-firehose-extended-s3-example-stream",
    destination="extended_s3",
    extended_s3_configuration={
        "role_arn": example_role.arn,
        "bucket_arn": example_bucket.arn,
    },
    tags={
        "LogDeliveryEnabled": "true",
    })
example_bucket_acl = aws.s3.BucketAcl("example",
    bucket=example_bucket.id,
    acl="private")
example_logging_configuration = aws.ivschat.LoggingConfiguration("example", destination_configuration={
    "firehose": {
        "delivery_stream_name": example.name,
    },
})
package main

import (
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/iam"
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/ivschat"
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/kinesis"
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/s3"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		exampleBucket, err := s3.NewBucket(ctx, "example", &s3.BucketArgs{
			BucketPrefix: pulumi.String("tf-ivschat-logging-bucket"),
		})
		if err != nil {
			return err
		}
		assumeRole, err := iam.GetPolicyDocument(ctx, &iam.GetPolicyDocumentArgs{
			Statements: []iam.GetPolicyDocumentStatement{
				{
					Effect: pulumi.StringRef("Allow"),
					Principals: []iam.GetPolicyDocumentStatementPrincipal{
						{
							Type: "Service",
							Identifiers: []string{
								"firehose.amazonaws.com",
							},
						},
					},
					Actions: []string{
						"sts:AssumeRole",
					},
				},
			},
		}, nil)
		if err != nil {
			return err
		}
		exampleRole, err := iam.NewRole(ctx, "example", &iam.RoleArgs{
			Name:             pulumi.String("firehose_example_role"),
			AssumeRolePolicy: pulumi.String(assumeRole.Json),
		})
		if err != nil {
			return err
		}
		example, err := kinesis.NewFirehoseDeliveryStream(ctx, "example", &kinesis.FirehoseDeliveryStreamArgs{
			Name:        pulumi.String("pulumi-kinesis-firehose-extended-s3-example-stream"),
			Destination: pulumi.String("extended_s3"),
			ExtendedS3Configuration: &kinesis.FirehoseDeliveryStreamExtendedS3ConfigurationArgs{
				RoleArn:   exampleRole.Arn,
				BucketArn: exampleBucket.Arn,
			},
			Tags: pulumi.StringMap{
				"LogDeliveryEnabled": pulumi.String("true"),
			},
		})
		if err != nil {
			return err
		}
		_, err = s3.NewBucketAcl(ctx, "example", &s3.BucketAclArgs{
			Bucket: exampleBucket.ID(),
			Acl:    pulumi.String("private"),
		})
		if err != nil {
			return err
		}
		_, err = ivschat.NewLoggingConfiguration(ctx, "example", &ivschat.LoggingConfigurationArgs{
			DestinationConfiguration: &ivschat.LoggingConfigurationDestinationConfigurationArgs{
				Firehose: &ivschat.LoggingConfigurationDestinationConfigurationFirehoseArgs{
					DeliveryStreamName: example.Name,
				},
			},
		})
		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 exampleBucket = new Aws.S3.Bucket("example", new()
    {
        BucketPrefix = "tf-ivschat-logging-bucket",
    });

    var assumeRole = Aws.Iam.GetPolicyDocument.Invoke(new()
    {
        Statements = new[]
        {
            new Aws.Iam.Inputs.GetPolicyDocumentStatementInputArgs
            {
                Effect = "Allow",
                Principals = new[]
                {
                    new Aws.Iam.Inputs.GetPolicyDocumentStatementPrincipalInputArgs
                    {
                        Type = "Service",
                        Identifiers = new[]
                        {
                            "firehose.amazonaws.com",
                        },
                    },
                },
                Actions = new[]
                {
                    "sts:AssumeRole",
                },
            },
        },
    });

    var exampleRole = new Aws.Iam.Role("example", new()
    {
        Name = "firehose_example_role",
        AssumeRolePolicy = assumeRole.Apply(getPolicyDocumentResult => getPolicyDocumentResult.Json),
    });

    var example = new Aws.Kinesis.FirehoseDeliveryStream("example", new()
    {
        Name = "pulumi-kinesis-firehose-extended-s3-example-stream",
        Destination = "extended_s3",
        ExtendedS3Configuration = new Aws.Kinesis.Inputs.FirehoseDeliveryStreamExtendedS3ConfigurationArgs
        {
            RoleArn = exampleRole.Arn,
            BucketArn = exampleBucket.Arn,
        },
        Tags = 
        {
            { "LogDeliveryEnabled", "true" },
        },
    });

    var exampleBucketAcl = new Aws.S3.BucketAcl("example", new()
    {
        Bucket = exampleBucket.Id,
        Acl = "private",
    });

    var exampleLoggingConfiguration = new Aws.IvsChat.LoggingConfiguration("example", new()
    {
        DestinationConfiguration = new Aws.IvsChat.Inputs.LoggingConfigurationDestinationConfigurationArgs
        {
            Firehose = new Aws.IvsChat.Inputs.LoggingConfigurationDestinationConfigurationFirehoseArgs
            {
                DeliveryStreamName = example.Name,
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.s3.Bucket;
import com.pulumi.aws.s3.BucketArgs;
import com.pulumi.aws.iam.IamFunctions;
import com.pulumi.aws.iam.inputs.GetPolicyDocumentArgs;
import com.pulumi.aws.iam.Role;
import com.pulumi.aws.iam.RoleArgs;
import com.pulumi.aws.kinesis.FirehoseDeliveryStream;
import com.pulumi.aws.kinesis.FirehoseDeliveryStreamArgs;
import com.pulumi.aws.kinesis.inputs.FirehoseDeliveryStreamExtendedS3ConfigurationArgs;
import com.pulumi.aws.s3.BucketAcl;
import com.pulumi.aws.s3.BucketAclArgs;
import com.pulumi.aws.ivschat.LoggingConfiguration;
import com.pulumi.aws.ivschat.LoggingConfigurationArgs;
import com.pulumi.aws.ivschat.inputs.LoggingConfigurationDestinationConfigurationArgs;
import com.pulumi.aws.ivschat.inputs.LoggingConfigurationDestinationConfigurationFirehoseArgs;
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 exampleBucket = new Bucket("exampleBucket", BucketArgs.builder()
            .bucketPrefix("tf-ivschat-logging-bucket")
            .build());

        final var assumeRole = IamFunctions.getPolicyDocument(GetPolicyDocumentArgs.builder()
            .statements(GetPolicyDocumentStatementArgs.builder()
                .effect("Allow")
                .principals(GetPolicyDocumentStatementPrincipalArgs.builder()
                    .type("Service")
                    .identifiers("firehose.amazonaws.com")
                    .build())
                .actions("sts:AssumeRole")
                .build())
            .build());

        var exampleRole = new Role("exampleRole", RoleArgs.builder()
            .name("firehose_example_role")
            .assumeRolePolicy(assumeRole.json())
            .build());

        var example = new FirehoseDeliveryStream("example", FirehoseDeliveryStreamArgs.builder()
            .name("pulumi-kinesis-firehose-extended-s3-example-stream")
            .destination("extended_s3")
            .extendedS3Configuration(FirehoseDeliveryStreamExtendedS3ConfigurationArgs.builder()
                .roleArn(exampleRole.arn())
                .bucketArn(exampleBucket.arn())
                .build())
            .tags(Map.of("LogDeliveryEnabled", "true"))
            .build());

        var exampleBucketAcl = new BucketAcl("exampleBucketAcl", BucketAclArgs.builder()
            .bucket(exampleBucket.id())
            .acl("private")
            .build());

        var exampleLoggingConfiguration = new LoggingConfiguration("exampleLoggingConfiguration", LoggingConfigurationArgs.builder()
            .destinationConfiguration(LoggingConfigurationDestinationConfigurationArgs.builder()
                .firehose(LoggingConfigurationDestinationConfigurationFirehoseArgs.builder()
                    .deliveryStreamName(example.name())
                    .build())
                .build())
            .build());

    }
}
resources:
  example:
    type: aws:kinesis:FirehoseDeliveryStream
    properties:
      name: pulumi-kinesis-firehose-extended-s3-example-stream
      destination: extended_s3
      extendedS3Configuration:
        roleArn: ${exampleRole.arn}
        bucketArn: ${exampleBucket.arn}
      tags:
        LogDeliveryEnabled: 'true'
  exampleBucket:
    type: aws:s3:Bucket
    name: example
    properties:
      bucketPrefix: tf-ivschat-logging-bucket
  exampleBucketAcl:
    type: aws:s3:BucketAcl
    name: example
    properties:
      bucket: ${exampleBucket.id}
      acl: private
  exampleRole:
    type: aws:iam:Role
    name: example
    properties:
      name: firehose_example_role
      assumeRolePolicy: ${assumeRole.json}
  exampleLoggingConfiguration:
    type: aws:ivschat:LoggingConfiguration
    name: example
    properties:
      destinationConfiguration:
        firehose:
          deliveryStreamName: ${example.name}
variables:
  assumeRole:
    fn::invoke:
      function: aws:iam:getPolicyDocument
      arguments:
        statements:
          - effect: Allow
            principals:
              - type: Service
                identifiers:
                  - firehose.amazonaws.com
            actions:
              - sts:AssumeRole

The firehose block references a Firehose delivery stream by name. Firehose buffers incoming chat events and writes them to the configured S3 destination. The extendedS3Configuration in the delivery stream controls buffering intervals, compression, and optional Lambda transformation. The IAM role grants Firehose permission to write to S3.

Write chat logs directly to S3

For simple archival without buffering or transformation, IVS Chat writes logs directly to S3.

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

const example = new aws.s3.Bucket("example", {
    bucketName: "tf-ivschat-logging",
    forceDestroy: true,
});
const exampleLoggingConfiguration = new aws.ivschat.LoggingConfiguration("example", {destinationConfiguration: {
    s3: {
        bucketName: example.id,
    },
}});
import pulumi
import pulumi_aws as aws

example = aws.s3.Bucket("example",
    bucket_name="tf-ivschat-logging",
    force_destroy=True)
example_logging_configuration = aws.ivschat.LoggingConfiguration("example", destination_configuration={
    "s3": {
        "bucket_name": example.id,
    },
})
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		example, err := s3.NewBucket(ctx, "example", &s3.BucketArgs{
			BucketName:   "tf-ivschat-logging",
			ForceDestroy: pulumi.Bool(true),
		})
		if err != nil {
			return err
		}
		_, err = ivschat.NewLoggingConfiguration(ctx, "example", &ivschat.LoggingConfigurationArgs{
			DestinationConfiguration: &ivschat.LoggingConfigurationDestinationConfigurationArgs{
				S3: &ivschat.LoggingConfigurationDestinationConfigurationS3Args{
					BucketName: example.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 example = new Aws.S3.Bucket("example", new()
    {
        BucketName = "tf-ivschat-logging",
        ForceDestroy = true,
    });

    var exampleLoggingConfiguration = new Aws.IvsChat.LoggingConfiguration("example", new()
    {
        DestinationConfiguration = new Aws.IvsChat.Inputs.LoggingConfigurationDestinationConfigurationArgs
        {
            S3 = new Aws.IvsChat.Inputs.LoggingConfigurationDestinationConfigurationS3Args
            {
                BucketName = example.Id,
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.s3.Bucket;
import com.pulumi.aws.s3.BucketArgs;
import com.pulumi.aws.ivschat.LoggingConfiguration;
import com.pulumi.aws.ivschat.LoggingConfigurationArgs;
import com.pulumi.aws.ivschat.inputs.LoggingConfigurationDestinationConfigurationArgs;
import com.pulumi.aws.ivschat.inputs.LoggingConfigurationDestinationConfigurationS3Args;
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 Bucket("example", BucketArgs.builder()
            .bucketName("tf-ivschat-logging")
            .forceDestroy(true)
            .build());

        var exampleLoggingConfiguration = new LoggingConfiguration("exampleLoggingConfiguration", LoggingConfigurationArgs.builder()
            .destinationConfiguration(LoggingConfigurationDestinationConfigurationArgs.builder()
                .s3(LoggingConfigurationDestinationConfigurationS3Args.builder()
                    .bucketName(example.id())
                    .build())
                .build())
            .build());

    }
}
resources:
  example:
    type: aws:s3:Bucket
    properties:
      bucketName: tf-ivschat-logging
      forceDestroy: true
  exampleLoggingConfiguration:
    type: aws:ivschat:LoggingConfiguration
    name: example
    properties:
      destinationConfiguration:
        s3:
          bucketName: ${example.id}

The s3 block specifies the target bucket by name. IVS Chat writes chat events directly to S3 without intermediate buffering. This approach is simpler than Firehose but lacks transformation and batching capabilities.

Beyond these examples

These snippets focus on specific logging configuration features: CloudWatch Logs integration, Kinesis Firehose streaming, and direct S3 archival. They’re intentionally minimal rather than full logging solutions.

The examples reference pre-existing infrastructure such as CloudWatch log groups, Kinesis Firehose delivery streams with IAM roles, and S3 buckets. They focus on configuring the logging destination rather than provisioning storage infrastructure.

To keep things focused, common logging patterns are omitted, including:

  • Logging configuration naming (name property)
  • Resource tagging for organization
  • Region-specific deployment (region property)

These omissions are intentional: the goal is to illustrate how each destination type is wired, not provide drop-in logging modules. See the IVS Chat Logging Configuration resource reference for all available configuration options.

Let's configure AWS IVS Chat Logging

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

Try Pulumi Cloud for FREE

Frequently Asked Questions

Destination Configuration
What logging destinations are available for IVS Chat?

You can log to three destinations (choose exactly one):

  1. CloudWatch Logs - Use cloudwatchLogs with logGroupName
  2. Kinesis Firehose - Use firehose with deliveryStreamName
  3. S3 - Use s3 with bucketName
Can I configure multiple logging destinations simultaneously?
No, destinationConfiguration must contain exactly one child argument (either cloudwatchLogs, firehose, or s3).
What IAM permissions does Kinesis Firehose need for IVS Chat logging?
The Firehose example shows an IAM role with firehose.amazonaws.com as the principal and sts:AssumeRole action. The role also needs permissions for the S3 destination (shown with roleArn in extendedS3Configuration).
Setup & Configuration
What fields are required to create an IVS Chat logging configuration?
Three fields are required: name (configuration name), region (defaults to provider region), and destinationConfiguration (with exactly one destination type).
Does the region need to match my IVS Chat room's region?
The region property determines where the logging configuration resource is managed and defaults to your provider configuration. The schema links to AWS regional endpoints documentation.

Using a different cloud?

Explore monitoring guides for other cloud providers: