Overview
This guide provides Terraform configuration examples for setting up the AWS infrastructure required for Meter's SIEM integration. You'll create a Kinesis Data Stream and IAM role that allows Meter to securely write security events to your AWS account.
Prerequisites
- Terraform installed (version 1.0 or later recommended)
- AWS account with permissions to create IAM roles, policies, and Kinesis streams
- AWS credentials configured for Terraform (via environment variables, AWS CLI, or IAM role)
- Basic familiarity with Terraform and AWS IAM concepts
Use cases
- Automate SIEM integration infrastructure deployment across multiple AWS accounts
- Maintain infrastructure-as-code for audit and compliance requirements
- Quickly replicate SIEM setup for dev, staging, and production environments
- Version control your security integration configuration
Minimal configuration
This minimal example creates the required AWS resources with secure defaults.
Variables
Define these variables in your Terraform configuration:
variable "aws_region" {
description = "AWS region for Kinesis stream"
type = string
default = "us-east-1"
}
variable "meter_service_role_arn" {
description = "ARN of Meter's IAM service role that will assume your role"
type = string
default = "arn:aws:iam::458553032353:role/Meter-data-export"
}
variable "external_id" {
description = "Secret external ID for secure role assumption"
type = string
sensitive = true
}
variable "kinesis_stream_name" {
description = "Name of the Kinesis stream for SIEM events"
type = string
default = "meter-siem-events"
}
variable "kinesis_shard_count" {
description = "Number of shards for the Kinesis stream"
type = number
default = 1
}
variable "kinesis_retention_hours" {
description = "Data retention period in hours (24-8760)"
type = number
default = 24
}Provider configuration
terraform {
required_version = ">= 1.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
provider "aws" {
region = var.aws_region
}Kinesis Data Stream
Create a Kinesis stream to receive events from Meter:
resource "aws_kinesis_stream" "meter_siem" {
name = var.kinesis_stream_name
shard_count = var.kinesis_shard_count
retention_period = var.kinesis_retention_hours
shard_level_metrics = [
"IncomingBytes",
"IncomingRecords",
"OutgoingBytes",
"OutgoingRecords",
]
stream_mode_details {
stream_mode = "PROVISIONED"
}
tags = {
Name = "Meter SIEM Integration"
ManagedBy = "Terraform"
Purpose = "Security Event Ingestion"
}
}IAM role for Meter
Create an IAM role that Meter can assume to write to your Kinesis stream:
resource "aws_iam_role" "meter_siem_publisher" {
name = "MeterSIEMIntegrationRole"
description = "Allows Meter to publish security events to Kinesis"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Principal = {
AWS = var.meter_service_role_arn
}
Action = "sts:AssumeRole"
Condition = {
StringEquals = {
"sts:ExternalId" = var.external_id
}
}
}
]
})
tags = {
Name = "Meter SIEM Integration"
ManagedBy = "Terraform"
}
}IAM policy for Kinesis write access
Create a policy that grants write permissions to the Kinesis stream:
resource "aws_iam_policy" "meter_kinesis_write" {
name = "MeterSIEMKinesisWritePolicy"
description = "Allows writing records to Meter SIEM Kinesis stream"
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = [
"kinesis:PutRecord",
"kinesis:PutRecords"
]
Resource = aws_kinesis_stream.meter_siem.arn
}
]
})
}Attach policy to role
resource "aws_iam_role_policy_attachment" "meter_kinesis_attach" {
role = aws_iam_role.meter_siem_publisher.name
policy_arn = aws_iam_policy.meter_kinesis_write.arn
}Outputs
Export the values needed for Dashboard configuration:
output "kinesis_stream_arn" {
description = "ARN of the Kinesis stream - use this in Dashboard"
value = aws_kinesis_stream.meter_siem.arn
}
output "iam_role_arn" {
description = "ARN of the IAM role - use this in Dashboard"
value = aws_iam_role.meter_siem_publisher.arn
}
output "external_id" {
description = "External ID for role assumption - use this in Dashboard"
value = var.external_id
sensitive = true
}
output "kinesis_stream_name" {
description = "Name of the Kinesis stream"
value = aws_kinesis_stream.meter_siem.name
}Configuration options
Kinesis stream settings
| Parameter | Type | Default | Description |
|---|---|---|---|
name | string | Required | Name of the Kinesis stream |
shard_count | number | 1 | Number of shards (each shard: 1 MB/sec in, 2 MB/sec out) |
retention_period | number | 24 | Data retention in hours (24-8760) |
stream_mode | string | "PROVISIONED" | Use "PROVISIONED" for predictable costs, "ON_DEMAND" for variable traffic |
shard_level_metrics | list | See example | CloudWatch metrics to enable for monitoring |
IAM role configuration
| Parameter | Type | Required | Description |
|---|---|---|---|
meter_service_role_arn | string | Yes | ARN of Meter's service role (provided by Meter) |
external_id | string | Yes | Random secret string (64+ characters recommended) |
Advanced configuration
On-demand Kinesis stream
For unpredictable event volumes, use on-demand mode:
resource "aws_kinesis_stream" "meter_siem" {
name = var.kinesis_stream_name
retention_period = var.kinesis_retention_hours
stream_mode_details {
stream_mode = "ON_DEMAND"
}
shard_level_metrics = [
"IncomingBytes",
"IncomingRecords",
]
tags = {
Name = "Meter SIEM Integration (On-Demand)"
}
}Enhanced monitoring with CloudWatch alarms
Add CloudWatch alarms to monitor integration health:
resource "aws_cloudwatch_metric_alarm" "kinesis_no_incoming_records" {
alarm_name = "meter-siem-no-events"
comparison_operator = "LessThanThreshold"
evaluation_periods = 2
metric_name = "IncomingRecords"
namespace = "AWS/Kinesis"
period = 300
statistic = "Sum"
threshold = 1
alarm_description = "Alert if no SIEM events received for 10 minutes"
treat_missing_data = "breaching"
dimensions = {
StreamName = aws_kinesis_stream.meter_siem.name
}
alarm_actions = [var.sns_topic_arn]
}
resource "aws_cloudwatch_metric_alarm" "kinesis_write_throttled" {
alarm_name = "meter-siem-write-throttled"
comparison_operator = "GreaterThanThreshold"
evaluation_periods = 1
metric_name = "WriteProvisionedThroughputExceeded"
namespace = "AWS/Kinesis"
period = 60
statistic = "Sum"
threshold = 0
alarm_description = "Alert if Kinesis writes are being throttled"
dimensions = {
StreamName = aws_kinesis_stream.meter_siem.name
}
alarm_actions = [var.sns_topic_arn]
}Server-side encryption
Enable encryption at rest for compliance:
resource "aws_kinesis_stream" "meter_siem" {
name = var.kinesis_stream_name
shard_count = var.kinesis_shard_count
retention_period = var.kinesis_retention_hours
encryption_type = "KMS"
kms_key_id = aws_kms_key.kinesis_encryption.id
stream_mode_details {
stream_mode = "PROVISIONED"
}
}
resource "aws_kms_key" "kinesis_encryption" {
description = "KMS key for Meter SIEM Kinesis stream encryption"
deletion_window_in_days = 10
enable_key_rotation = true
tags = {
Name = "Meter SIEM Kinesis Encryption"
}
}
resource "aws_kms_alias" "kinesis_encryption" {
name = "alias/meter-siem-kinesis"
target_key_id = aws_kms_key.kinesis_encryption.key_id
}
# Update IAM policy to allow KMS operations
resource "aws_iam_policy" "meter_kinesis_write_encrypted" {
name = "MeterSIEMKinesisWritePolicy"
description = "Allows writing records to encrypted Meter SIEM Kinesis stream"
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = [
"kinesis:PutRecord",
"kinesis:PutRecords"
]
Resource = aws_kinesis_stream.meter_siem.arn
},
{
Effect = "Allow"
Action = [
"kms:Decrypt",
"kms:GenerateDataKey"
]
Resource = aws_kms_key.kinesis_encryption.arn
}
]
})
}VPC endpoints for private connectivity
Route Kinesis traffic through your VPC:
resource "aws_vpc_endpoint" "kinesis_streams" {
vpc_id = var.vpc_id
service_name = "com.amazonaws.${var.aws_region}.kinesis-streams"
vpc_endpoint_type = "Interface"
subnet_ids = var.private_subnet_ids
security_group_ids = [
aws_security_group.kinesis_endpoint.id
]
private_dns_enabled = true
tags = {
Name = "Meter SIEM Kinesis VPC Endpoint"
}
}
resource "aws_security_group" "kinesis_endpoint" {
name = "meter-siem-kinesis-endpoint"
description = "Security group for Kinesis VPC endpoint"
vpc_id = var.vpc_id
ingress {
description = "HTTPS from VPC"
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = [var.vpc_cidr]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}Integration with Kinesis Firehose
Forward events to S3 for long-term storage:
resource "aws_kinesis_firehose_delivery_stream" "meter_siem_to_s3" {
name = "meter-siem-to-s3"
destination = "extended_s3"
kinesis_source_configuration {
kinesis_stream_arn = aws_kinesis_stream.meter_siem.arn
role_arn = aws_iam_role.firehose_role.arn
}
extended_s3_configuration {
role_arn = aws_iam_role.firehose_role.arn
bucket_arn = aws_s3_bucket.siem_events.arn
prefix = "siem-events/year=!{timestamp:yyyy}/month=!{timestamp:MM}/day=!{timestamp:dd}/"
compression_format = "GZIP"
buffer_size = 5
buffer_interval = 300
}
}
resource "aws_s3_bucket" "siem_events" {
bucket = "your-org-meter-siem-events"
}
resource "aws_s3_bucket_lifecycle_configuration" "siem_events_lifecycle" {
bucket = aws_s3_bucket.siem_events.id
rule {
id = "archive-old-events"
status = "Enabled"
transition {
days = 90
storage_class = "GLACIER"
}
expiration {
days = 365
}
}
}Best practices
Security
- Store external ID securely: Never commit the external ID to version control. Use Terraform variables with
sensitive = trueand store the value in AWS Secrets Manager or a secure secrets management tool. - Enable encryption: Use KMS encryption for streams containing sensitive security data.
- Least privilege: Only grant
PutRecordandPutRecordspermissions, not full Kinesis access. - Enable CloudTrail: Log all API calls to your Kinesis stream and IAM role for audit purposes.
Cost optimization
- Right-size shards: Start with 1 shard and monitor
IncomingBytesandWriteProvisionedThroughputExceededmetrics. Add shards only when needed. - Optimize retention: The default 24-hour retention is sufficient if you're consuming events in real-time. Longer retention increases costs.
- Use on-demand carefully: On-demand mode is convenient but can be more expensive for consistent, high-volume streams.
Operational
- Tag resources: Add consistent tags for cost tracking and resource management.
- Monitor metrics: Enable shard-level metrics and set up CloudWatch alarms for proactive monitoring.
- Test before production: Deploy to a test environment first and send test events to validate the configuration.
- Document your setup: Keep a record of your Terraform module version, external ID rotation schedule, and any customizations.
Related resources
- SIEM integration technical overview
- Configuring SIEM integration in Dashboard
- Terraform AWS Provider documentation
- AWS Kinesis Data Streams pricing
Need help?
If you run into any issues or have questions, please reach out to our Support Engineering team by opening a ticket via the Dashboard.
Last updated by Meter Support Engineering on 09/29/2025