diff --git a/infrastructure/modules/obs-datasource/README.md b/infrastructure/modules/obs-datasource/README.md new file mode 100644 index 0000000..2ce7fee --- /dev/null +++ b/infrastructure/modules/obs-datasource/README.md @@ -0,0 +1,36 @@ + + + + +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.9.0 | +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [aws\_account\_id](#input\_aws\_account\_id) | The AWS Account ID (numeric) | `string` | n/a | yes | +| [component](#input\_component) | The name of the terraformscaffold component calling this module | `string` | n/a | yes | +| [default\_tags](#input\_default\_tags) | Default tag map for application to all taggable resources in the module | `map(string)` | `{}` | no | +| [environment](#input\_environment) | The name of the terraformscaffold environment the module is called for | `string` | n/a | yes | +| [log\_group\_configuration](#input\_log\_group\_configuration) | Configuration for filtering log groups in the link configuration. |
object({
filter = string
}) | `null` | no |
+| [metric\_configuration](#input\_metric\_configuration) | Configuration for filtering metrics in the link configuration. | object({
filter = string
}) | `null` | no |
+| [name](#input\_name) | A unique name to distinguish this module invocation from others within the same CSI scope | `string` | n/a | yes |
+| [oam\_sink\_id](#input\_oam\_sink\_id) | The ID of the Cloudwatch OAM sink in the appropriate observability account. | `string` | `""` | no |
+| [observability\_account\_id](#input\_observability\_account\_id) | The Observability Account ID that needs access | `string` | n/a | yes |
+| [project](#input\_project) | The name of the terraformscaffold project calling the module | `string` | n/a | yes |
+| [region](#input\_region) | The AWS Region | `string` | n/a | yes |
+| [resource\_types](#input\_resource\_types) | The resource types to include in the OAM link. | `list(string)` | [| no | +## Modules + +No modules. +## Outputs + +| Name | Description | +|------|-------------| +| [log\_subscription\_role\_arn](#output\_log\_subscription\_role\_arn) | The ARN of the log subscription IAM role. | + + + diff --git a/infrastructure/modules/obs-datasource/iam_role_log_subscription_role.tf b/infrastructure/modules/obs-datasource/iam_role_log_subscription_role.tf new file mode 100644 index 0000000..698fa19 --- /dev/null +++ b/infrastructure/modules/obs-datasource/iam_role_log_subscription_role.tf @@ -0,0 +1,42 @@ +resource "aws_iam_role" "log_subscription_role" { + name = "${local.csi}-log-subscription-role" + + assume_role_policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Effect = "Allow" + Principal = { + Service = "logs.${var.region}.amazonaws.com" + } + Action = "sts:AssumeRole" + } + ] + }) +} + +resource "aws_iam_policy" "log_subscription_policy" { + name = "${local.csi}-log-subscription-policy" + description = "Policy for log subscription to send logs to the destination" + + policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Effect = "Allow" + Action = [ + "logs:PutSubscriptionFilter", + "logs:DescribeLogGroups", + "logs:DescribeLogStreams", + "logs:PutLogEvents" + ] + Resource = "arn:aws:logs:${var.region}:${var.observability_account_id}:destination:nhs-notify-main-acct-firehose-logs" + } + ] + }) +} + +resource "aws_iam_role_policy_attachment" "log_subscription_policy_attachment" { + role = aws_iam_role.log_subscription_role.name + policy_arn = aws_iam_policy.log_subscription_policy.arn +} diff --git a/infrastructure/modules/obs-datasource/locals.tf b/infrastructure/modules/obs-datasource/locals.tf new file mode 100644 index 0000000..f3bdfa0 --- /dev/null +++ b/infrastructure/modules/obs-datasource/locals.tf @@ -0,0 +1,23 @@ +locals { + module = "obs-datasource" + + csi = replace( + format( + "%s-%s-%s-%s", + var.project, + var.environment, + var.component, + var.name, + ), + "_", + "", + ) + default_tags = merge( + var.default_tags, + { + Module = local.module + Name = local.csi + }, + ) + +} diff --git a/infrastructure/modules/obs-datasource/oam_link_cross_account_obs.tf b/infrastructure/modules/obs-datasource/oam_link_cross_account_obs.tf new file mode 100644 index 0000000..b0c062f --- /dev/null +++ b/infrastructure/modules/obs-datasource/oam_link_cross_account_obs.tf @@ -0,0 +1,65 @@ +resource "aws_oam_link" "cross_account_obs" { + label_template = "$AccountName" + resource_types = var.resource_types + sink_identifier = "arn:aws:oam:${var.region}:${var.observability_account_id}:sink/${var.oam_sink_id}" + tags = var.default_tags + + link_configuration { + dynamic "log_group_configuration" { + for_each = var.log_group_configuration != null ? [var.log_group_configuration] : [] + content { + filter = log_group_configuration.value.filter + } + } + + dynamic "metric_configuration" { + for_each = var.metric_configuration != null ? [var.metric_configuration] : [] + content { + filter = metric_configuration.value.filter + } + } + } +} + +data "aws_iam_policy" "cloudwatch_read_only" { + name = "CloudWatchReadOnlyAccess" +} + +data "aws_iam_policy" "cloudwatch_automatic_dashboards" { + name = "CloudWatchAutomaticDashboardsAccess" +} + +data "aws_iam_policy" "aws_xray_read_only" { + name = "AWSXrayReadOnlyAccess" +} + +data "aws_iam_policy_document" "cross_account_obs_assume_role_policy" { + statement { + effect = "Allow" + principals { + type = "AWS" + identifiers = [var.observability_account_id] + } + actions = ["sts:AssumeRole"] + } +} + +resource "aws_iam_role" "cross_account_obs_role" { + name = "CloudWatch-CrossAccountSharingRole" + assume_role_policy = data.aws_iam_policy_document.cross_account_obs_assume_role_policy.json +} + +resource "aws_iam_role_policy_attachment" "cloudwatch_read_only_attachment" { + policy_arn = data.aws_iam_policy.cloudwatch_read_only.arn + role = aws_iam_role.cross_account_obs_role.name +} + +resource "aws_iam_role_policy_attachment" "cloudwatch_automatic_dashboards_attachment" { + policy_arn = data.aws_iam_policy.cloudwatch_automatic_dashboards.arn + role = aws_iam_role.cross_account_obs_role.name +} + +resource "aws_iam_role_policy_attachment" "aws_xray_read_only_attachment" { + policy_arn = data.aws_iam_policy.aws_xray_read_only.arn + role = aws_iam_role.cross_account_obs_role.name +} diff --git a/infrastructure/modules/obs-datasource/outputs.tf b/infrastructure/modules/obs-datasource/outputs.tf new file mode 100644 index 0000000..b82647f --- /dev/null +++ b/infrastructure/modules/obs-datasource/outputs.tf @@ -0,0 +1,4 @@ +output "log_subscription_role_arn" { + description = "The ARN of the log subscription IAM role." + value = aws_iam_role.log_subscription_role.arn +} diff --git a/infrastructure/modules/obs-datasource/variables.tf b/infrastructure/modules/obs-datasource/variables.tf new file mode 100644 index 0000000..14f01d5 --- /dev/null +++ b/infrastructure/modules/obs-datasource/variables.tf @@ -0,0 +1,82 @@ +## +# Basic inherited variables for terraformscaffold modules +## + +variable "project" { + type = string + description = "The name of the terraformscaffold project calling the module" +} + +variable "environment" { + type = string + description = "The name of the terraformscaffold environment the module is called for" +} + +variable "component" { + type = string + description = "The name of the terraformscaffold component calling this module" +} + +variable "aws_account_id" { + type = string + description = "The AWS Account ID (numeric)" +} + +## +# Variable specific to the module +## + +# We presume this will always be specified. The default of {} will cause an error if a valid map is not specified. +# If we ever want to define this but allow it to not be specified, then we must provide a default tag keypair will be applied +# as the true default. In any other case default_tags should be removed from the module. +variable "default_tags" { + type = map(string) + description = "Default tag map for application to all taggable resources in the module" + default = {} +} + +variable "region" { + type = string + description = "The AWS Region" +} + +variable "name" { + type = string + description = "A unique name to distinguish this module invocation from others within the same CSI scope" +} + +variable "oam_sink_id" { + description = "The ID of the Cloudwatch OAM sink in the appropriate observability account." + type = string + default = "" +} + +variable "observability_account_id" { + type = string + description = "The Observability Account ID that needs access" +} + +variable "log_group_configuration" { + description = "Configuration for filtering log groups in the link configuration." # https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/oam_link#link_configuration-block + type = object({ + filter = string + }) + default = null +} + +variable "metric_configuration" { + description = "Configuration for filtering metrics in the link configuration." # https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/oam_link#link_configuration-block + type = object({ + filter = string + }) + default = null +} + +variable "resource_types" { + type = list(string) + description = "The resource types to include in the OAM link." + default = [ + "AWS::CloudWatch::Metric", + "AWS::Logs::LogGroup" + ] +} diff --git a/infrastructure/modules/obs-datasource/versions.tf b/infrastructure/modules/obs-datasource/versions.tf new file mode 100644 index 0000000..f8dc86e --- /dev/null +++ b/infrastructure/modules/obs-datasource/versions.tf @@ -0,0 +1,9 @@ + +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + } + } + required_version = ">= 1.9.0" +}
"AWS::CloudWatch::Metric",
"AWS::Logs::LogGroup"
]