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, Firehose streaming to S3, and direct S3 delivery.

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

Send chat logs to CloudWatch Logs

Teams monitoring live chat activity often route logs to CloudWatch for centralized observability alongside other application logs.

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 are sent. The cloudwatchLogs block routes chat activity to a CloudWatch log group, enabling queries, metric filters, and alarms. You must create the log group separately.

Stream chat logs through Firehose to S3

Applications that need to archive chat activity or feed logs into analytics pipelines route messages through Kinesis Firehose for transformation and delivery to S3.

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 sends logs to a Firehose delivery stream, which handles buffering, transformation, and S3 delivery. The deliveryStreamName references your stream. Firehose requires an IAM role with S3 write permissions and a destination bucket.

Write chat logs directly to S3

For simple archival without transformation, IVS Chat can write logs directly to S3, bypassing intermediate streaming services.

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 delivers logs straight to an S3 bucket. The bucketName property references your bucket. This approach skips Firehose’s buffering and transformation, providing simpler archival for compliance or long-term storage.

Beyond these examples

These snippets focus on specific logging configuration features: CloudWatch, Firehose, and S3 destinations. They’re intentionally minimal rather than full logging pipelines.

The examples reference pre-existing infrastructure such as CloudWatch log groups, Firehose delivery streams, S3 buckets, and IAM roles for Firehose (when using Firehose destination). They focus on configuring the logging destination rather than provisioning the underlying infrastructure.

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

  • Logging configuration naming (name property)
  • Resource tagging (tags property)
  • Cross-region configuration (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 LoggingConfiguration 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 CloudWatch Logs (cloudwatchLogs), Kinesis Firehose (firehose), or S3 (s3). Each destination type is shown in the provided examples.
Can I log to multiple destinations at once?
No, destinationConfiguration must contain exactly one destination type. Choose either cloudwatchLogs, firehose, or s3.
What's the difference between the s3 and firehose destinations?
Direct s3 logging writes to a bucket using bucketName. The firehose destination routes logs through a Kinesis Firehose delivery stream (with extended_s3 configuration), requiring additional IAM role setup.
Setup & IAM Requirements
What IAM configuration does Kinesis Firehose logging require?
The Firehose example shows an IAM role with a trust policy allowing firehose.amazonaws.com to assume the role via sts:AssumeRole. The role ARN is passed to extendedS3Configuration.roleArn.

Using a different cloud?

Explore monitoring guides for other cloud providers: