diff --git a/examples/serverless/main.tf b/examples/serverless/main.tf new file mode 100644 index 0000000..d7e94bc --- /dev/null +++ b/examples/serverless/main.tf @@ -0,0 +1,87 @@ +provider "aws" { + region = local.region +} + +data "aws_availability_zones" "available" {} + +locals { + name = "ex-${basename(path.cwd)}" + region = "us-east-1" + + vpc_cidr = "10.0.0.0/16" + azs = slice(data.aws_availability_zones.available.names, 0, 3) + + tags = { + Example = local.name + GithubRepo = "terraform-aws-msk-kafka-cluster" + GithubOrg = "terraform-aws-modules" + } +} + +module "msk_serverless_cluster" { + source = "../../modules/serverless" + + name = local.name + + security_group_ids = [module.security_group.security_group_id] + subnet_ids = module.vpc.private_subnets + + create_cluster_policy = true + cluster_policy_statements = { + firehose = { + sid = "firehose" + principals = [ + { + type = "Service" + identifiers = ["firehose.amazonaws.com"] + } + ] + actions = [ + "kafka:CreateVpcConnection", + "kafka:GetBootstrapBrokers", + "kafka:DescribeClusterV2" + ] + } + } + + tags = local.tags +} + +################################################################################ +# Supporting Resources +################################################################################ + +module "vpc" { + source = "terraform-aws-modules/vpc/aws" + version = "~> 5.0" + + name = local.name + cidr = local.vpc_cidr + + azs = local.azs + public_subnets = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 8, k)] + private_subnets = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 8, k + 3)] + database_subnets = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 8, k + 6)] + + create_database_subnet_group = true + enable_nat_gateway = true + single_nat_gateway = true + + tags = local.tags +} + +module "security_group" { + source = "terraform-aws-modules/security-group/aws" + version = "~> 5.0" + + name = local.name + description = "Security group for ${local.name}" + vpc_id = module.vpc.vpc_id + + ingress_cidr_blocks = module.vpc.private_subnets_cidr_blocks + ingress_rules = [ + "kafka-broker-sasl-iam-tcp" + ] + + tags = local.tags +} diff --git a/examples/serverless/outputs.tf b/examples/serverless/outputs.tf new file mode 100644 index 0000000..e11dcc8 --- /dev/null +++ b/examples/serverless/outputs.tf @@ -0,0 +1,9 @@ +output "serverless_arn" { + description = "The ARN of the serverless cluster" + value = module.msk_serverless_cluster.serverless_arn +} + +output "serverless_cluster_uuid" { + description = "UUID of the serverless cluster, for use in IAM policies" + value = module.msk_serverless_cluster.serverless_cluster_uuid +} diff --git a/examples/serverless/variables.tf b/examples/serverless/variables.tf new file mode 100644 index 0000000..e69de29 diff --git a/examples/serverless/versions.tf b/examples/serverless/versions.tf new file mode 100644 index 0000000..5cdb3d4 --- /dev/null +++ b/examples/serverless/versions.tf @@ -0,0 +1,10 @@ +terraform { + required_version = ">= 1.0" + + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 5.21" + } + } +} diff --git a/modules/serverless/main.tf b/modules/serverless/main.tf new file mode 100644 index 0000000..b009383 --- /dev/null +++ b/modules/serverless/main.tf @@ -0,0 +1,82 @@ +################################################################################ +# Serverless Cluster +################################################################################ +resource "aws_msk_serverless_cluster" "this" { + count = var.create ? 1 : 0 + + client_authentication { + sasl { + iam { + enabled = true + } + } + } + + cluster_name = var.name + + vpc_config { + security_group_ids = var.security_group_ids + subnet_ids = var.subnet_ids + } + + tags = var.tags +} + +################################################################################ +# Cluster Policy +################################################################################ + +resource "aws_msk_cluster_policy" "this" { + count = var.create && var.create_cluster_policy ? 1 : 0 + + cluster_arn = aws_msk_serverless_cluster.this[0].arn + policy = data.aws_iam_policy_document.this[0].json +} + +data "aws_iam_policy_document" "this" { + count = var.create && var.create_cluster_policy ? 1 : 0 + + source_policy_documents = var.cluster_source_policy_documents + override_policy_documents = var.cluster_override_policy_documents + + dynamic "statement" { + for_each = var.cluster_policy_statements + + content { + sid = try(statement.value.sid, null) + actions = try(statement.value.actions, null) + not_actions = try(statement.value.not_actions, null) + effect = try(statement.value.effect, null) + resources = try(statement.value.resources, [aws_msk_serverless_cluster.this[0].arn]) + not_resources = try(statement.value.not_resources, null) + + dynamic "principals" { + for_each = try(statement.value.principals, []) + + content { + type = principals.value.type + identifiers = principals.value.identifiers + } + } + + dynamic "not_principals" { + for_each = try(statement.value.not_principals, []) + + content { + type = not_principals.value.type + identifiers = not_principals.value.identifiers + } + } + + dynamic "condition" { + for_each = try(statement.value.conditions, []) + + content { + test = condition.value.test + values = condition.value.values + variable = condition.value.variable + } + } + } + } +} diff --git a/modules/serverless/outputs.tf b/modules/serverless/outputs.tf new file mode 100644 index 0000000..5148bff --- /dev/null +++ b/modules/serverless/outputs.tf @@ -0,0 +1,13 @@ +################################################################################ +# Serverless Cluster +################################################################################ + +output "serverless_arn" { + description = "The ARN of the serverless cluster" + value = try(aws_msk_serverless_cluster.this[0].arn, null) +} + +output "serverless_cluster_uuid" { + description = "UUID of the serverless cluster, for use in IAM policies" + value = try(aws_msk_serverless_cluster.this[0].cluster_uuid, null) +} diff --git a/modules/serverless/variables.tf b/modules/serverless/variables.tf new file mode 100644 index 0000000..9f61cd3 --- /dev/null +++ b/modules/serverless/variables.tf @@ -0,0 +1,60 @@ +################################################################################ +# Serverless Cluster +################################################################################ +variable "create" { + description = "Determines whether cluster resources will be created" + type = bool + default = true +} + +variable "name" { + description = "Name of the MSK serverless cluster" + type = string + default = null +} + +variable "security_group_ids" { + description = "Specifies up to five security groups that control inbound and outbound traffic for the serverless cluster" + type = list(string) + default = null +} + +variable "subnet_ids" { + description = "A list of subnets in at least two different Availability Zones that host your client applications" + type = list(string) + default = null +} + +variable "tags" { + description = "A map of tags to assign to the resources created" + type = map(string) + default = {} +} + +################################################################################ +# Cluster Policy +################################################################################ + +variable "create_cluster_policy" { + description = "Determines whether to create an MSK cluster policy" + type = bool + default = false +} + +variable "cluster_source_policy_documents" { + description = "Source policy documents for cluster policy" + type = list(string) + default = null +} + +variable "cluster_override_policy_documents" { + description = "Override policy documents for cluster policy" + type = list(string) + default = null +} + +variable "cluster_policy_statements" { + description = "Map of policy statements for cluster policy" + type = any + default = null +} diff --git a/modules/serverless/versions.tf b/modules/serverless/versions.tf new file mode 100644 index 0000000..5cdb3d4 --- /dev/null +++ b/modules/serverless/versions.tf @@ -0,0 +1,10 @@ +terraform { + required_version = ">= 1.0" + + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 5.21" + } + } +}