From 227f8eba90a4596b47462ed807bdb235bd76a4b5 Mon Sep 17 00:00:00 2001 From: "Matthew C. Morgan" Date: Thu, 12 Feb 2026 16:53:51 -0500 Subject: [PATCH] updates for tags --- .pre-commit.yaml => .pre-commit-config.yaml | 2 +- common/base_settings.tf | 9 + common/base_tags.tf | 16 ++ common/data.tf | 34 +++ common/defaults.tf | 39 ++++ common/locals.tf | 2 + common/main.tf | 4 + common/outputs.tf | 59 +++++ common/resources.tf | 42 ++++ common/variables.parameters.tf | 5 + common/variables.product.tf | 94 ++++++++ common/variables.safeguards.tf | 24 ++ common/variables.tags.tf | 22 ++ common/version.tf | 9 + common/versions.tf | 9 + ec2/base_settings.tf | 10 +- ec2/base_tags.tf | 17 +- ec2/data.tf | 44 +--- ec2/defaults.tf | 1 + ec2/locals.tf | 1 + ec2/{version.tf => module_name.tf} | 2 +- ec2/outputs.tf | 60 +---- ec2/resources.tf | 1 + ec2/variables.parameters.tf | 6 +- ec2/variables.product.tf | 89 +------- ec2/variables.safeguards.tf | 25 +-- ec2/variables.tags.tf | 1 + ec2/versions.tf | 10 +- examples/ec2/README.md | 71 ++++++ examples/ec2/linux/README.md | 114 ++++++++++ examples/ec2/linux/census-rhel-instance.tf | 60 +++++ examples/ec2/linux/main.tf | 13 ++ examples/ec2/linux/outputs.tf | 36 +++ examples/ec2/linux/providers.tf | 3 + examples/ec2/linux/terraform.tfvars.example | 120 ++++++++++ examples/ec2/linux/variables.tf | 232 ++++++++++++++++++++ examples/ecs/simple-ecs.tf | 75 +++++++ examples/rds/simple-rds.tf | 77 +++++++ examples/s3/simple-s3.tf | 64 ++++++ 39 files changed, 1247 insertions(+), 255 deletions(-) rename .pre-commit.yaml => .pre-commit-config.yaml (86%) create mode 100644 common/base_settings.tf create mode 100644 common/base_tags.tf create mode 100644 common/data.tf create mode 100644 common/defaults.tf create mode 100644 common/locals.tf create mode 100644 common/main.tf create mode 100644 common/outputs.tf create mode 100644 common/resources.tf create mode 100644 common/variables.parameters.tf create mode 100644 common/variables.product.tf create mode 100644 common/variables.safeguards.tf create mode 100644 common/variables.tags.tf create mode 100644 common/version.tf create mode 100644 common/versions.tf mode change 100644 => 120000 ec2/base_settings.tf mode change 100644 => 120000 ec2/base_tags.tf mode change 100644 => 120000 ec2/data.tf mode change 100644 => 120000 ec2/defaults.tf mode change 100644 => 120000 ec2/locals.tf rename ec2/{version.tf => module_name.tf} (96%) mode change 100644 => 120000 ec2/outputs.tf mode change 100644 => 120000 ec2/resources.tf mode change 100644 => 120000 ec2/variables.parameters.tf mode change 100644 => 120000 ec2/variables.product.tf mode change 100644 => 120000 ec2/variables.safeguards.tf mode change 100644 => 120000 ec2/variables.tags.tf mode change 100644 => 120000 ec2/versions.tf create mode 100644 examples/ec2/README.md create mode 100644 examples/ec2/linux/README.md create mode 100644 examples/ec2/linux/census-rhel-instance.tf create mode 100644 examples/ec2/linux/main.tf create mode 100644 examples/ec2/linux/outputs.tf create mode 100644 examples/ec2/linux/providers.tf create mode 100644 examples/ec2/linux/terraform.tfvars.example create mode 100644 examples/ec2/linux/variables.tf create mode 100644 examples/ecs/simple-ecs.tf create mode 100644 examples/rds/simple-rds.tf create mode 100644 examples/s3/simple-s3.tf diff --git a/.pre-commit.yaml b/.pre-commit-config.yaml similarity index 86% rename from .pre-commit.yaml rename to .pre-commit-config.yaml index d258b1e..65fd3f3 100644 --- a/.pre-commit.yaml +++ b/.pre-commit-config.yaml @@ -5,4 +5,4 @@ repos: - id: terraform_fmt - id: terraform_docs - id: terraform_validate - - id: terraform_tflint \ No newline at end of file + - id: terraform_tflint diff --git a/common/base_settings.tf b/common/base_settings.tf new file mode 100644 index 0000000..8ed5cee --- /dev/null +++ b/common/base_settings.tf @@ -0,0 +1,9 @@ +data "aws_caller_identity" "current" {} +data "aws_region" "current" {} +data "aws_partition" "current" {} + +locals { + account_id = data.aws_caller_identity.current.account_id + region = data.aws_region.current.id + partition = data.aws_partition.current.partition +} diff --git a/common/base_tags.tf b/common/base_tags.tf new file mode 100644 index 0000000..1c3a269 --- /dev/null +++ b/common/base_tags.tf @@ -0,0 +1,16 @@ +locals { + standard_tags = { + ManagedBy = "Terraform" + Module = local.module_name + } + + enforced_tags = merge( + local.standard_tags, + var.enforced_tags + ) + + tags = merge( + local.enforced_tags, + var.tags + ) +} diff --git a/common/data.tf b/common/data.tf new file mode 100644 index 0000000..8a542a9 --- /dev/null +++ b/common/data.tf @@ -0,0 +1,34 @@ +# List portfolios to find by name pattern +data "aws_servicecatalog_portfolios" "all" { + count = var.portfolio_id == null ? 1 : 0 + accept_language = var.accept_language +} + +# Get portfolio details if we provided the ID +data "aws_servicecatalog_portfolio" "by_id" { + count = var.portfolio_id != null ? 1 : 0 + id = var.portfolio_id + accept_language = var.accept_language +} + +# Get product details by ID (requires product_id) +data "aws_servicecatalog_product" "by_id" { + count = var.product_id != null ? 1 : 0 + id = var.product_id + accept_language = var.accept_language +} + +# Get the latest provisioning artifact (product version) +data "aws_servicecatalog_provisioning_artifacts" "this" { + accept_language = var.accept_language + product_id = var.product_id != null ? var.product_id : var.product_id +} + +# Get CloudFormation stack outputs if provisioned product exists +data "aws_cloudformation_stack" "this" { + count = var.retrieve_stack_outputs ? 1 : 0 + + name = format("SC-%s-%s", aws_servicecatalog_provisioned_product.this.id, aws_servicecatalog_provisioned_product.this.name) + + depends_on = [aws_servicecatalog_provisioned_product.this] +} diff --git a/common/defaults.tf b/common/defaults.tf new file mode 100644 index 0000000..505eab5 --- /dev/null +++ b/common/defaults.tf @@ -0,0 +1,39 @@ +locals { + # Get portfolio ID from lookup or variable + portfolio_id = var.portfolio_id != null ? var.portfolio_id : try( + [for p in data.aws_servicecatalog_portfolios.all[0].details : + p.id if can(regex(var.portfolio_name_pattern, p.name)) + ][0], + null + ) + + # Get product ID from variable or portfolio lookup + product_id = var.product_id + + # Get the latest provisioning artifact ID + latest_artifact_id = try( + [for artifact in data.aws_servicecatalog_provisioning_artifacts.this.provisioning_artifact_details : + artifact.id if artifact.active + ][0], + null + ) + + # Use provided path_id or default to latest + provisioning_artifact_id = var.path_id != null ? var.path_id : local.latest_artifact_id + + # Merge default parameters with user-provided parameters + default_parameters = {} + + parameters = merge( + local.default_parameters, + var.parameters + ) + + # Convert parameters map to the format expected by aws_servicecatalog_provisioned_product + provisioning_parameters = [ + for key, value in local.parameters : { + key = key + value = tostring(value) + } + ] +} diff --git a/common/locals.tf b/common/locals.tf new file mode 100644 index 0000000..e41bf43 --- /dev/null +++ b/common/locals.tf @@ -0,0 +1,2 @@ +# Intentionally empty file for consistency with aws-s3 pattern +# Module-specific locals should be defined in each submodule diff --git a/common/main.tf b/common/main.tf new file mode 100644 index 0000000..60ccef9 --- /dev/null +++ b/common/main.tf @@ -0,0 +1,4 @@ +# Product Submodule +# +# Provisions a Service Catalog product +# using a pre-configured portfolio and product diff --git a/common/outputs.tf b/common/outputs.tf new file mode 100644 index 0000000..8db182f --- /dev/null +++ b/common/outputs.tf @@ -0,0 +1,59 @@ +output "provisioned_product_id" { + description = "The ID of the provisioned product" + value = aws_servicecatalog_provisioned_product.this.id +} + +output "provisioned_product_name" { + description = "The name of the provisioned product" + value = aws_servicecatalog_provisioned_product.this.name +} + +output "provisioned_product_arn" { + description = "The ARN of the provisioned product" + value = aws_servicecatalog_provisioned_product.this.arn +} + +output "provisioned_product_type" { + description = "The type of the provisioned product" + value = aws_servicecatalog_provisioned_product.this.type +} + +output "provisioned_product_status" { + description = "The status of the provisioned product" + value = aws_servicecatalog_provisioned_product.this.status +} + +output "provisioned_product_status_message" { + description = "The status message for the provisioned product" + value = aws_servicecatalog_provisioned_product.this.status_message +} + +output "cloudformation_stack_arn" { + description = "The ARN of the CloudFormation stack" + value = aws_servicecatalog_provisioned_product.this.cloudformation_stack_arn +} + +output "launch_role_arn" { + description = "The ARN of the launch role" + value = aws_servicecatalog_provisioned_product.this.launch_role_arn +} + +output "stack_outputs" { + description = "CloudFormation stack outputs" + value = var.retrieve_stack_outputs ? try(data.aws_cloudformation_stack.this[0].outputs, {}) : {} +} + +output "portfolio_id" { + description = "The ID of the portfolio used" + value = local.portfolio_id +} + +output "product_id" { + description = "The ID of the product used" + value = local.product_id +} + +output "provisioning_artifact_id" { + description = "The ID of the provisioning artifact used" + value = local.provisioning_artifact_id +} diff --git a/common/resources.tf b/common/resources.tf new file mode 100644 index 0000000..941e4d7 --- /dev/null +++ b/common/resources.tf @@ -0,0 +1,42 @@ +resource "aws_servicecatalog_provisioned_product" "this" { + name = var.provisioned_product_name + product_id = local.product_id + provisioning_artifact_id = local.provisioning_artifact_id + path_id = var.path_id + accept_language = var.accept_language + ignore_errors = var.ignore_errors + notification_arns = var.notification_arns + retain_physical_resources = var.retain_physical_resources + + dynamic "provisioning_parameters" { + for_each = local.provisioning_parameters + content { + key = provisioning_parameters.value.key + value = provisioning_parameters.value.value + } + } + + dynamic "stack_set_provisioning_preferences" { + for_each = var.stack_set_provisioning_preferences != null ? [var.stack_set_provisioning_preferences] : [] + content { + accounts = try(stack_set_provisioning_preferences.value.accounts, null) + failure_tolerance_count = try(stack_set_provisioning_preferences.value.failure_tolerance_count, null) + failure_tolerance_percentage = try(stack_set_provisioning_preferences.value.failure_tolerance_percentage, null) + max_concurrency_count = try(stack_set_provisioning_preferences.value.max_concurrency_count, null) + max_concurrency_percentage = try(stack_set_provisioning_preferences.value.max_concurrency_percentage, null) + regions = try(stack_set_provisioning_preferences.value.regions, null) + } + } + + tags = local.tags + + timeouts { + create = var.timeout_create + update = var.timeout_update + delete = var.timeout_delete + } + + depends_on = [ + data.aws_servicecatalog_provisioning_artifacts.this + ] +} diff --git a/common/variables.parameters.tf b/common/variables.parameters.tf new file mode 100644 index 0000000..92e9357 --- /dev/null +++ b/common/variables.parameters.tf @@ -0,0 +1,5 @@ +variable "parameters" { + description = "Parameters to pass to the Service Catalog product. Map of parameter names to values" + type = map(string) + default = {} +} diff --git a/common/variables.product.tf b/common/variables.product.tf new file mode 100644 index 0000000..af57f28 --- /dev/null +++ b/common/variables.product.tf @@ -0,0 +1,94 @@ +variable "provisioned_product_name" { + description = "Name of the provisioned product" + type = string + + validation { + condition = length(var.provisioned_product_name) > 0 && length(var.provisioned_product_name) <= 128 + error_message = "provisioned_product_name must be between 1 and 128 characters" + } +} + +variable "portfolio_id" { + description = "Portfolio ID. If not provided, will lookup by portfolio_name_pattern" + type = string + default = null +} + +variable "portfolio_name_pattern" { + description = "Pattern to search for portfolio by name. Used when portfolio_id is not provided" + type = string + default = "edl-portfolio" +} + +variable "product_id" { + description = "Product ID. If not provided, will lookup by product_name_pattern" + type = string + default = null +} + +variable "product_name_pattern" { + description = "Pattern to search for product by name" + type = string + default = "linux-product" +} + +variable "path_id" { + description = "Path identifier of the product. If not provided, will use the latest active artifact" + type = string + default = null +} + +variable "ignore_errors" { + description = "Only applies to deleting. If true, errors from the underlying service are ignored" + type = bool + default = false +} + +variable "notification_arns" { + description = "SNS topic ARNs to notify when the provisioned product changes" + type = list(string) + default = [] +} + +variable "retain_physical_resources" { + description = "Whether to retain the physical resources when the provisioned product is terminated" + type = bool + default = false +} + +variable "stack_set_provisioning_preferences" { + description = "Configuration for StackSet provisioning" + type = object({ + accounts = optional(list(string)) + failure_tolerance_count = optional(number) + failure_tolerance_percentage = optional(number) + max_concurrency_count = optional(number) + max_concurrency_percentage = optional(number) + regions = optional(list(string)) + }) + default = null +} + +variable "retrieve_stack_outputs" { + description = "Whether to retrieve CloudFormation stack outputs" + type = bool + default = true +} + +variable "timeout_create" { + description = "Timeout for provisioned product creation" + type = string + default = "60m" +} + +variable "timeout_update" { + description = "Timeout for provisioned product updates" + type = string + default = "60m" +} + +variable "timeout_delete" { + description = "Timeout for provisioned product deletion" + type = string + default = "60m" +} \ No newline at end of file diff --git a/common/variables.safeguards.tf b/common/variables.safeguards.tf new file mode 100644 index 0000000..91c21b1 --- /dev/null +++ b/common/variables.safeguards.tf @@ -0,0 +1,24 @@ +# This file contains safeguard variables to prevent accidental destruction +# Pattern follows aws-s3 module conventions + +variable "enable_deletion_protection" { + description = "Enable deletion protection to prevent accidental termination" + type = bool + default = false +} + +locals { + deletion_protection_error = "Deletion protection is enabled. Set enable_deletion_protection = false to allow termination." +} + +resource "null_resource" "deletion_protection" { + count = var.enable_deletion_protection ? 1 : 0 + + lifecycle { + prevent_destroy = true + } + + triggers = { + provisioned_product_id = aws_servicecatalog_provisioned_product.this.id + } +} diff --git a/common/variables.tags.tf b/common/variables.tags.tf new file mode 100644 index 0000000..18c6001 --- /dev/null +++ b/common/variables.tags.tf @@ -0,0 +1,22 @@ +variable "tags" { + description = "Additional tags to apply to resources" + type = map(string) + default = {} +} + +variable "enforced_tags" { + description = "Tags enforced on all resources" + type = map(string) + default = {} +} + +variable "accept_language" { + description = "Language code for Service Catalog API calls" + type = string + default = "en" + + validation { + condition = contains(["en", "jp", "zh"], var.accept_language) + error_message = "accept_language must be one of: en, jp, zh" + } +} diff --git a/common/version.tf b/common/version.tf new file mode 100644 index 0000000..dd0ebb9 --- /dev/null +++ b/common/version.tf @@ -0,0 +1,9 @@ +terraform { + required_version = ">= 1.0" + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 5.0" + } + } +} diff --git a/common/versions.tf b/common/versions.tf new file mode 100644 index 0000000..dd0ebb9 --- /dev/null +++ b/common/versions.tf @@ -0,0 +1,9 @@ +terraform { + required_version = ">= 1.0" + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 5.0" + } + } +} diff --git a/ec2/base_settings.tf b/ec2/base_settings.tf deleted file mode 100644 index 646ba15..0000000 --- a/ec2/base_settings.tf +++ /dev/null @@ -1,9 +0,0 @@ -data "aws_caller_identity" "current" {} -data "aws_region" "current" {} -data "aws_partition" "current" {} - -locals { - account_id = data.aws_caller_identity.current.account_id - region = data.aws_region.current.name - partition = data.aws_partition.current.partition -} \ No newline at end of file diff --git a/ec2/base_settings.tf b/ec2/base_settings.tf new file mode 120000 index 0000000..396784e --- /dev/null +++ b/ec2/base_settings.tf @@ -0,0 +1 @@ +../common/base_settings.tf \ No newline at end of file diff --git a/ec2/base_tags.tf b/ec2/base_tags.tf deleted file mode 100644 index 40588ba..0000000 --- a/ec2/base_tags.tf +++ /dev/null @@ -1,16 +0,0 @@ -locals { - standard_tags = { - ManagedBy = "Terraform" - Module = local.module_name - } - - enforced_tags = merge( - local.standard_tags, - var.enforced_tags - ) - - tags = merge( - local.enforced_tags, - var.tags - ) -} \ No newline at end of file diff --git a/ec2/base_tags.tf b/ec2/base_tags.tf new file mode 120000 index 0000000..91c15aa --- /dev/null +++ b/ec2/base_tags.tf @@ -0,0 +1 @@ +../common/base_tags.tf \ No newline at end of file diff --git a/ec2/data.tf b/ec2/data.tf deleted file mode 100644 index 3e42b73..0000000 --- a/ec2/data.tf +++ /dev/null @@ -1,43 +0,0 @@ -# Lookup portfolio by name pattern -data "aws_servicecatalog_portfolio" "this" { - count = var.portfolio_id == null ? 1 : 0 - - id = var.portfolio_id != null ? var.portfolio_id : null - accept_language = var.accept_language - - filter { - key = "Name" - value = var.portfolio_name_pattern - } -} - -# Search for product in portfolio -data "aws_servicecatalog_product" "this" { - id = var.product_id != null ? var.product_id : try(data.aws_servicecatalog_portfolio.this[0].id, null) - - accept_language = var.accept_language - - filter { - key = "FullTextSearch" - value = var.product_name_pattern - } -} - -# Get the latest provisioning artifact (product version) -data "aws_servicecatalog_provisioning_artifacts" "this" { - accept_language = var.accept_language - product_id = data.aws_servicecatalog_product.this.id -} - -# Get CloudFormation stack outputs if provisioned product exists -data "aws_cloudformation_stack" "this" { - count = var.retrieve_stack_outputs ? 1 : 0 - - name = format("SC-%s-%s", aws_servicecatalog_provisioned_product.this.id, aws_servicecatalog_provisioned_product.this.name) - - depends_on = [aws_servicecatalog_provisioned_product.this] -} - -data "aws_iam_policy_document" "empty" { - # Empty policy document for conditional logic -} \ No newline at end of file diff --git a/ec2/data.tf b/ec2/data.tf new file mode 120000 index 0000000..995624d --- /dev/null +++ b/ec2/data.tf @@ -0,0 +1 @@ +../common/data.tf \ No newline at end of file diff --git a/ec2/defaults.tf b/ec2/defaults.tf deleted file mode 100644 index e69de29..0000000 diff --git a/ec2/defaults.tf b/ec2/defaults.tf new file mode 120000 index 0000000..a5556ac --- /dev/null +++ b/ec2/defaults.tf @@ -0,0 +1 @@ +../common/defaults.tf \ No newline at end of file diff --git a/ec2/locals.tf b/ec2/locals.tf deleted file mode 100644 index e69de29..0000000 diff --git a/ec2/locals.tf b/ec2/locals.tf new file mode 120000 index 0000000..cad3d5e --- /dev/null +++ b/ec2/locals.tf @@ -0,0 +1 @@ +../common/locals.tf \ No newline at end of file diff --git a/ec2/version.tf b/ec2/module_name.tf similarity index 96% rename from ec2/version.tf rename to ec2/module_name.tf index ae4e884..87a6a5c 100644 --- a/ec2/version.tf +++ b/ec2/module_name.tf @@ -1,3 +1,3 @@ locals { module_name = "aws-servicecatalog/ec2" -} \ No newline at end of file +} diff --git a/ec2/outputs.tf b/ec2/outputs.tf deleted file mode 100644 index 74365e6..0000000 --- a/ec2/outputs.tf +++ /dev/null @@ -1,59 +0,0 @@ -output "provisioned_product_id" { - description = "The ID of the provisioned product" - value = aws_servicecatalog_provisioned_product.this.id -} - -output "provisioned_product_name" { - description = "The name of the provisioned product" - value = aws_servicecatalog_provisioned_product.this.name -} - -output "provisioned_product_arn" { - description = "The ARN of the provisioned product" - value = aws_servicecatalog_provisioned_product.this.arn -} - -output "provisioned_product_type" { - description = "The type of the provisioned product" - value = aws_servicecatalog_provisioned_product.this.type -} - -output "provisioned_product_status" { - description = "The status of the provisioned product" - value = aws_servicecatalog_provisioned_product.this.status -} - -output "provisioned_product_status_message" { - description = "The status message for the provisioned product" - value = aws_servicecatalog_provisioned_product.this.status_message -} - -output "cloudformation_stack_arn" { - description = "The ARN of the CloudFormation stack" - value = aws_servicecatalog_provisioned_product.this.cloudformation_stack_arn -} - -output "launch_role_arn" { - description = "The ARN of the launch role" - value = aws_servicecatalog_provisioned_product.this.launch_role_arn -} - -output "stack_outputs" { - description = "CloudFormation stack outputs" - value = var.retrieve_stack_outputs ? try(data.aws_cloudformation_stack.this[0].outputs, {}) : {} -} - -output "portfolio_id" { - description = "The ID of the portfolio used" - value = local.portfolio_id -} - -output "product_id" { - description = "The ID of the product used" - value = data.aws_servicecatalog_product.this.id -} - -output "provisioning_artifact_id" { - description = "The ID of the provisioning artifact used" - value = local.provisioning_artifact_id -} \ No newline at end of file diff --git a/ec2/outputs.tf b/ec2/outputs.tf new file mode 120000 index 0000000..93b0065 --- /dev/null +++ b/ec2/outputs.tf @@ -0,0 +1 @@ +../common/outputs.tf \ No newline at end of file diff --git a/ec2/resources.tf b/ec2/resources.tf deleted file mode 100644 index e69de29..0000000 diff --git a/ec2/resources.tf b/ec2/resources.tf new file mode 120000 index 0000000..6dd8c84 --- /dev/null +++ b/ec2/resources.tf @@ -0,0 +1 @@ +../common/resources.tf \ No newline at end of file diff --git a/ec2/variables.parameters.tf b/ec2/variables.parameters.tf deleted file mode 100644 index 5bcefc5..0000000 --- a/ec2/variables.parameters.tf +++ /dev/null @@ -1,5 +0,0 @@ -variable "parameters" { - description = "Parameters to pass to the Service Catalog product. Map of parameter names to values" - type = map(string) - default = {} -} \ No newline at end of file diff --git a/ec2/variables.parameters.tf b/ec2/variables.parameters.tf new file mode 120000 index 0000000..95d9713 --- /dev/null +++ b/ec2/variables.parameters.tf @@ -0,0 +1 @@ +../common/variables.parameters.tf \ No newline at end of file diff --git a/ec2/variables.product.tf b/ec2/variables.product.tf deleted file mode 100644 index 92308ed..0000000 --- a/ec2/variables.product.tf +++ /dev/null @@ -1,88 +0,0 @@ -variable "provisioned_product_name" { - description = "Name of the provisioned product" - type = string - - validation { - condition = length(var.provisioned_product_name) > 0 && length(var.provisioned_product_name) <= 128 - error_message = "provisioned_product_name must be between 1 and 128 characters" - } -} - -variable "portfolio_id" { - description = "Portfolio ID. If not provided, will lookup by portfolio_name_pattern" - type = string - default = null -} - -variable "portfolio_name_pattern" { - description = "Pattern to search for portfolio by name. Used when portfolio_id is not provided" - type = string - default = "edl-portfolio" -} - -variable "product_name_pattern" { - description = "Pattern to search for product by name" - type = string - default = "linux-product" -} - -variable "path_id" { - description = "Path identifier of the product. If not provided, will use the latest active artifact" - type = string - default = null -} - -variable "ignore_errors" { - description = "Only applies to deleting. If true, errors from the underlying service are ignored" - type = bool - default = false -} - -variable "notification_arns" { - description = "SNS topic ARNs to notify when the provisioned product changes" - type = list(string) - default = [] -} - -variable "retain_physical_resources" { - description = "Whether to retain the physical resources when the provisioned product is terminated" - type = bool - default = false -} - -variable "stack_set_provisioning_preferences" { - description = "Configuration for StackSet provisioning" - type = object({ - accounts = optional(list(string)) - failure_tolerance_count = optional(number) - failure_tolerance_percentage = optional(number) - max_concurrency_count = optional(number) - max_concurrency_percentage = optional(number) - regions = optional(list(string)) - }) - default = null -} - -variable "retrieve_stack_outputs" { - description = "Whether to retrieve CloudFormation stack outputs" - type = bool - default = true -} - -variable "timeout_create" { - description = "Timeout for provisioned product creation" - type = string - default = "60m" -} - -variable "timeout_update" { - description = "Timeout for provisioned product updates" - type = string - default = "60m" -} - -variable "timeout_delete" { - description = "Timeout for provisioned product deletion" - type = string - default = "60m" -} \ No newline at end of file diff --git a/ec2/variables.product.tf b/ec2/variables.product.tf new file mode 120000 index 0000000..33b985c --- /dev/null +++ b/ec2/variables.product.tf @@ -0,0 +1 @@ +../common/variables.product.tf \ No newline at end of file diff --git a/ec2/variables.safeguards.tf b/ec2/variables.safeguards.tf deleted file mode 100644 index 80f8e94..0000000 --- a/ec2/variables.safeguards.tf +++ /dev/null @@ -1,24 +0,0 @@ -# This file contains safeguard variables to prevent accidental destruction -# Pattern follows aws-s3 module conventions - -variable "enable_deletion_protection" { - description = "Enable deletion protection to prevent accidental termination" - type = bool - default = false -} - -locals { - deletion_protection_error = "Deletion protection is enabled. Set enable_deletion_protection = false to allow termination." -} - -resource "null_resource" "deletion_protection" { - count = var.enable_deletion_protection ? 1 : 0 - - lifecycle { - prevent_destroy = true - } - - triggers = { - provisioned_product_id = aws_servicecatalog_provisioned_product.this.id - } -} \ No newline at end of file diff --git a/ec2/variables.safeguards.tf b/ec2/variables.safeguards.tf new file mode 120000 index 0000000..4c1b2bb --- /dev/null +++ b/ec2/variables.safeguards.tf @@ -0,0 +1 @@ +../common/variables.safeguards.tf \ No newline at end of file diff --git a/ec2/variables.tags.tf b/ec2/variables.tags.tf deleted file mode 100644 index e69de29..0000000 diff --git a/ec2/variables.tags.tf b/ec2/variables.tags.tf new file mode 120000 index 0000000..5b14f30 --- /dev/null +++ b/ec2/variables.tags.tf @@ -0,0 +1 @@ +../common/variables.tags.tf \ No newline at end of file diff --git a/ec2/versions.tf b/ec2/versions.tf deleted file mode 100644 index 4c65a71..0000000 --- a/ec2/versions.tf +++ /dev/null @@ -1,9 +0,0 @@ -terraform { - required_version = ">= 1.0" - required_providers { - aws = { - source = "hashicorp/aws" - version = ">= 5.0" - } - } -} \ No newline at end of file diff --git a/ec2/versions.tf b/ec2/versions.tf new file mode 120000 index 0000000..41bb22f --- /dev/null +++ b/ec2/versions.tf @@ -0,0 +1 @@ +../common/versions.tf \ No newline at end of file diff --git a/examples/ec2/README.md b/examples/ec2/README.md new file mode 100644 index 0000000..ad2934f --- /dev/null +++ b/examples/ec2/README.md @@ -0,0 +1,71 @@ +# EC2 Instance Example + +This example demonstrates how to provision an EC2 instance from AWS Service Catalog. + +## Usage + +```bash +terraform init +terraform plan +terraform apply +``` + +## Example Configuration + +```hcl +module "ec2_instance" { + source = "../../ec2" + + provisioned_product_name = "my-web-server" + portfolio_name_pattern = "edl-portfolio" + product_name_pattern = "linux-product" + + parameters = { + InstanceType = "t3.medium" + KeyName = "my-key" + SubnetId = "subnet-12345" + VpcId = "vpc-12345" + } + + tags = { + Environment = "development" + Application = "web" + Owner = "platform-team" + } +} + +output "instance_id" { + value = module.ec2_instance.stack_outputs["InstanceId"] +} + +output "instance_private_ip" { + value = module.ec2_instance.stack_outputs["PrivateIp"] +} +``` + +## With Service Actions + +```hcl +# Provision the instance +module "ec2_instance" { + source = "../../ec2" + + provisioned_product_name = "my-web-server" + product_name_pattern = "linux-product" + + parameters = { + InstanceType = "t3.medium" + KeyName = "my-key" + SubnetId = "subnet-12345" + } +} + +# Stop the instance (run this separately when needed) +module "ec2_stop" { + source = "../../ec2-actions" + + provisioned_product_id = module.ec2_instance.provisioned_product_id + service_action_name = "Stop" + execute_action = false # Set to true to execute +} +``` diff --git a/examples/ec2/linux/README.md b/examples/ec2/linux/README.md new file mode 100644 index 0000000..b531263 --- /dev/null +++ b/examples/ec2/linux/README.md @@ -0,0 +1,114 @@ +# === Census RHEL EC2 Instance - Service Catalog Example === + +This example demonstrates how to provision a Census RHEL EC2 instance using the AWS Service Catalog module. + +## CloudFormation Template Reference + +This example implements the parameters from the **2-0-2.yaml** CloudFormation template, which provides a comprehensive Census EC2 instance provisioning workflow including: + +- **EC2 Configuration**: Instance type, hostname, OS version (RHEL8/RHEL9), disk sizing +- **Network Setup**: VPC, availability zone, security groups +- **Storage**: Apps disk plus optional extra volumes for /data mounts +- **Creator/Contact Info**: JBID, email addresses for notifications +- **Compliance Tags**: FISMA ID, Title Data classifications +- **Operational Settings**: Backup requirements, power scheduling, deletion protection + +## Files + +- **main.tf** - Service Catalog module invocation +- **variables.tf** - All input variables with validation rules +- **outputs.tf** - Output definitions for provisioned product details +- **terraform.tfvars.example** - Example values (copy to terraform.tfvars and customize) + +## Quick Start + +1. Copy the example configuration: + ```bash + cp terraform.tfvars.example terraform.tfvars + ``` + +2. Edit `terraform.tfvars` with your specific values: + - Set your VPC ID and Availability Zone + - Provide census project identifier + - Set creator/contact email addresses + - Customize instance type, hostname, OS version as needed + +3. Initialize and apply: + ```bash + terraform init + terraform plan + terraform apply + ``` + +## Key Parameters + +### Required Parameters +- `project_name` - Census project identifier (org_project_env_type-accountid) +- `vpc_id` - VPC where instance will be deployed +- `availability_zone` - AZ for the instance +- `instance_hostname` - Local hostname (3-16 chars) +- `creator_jbid` - Census user ID of creator +- `contact_email` - Provisioning user's email +- `incident_poc_email` - Team distribution list email + +### Optional Parameters +- `instance_type` - EC2 type (default: t3.small) +- `os_name` - OS version (default: RHEL9) +- `volume_apps_size` - Apps disk size (default: 10 GiB) +- `requires_backup` - Backup requirement (default: yes) +- `power_schedule` - Power on/off schedule (default: Always_On) +- `fisma_id` - FISMA asset tracking ID +- `title_data_types` - Data classification +- `extra_volumes` - Additional EBS volumes + +## Validation + +All variables include validation rules that match the CloudFormation template constraints: +- Hostname: 3-16 lowercase alphanumeric with hyphens/underscores +- Volume sizes: 10-1024 GiB +- Email addresses: Valid email format +- JBID: Letters + 3-6 digits + +## Outputs + +After `terraform apply` completes, you'll receive: +- `provisioned_product_id` - Service Catalog product ID +- `provisioned_product_status` - Current status (AVAILABLE, etc.) +- `stack_outputs` - CloudFormation outputs with instance details +- `instance_configuration_summary` - Readable configuration summary + +## Example Usage + +```bash +# Initialize terraform +terraform init + +# Create terraform.tfvars with your values +cp terraform.tfvars.example terraform.tfvars +# ... edit terraform.tfvars with your specific values ... + +# Plan the deployment +terraform plan + +# Apply to provision the instance +terraform apply + +# Check the outputs +terraform output instance_configuration_summary +``` + +## Cleanup + +To terminate the instance and remove the provisioned product: + +```bash +terraform destroy +``` + +Note: If `enable_deletion_protection` is set to true, you must change it to false before destroying. + +## Related Documentation + +- CloudFormation Template: `../../templates/products/ec2/instance-linux/2-0-2.yaml` +- Module Documentation: `../../ec2/README.md` +- Parent Module: `../../ec2/main.tf` diff --git a/examples/ec2/linux/census-rhel-instance.tf b/examples/ec2/linux/census-rhel-instance.tf new file mode 100644 index 0000000..b88a972 --- /dev/null +++ b/examples/ec2/linux/census-rhel-instance.tf @@ -0,0 +1,60 @@ +# === EC2 Service Catalog Product Example === +# This example demonstrates how to provision a Census RHEL EC2 instance +# via AWS Service Catalog using the aws-servicecatalog ec2 module. +# Aligns with the 2-0-2.yaml CloudFormation product template. + +module "ec2_instance" { + source = "../../../ec2" + + provisioned_product_name = var.provisioned_product_name + portfolio_name_pattern = var.portfolio_name_pattern + product_name_pattern = var.product_name_pattern + + # CloudFormation parameters passed to the Service Catalog product template + parameters = { + # === Required Census Project Parameters === + ProjectName = var.project_name + VpcId = var.vpc_id + AZName = var.availability_zone + NameTag = var.instance_hostname + InstanceType = var.instance_type + OSName = var.os_name + Creator = var.creator_jbid + ContactEmail = var.contact_email + IncPocEmail = var.incident_poc_email + + # === Optional EC2 Configuration === + pDescription = var.instance_description + VolumeAppsSize = tostring(var.volume_apps_size) + SecurityGroupNames = join(",", var.security_group_names) + + # === Extra Volumes (optional) === + # Uncomment below and populate var.extra_volumes to add additional volumes: + # Volume1Mount = try(var.extra_volumes[0].mount, "") + # Volume1Size = try(tostring(var.extra_volumes[0].size), "0") + # Volume2Mount = try(var.extra_volumes[1].mount, "") + # Volume2Size = try(tostring(var.extra_volumes[1].size), "0") + # Volume3Mount = try(var.extra_volumes[2].mount, "") + # Volume3Size = try(tostring(var.extra_volumes[2].size), "0") + + # === Compliance and Operational Tags === + FISMAID = var.fisma_id + TitleData = join(",", var.title_data_types) + RequiresBackup = var.requires_backup + PowerSchedule = var.power_schedule + ProjectRole = var.project_role + MapMigrated = var.map_migrated + } + + enable_deletion_protection = var.enable_deletion_protection + + tags = merge( + { + Environment = var.environment + ManagedBy = "Terraform" + ServiceCatalog = "true" + Creator = var.creator_jbid + }, + var.additional_tags + ) +} diff --git a/examples/ec2/linux/main.tf b/examples/ec2/linux/main.tf new file mode 100644 index 0000000..a50c01b --- /dev/null +++ b/examples/ec2/linux/main.tf @@ -0,0 +1,13 @@ +# terraform { +# required_version = ">= 1.0" +# required_providers { +# aws = { +# source = "hashicorp/aws" +# version = ">= 5.0" +# } +# } +# } + +# provider "aws" { +# region = var.region +# } diff --git a/examples/ec2/linux/outputs.tf b/examples/ec2/linux/outputs.tf new file mode 100644 index 0000000..2608418 --- /dev/null +++ b/examples/ec2/linux/outputs.tf @@ -0,0 +1,36 @@ +output "provisioned_product_id" { + description = "The unique ID of the provisioned EC2 product" + value = module.ec2_instance.provisioned_product_id +} + +output "provisioned_product_arn" { + description = "The ARN of the provisioned EC2 product" + value = module.ec2_instance.provisioned_product_arn +} + +output "provisioned_product_status" { + description = "The current status of the provisioned product (AVAILABLE, UNDER_CHANGE, ERROR, PLAN_IN_PROGRESS, CREATE_IN_PROGRESS, DELETE_IN_PROGRESS, UPDATE_IN_PROGRESS)" + value = module.ec2_instance.provisioned_product_status +} + +output "stack_outputs" { + description = "CloudFormation stack outputs from the provisioned product (instance details, IPs, etc.)" + value = module.ec2_instance.stack_outputs + sensitive = true +} + +output "instance_configuration_summary" { + description = "Summary of the EC2 instance configuration" + value = { + instance_type = var.instance_type + os_version = var.os_name + hostname = var.instance_hostname + availability_zone = var.availability_zone + vpc_id = var.vpc_id + volume_apps_size_gb = var.volume_apps_size + security_groups = var.security_group_names + backup_enabled = var.requires_backup == "yes" + power_schedule = var.power_schedule + deletion_protected = var.enable_deletion_protection + } +} diff --git a/examples/ec2/linux/providers.tf b/examples/ec2/linux/providers.tf new file mode 100644 index 0000000..dc58d9a --- /dev/null +++ b/examples/ec2/linux/providers.tf @@ -0,0 +1,3 @@ +provider "aws" { + region = var.region +} diff --git a/examples/ec2/linux/terraform.tfvars.example b/examples/ec2/linux/terraform.tfvars.example new file mode 100644 index 0000000..efa479b --- /dev/null +++ b/examples/ec2/linux/terraform.tfvars.example @@ -0,0 +1,120 @@ +# === Census RHEL EC2 Instance Example === +# This terraform.tfvars.example file shows how to provision a Census RHEL EC2 instance +# via AWS Service Catalog. Copy this file to terraform.tfvars and customize the values. +# +# Based on the 2-0-2.yaml CloudFormation template for Census EC2 provisioning. + +# AWS Region +region = "us-east-1" +environment = "Production" + +# Service Catalog Configuration (typically don't need to change these) +provisioned_product_name = "census-rhel-instance" +portfolio_name_pattern = "csvd-portfolio" +product_name_pattern = "linux-product" + +# === EC2 INSTANCE CONFIGURATION === + +# Instance Type - Options: t3.nano, t3.micro, t3.small (default), t3.medium, c5.large, m5.large, etc. +instance_type = "t3a.small" + +# Operating System - Options: RHEL9 (default), RHEL8 +os_name = "RHEL9" + +# Instance Hostname - Must be 3-16 characters, lowercase alphanumeric with hyphens/underscores +instance_hostname = "app-mcm-01" + +# Instance Description +instance_description = "Census RHEL instance provisioned via Terraform" + +# === NETWORK CONFIGURATION === + +# VPC ID (REQUIRED - must be in the same account as the project) +vpc_id = "vpc-1234567890abcdef0" + +# Availability Zone (REQUIRED) +availability_zone = "us-east-1a" + +# Security Groups (it-linux-base is required and included by default) +# Add additional security groups as needed +security_group_names = ["it-linux-base", "ois-scanner"] + +# === PROJECT CONFIGURATION === + +# Census Project Identifier (REQUIRED) +# Format: organization_project_environment_type-accountid +# Example: csvd_ami_dev_dev-229685449397 +# Allowed values are defined in the Service Catalog product template. +project_name = "csvd_ami_dev_dev-229685449397" + +# FinOps Project Role (Optional) - For cost allocation within the project +project_role = "sc_module_test" + +# === STORAGE CONFIGURATION === + +# Apps Disk Size in GiB (must be 10-1024, default: 10) +volume_apps_size = 10 + +# Additional Volumes (Optional) +# Uncomment and customize to add extra volumes +# extra_volumes = [ +# { mount = "tomcat", size = 50 }, +# { mount = "logs", size = 30 } +# ] +extra_volumes = [] + +# === CREATOR AND CONTACT INFORMATION === + +# Creator JBID (Census User ID) +creator_jbid = "morga471" + +# Provisioning User's Email (REQUIRED) +contact_email = "matthew.c.morgan@census.gov" + +# Team Distribution List Email (REQUIRED) +incident_poc_email = "matthew.c.morgan@census.gov" + +# === COMPLIANCE AND TAGGING === + +# FISMA ID - Asset tracking alias for security compliance +# Common values: ADCOM_CEM, ADDCP_ACSO, OCIO_CSVD, etc. +# Leave empty ("") if not applicable +fisma_id = "OCIO_CSVD" + +# Title Data Types - Data classification for this instance +# Options: no_title, title_2, title_5, title_13, title_14, title_21, title_25, title_26, etc. +title_data_types = ["no_title"] + +# Backup Requirement - Whether to add instance to enterprise backup +# Options: "yes" or "no" +requires_backup = "no" + +# Power Schedule - Controls automatic on/off scheduling +# Options: +# - Always_On (default) +# - Full_Week_Business_Hours_9-5 +# - Full_Week_Core_Hours_7-7 +# - Full_Week_Hours_0600-1900 +# - Weekday_Business_Hours_9-5 +# - Weekday_Core_Hours_7-7 +# - Weekday_Hours_0600-1900 +power_schedule = "Weekday_Core_Hours_7-7" + +# MAP Migration Tag - For AWS Migration Accelerator Program credits +# Leave empty ("") if not applicable +# Examples: "migMSDNVLIJDZ: On-Prem Server, Storage, Networking" +map_migrated = "" + +# === SAFEGUARDS === + +# Enable Deletion Protection - Prevents accidental instance termination +enable_deletion_protection = false + +# === CUSTOM TAGS === + +# Additional custom tags to apply to the instance +additional_tags = { + CostCenter = "12345" + DataClass = "Internal" + Owner = "morga471" +} diff --git a/examples/ec2/linux/variables.tf b/examples/ec2/linux/variables.tf new file mode 100644 index 0000000..b84c0ea --- /dev/null +++ b/examples/ec2/linux/variables.tf @@ -0,0 +1,232 @@ +# === Provider and Global Configuration === + +variable "region" { + description = "AWS region where resources will be deployed" + type = string + default = "us-east-1" +} + +variable "environment" { + description = "Environment name for tagging (e.g., Production, QA, Dev)" + type = string + default = "Production" +} + +# === Service Catalog Configuration === + +variable "provisioned_product_name" { + description = "Name of the provisioned EC2 product in Service Catalog" + type = string + default = "census-rhel-instance" +} + +variable "portfolio_name_pattern" { + description = "Regex pattern to match the Service Catalog portfolio name" + type = string + default = "edl-portfolio" +} + +variable "product_name_pattern" { + description = "Regex pattern to match the Service Catalog product name" + type = string + default = "linux-product" +} + +# === EC2 Instance Configuration === + +variable "instance_type" { + description = "EC2 instance type (t3.nano, t3.micro, t3.small, t3.medium, c5.large, m5.large, etc.)" + type = string + default = "t3.small" +} + +variable "os_name" { + description = "Operating system version" + type = string + default = "RHEL9" + validation { + condition = contains(["RHEL8", "RHEL9"], var.os_name) + error_message = "os_name must be either RHEL8 or RHEL9." + } +} + +variable "instance_hostname" { + description = "EC2 instance hostname (local name, no domain). 3-16 chars, lowercase alphanumeric with hyphens/underscores" + type = string + default = "test-instance" + validation { + condition = can(regex("^[a-z0-9]{1}[a-z0-9_\\-]{1,14}[a-z0-9]{1}$", var.instance_hostname)) + error_message = "Hostname must be 3-16 chars, start/end with alphanumeric, contain only lowercase letters, numbers, hyphens, and underscores." + } +} + +variable "instance_description" { + description = "Description of the EC2 instance's purpose" + type = string + default = "Census RHEL instance provisioned via Terraform Service Catalog" +} + +# === Network Configuration === + +variable "vpc_id" { + description = "VPC ID where the EC2 instance will be deployed" + type = string +} + +variable "availability_zone" { + description = "Availability zone for the EC2 instance deployment" + type = string +} + +variable "security_group_names" { + description = "List of additional security group names. 'it-linux-base' is required" + type = list(string) + default = ["it-linux-base"] +} + +# === Project Configuration === + +variable "project_name" { + description = "Census project identifier (format: org_project_env_type-accountid). Must be one of the allowed values defined in the product template." + type = string + # Example: "edl_adsd_dapps_prod_prod-260954754267" + # The authoritative AllowedValues list is maintained in the Service Catalog product template. +} + +variable "project_role" { + description = "FinOps project role for cost allocation and tracking (optional)" + type = string + default = "" +} + +# === Storage Configuration === + +variable "volume_apps_size" { + description = "Size of the /apps disk in GiB (must be 10-1024)" + type = number + default = 10 + validation { + condition = var.volume_apps_size >= 10 && var.volume_apps_size <= 1024 + error_message = "volume_apps_size must be between 10 and 1024 GiB." + } +} + +variable "extra_volumes" { + description = "Additional EBS volumes to attach to the instance" + type = list(object({ + mount = string # Mount point name under /data (e.g., 'tomcat', 'oracle') + size = number # Volume size in GiB (10-1024) + })) + default = [] + validation { + condition = alltrue([ + for v in var.extra_volumes : v.size >= 10 && v.size <= 1024 + ]) + error_message = "Each volume size must be between 10 and 1024 GiB." + } +} + +# === Creator and Contact Information === + +variable "creator_jbid" { + description = "Creator's JBID (Census system user ID, format: letters + 3-6 digits)" + type = string + default = "terraform001" + validation { + condition = can(regex("^[a-zA-Z]{2,}[0-9]{3,6}$", var.creator_jbid)) + error_message = "JBID must start with 2+ letters followed by 3-6 digits (e.g., user001, svc12345)." + } +} + +variable "contact_email" { + description = "Provisioning user's email address for notifications" + type = string + validation { + condition = can(regex("^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$", var.contact_email)) + error_message = "Must be a valid email address." + } +} + +variable "incident_poc_email" { + description = "Team distribution list email for incident notifications (e.g., team@census.gov)" + type = string + validation { + condition = can(regex("^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$", var.incident_poc_email)) + error_message = "Must be a valid email address." + } +} + +# === Tagging and Compliance === + +variable "fisma_id" { + description = "FISMA ID (OIS asset tracking alias) for security compliance tagging" + type = string + default = "" + # Example values: "ADCOM_CEM", "ADDCP_ACSO", "OCIO_CSVD", etc. +} + +variable "title_data_types" { + description = "Title data types that will be stored on this instance" + type = list(string) + default = ["no_title"] + validation { + condition = alltrue([ + for t in var.title_data_types : contains([ + "no_title", "title_2", "title_5", "title_13", "title_14", "title_21", + "title_25", "title_26", "title_26_pending", "title_health_data", "title_pii", "title_pub_1075" + ], t) + ]) + error_message = "Invalid title data type(s). Valid options: no_title, title_2, title_5, title_13, title_14, title_21, title_25, title_26, title_26_pending, title_health_data, title_pii, title_pub_1075." + } +} + +variable "requires_backup" { + description = "Whether to add the instance to enterprise backup service" + type = string + default = "yes" + validation { + condition = contains(["yes", "no"], var.requires_backup) + error_message = "Must be either 'yes' or 'no'." + } +} + +variable "power_schedule" { + description = "Power schedule for automatic instance on/off management" + type = string + default = "Always_On" + validation { + condition = contains([ + "Always_On", + "Full_Week_Business_Hours_9-5", + "Full_Week_Core_Hours_7-7", + "Full_Week_Hours_0600-1900", + "Weekday_Business_Hours_9-5", + "Weekday_Core_Hours_7-7", + "Weekday_Hours_0600-1900" + ], var.power_schedule) + error_message = "Invalid power schedule. Options: Always_On, Full_Week_Business_Hours_9-5, Full_Week_Core_Hours_7-7, Full_Week_Hours_0600-1900, Weekday_Business_Hours_9-5, Weekday_Core_Hours_7-7, Weekday_Hours_0600-1900." + } +} + +variable "map_migrated" { + description = "MAP program migration tag for cost credits (5-25% discount based on migration type)" + type = string + default = "" + # Example values: "migMSDNVLIJDZ: On-Prem Server, Storage, Networking", "oracleMSDNVLIJDZ: On-Prem Oracle Database", etc. +} + +# === Safeguards === + +variable "enable_deletion_protection" { + description = "Enable deletion protection to prevent accidental instance termination" + type = bool + default = false +} + +# === Custom Tags === + +variable "additional_tags" { + description = "Additional custom tags to apply to the instance" + type = map(string) + default = {} +} diff --git a/examples/ecs/simple-ecs.tf b/examples/ecs/simple-ecs.tf new file mode 100644 index 0000000..981dc62 --- /dev/null +++ b/examples/ecs/simple-ecs.tf @@ -0,0 +1,75 @@ +terraform { + required_version = ">= 1.0" + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 5.0" + } + } +} + +provider "aws" { + region = var.region +} + +variable "region" { + description = "AWS region" + type = string + default = "us-east-1" +} + +variable "provisioned_product_name" { + description = "Name for the ECS provisioned product" + type = string + default = "example-ecs-service" +} + +variable "cluster_name" { + description = "ECS cluster name" + type = string +} + +variable "subnet_ids" { + description = "Subnet IDs for the ECS service" + type = list(string) +} + +variable "vpc_id" { + description = "VPC ID for the ECS service" + type = string +} + +module "ecs_service" { + source = "../../ecs" + + provisioned_product_name = var.provisioned_product_name + portfolio_name_pattern = "edl-portfolio" + product_name_pattern = "ecs-product" + + parameters = { + ClusterName = var.cluster_name + SubnetIds = join(",", var.subnet_ids) + VpcId = var.vpc_id + DesiredCount = "2" + } + + tags = { + Environment = "example" + ManagedBy = "Terraform" + } +} + +output "provisioned_product_id" { + description = "The ID of the provisioned product" + value = module.ecs_service.provisioned_product_id +} + +output "provisioned_product_arn" { + description = "The ARN of the provisioned product" + value = module.ecs_service.provisioned_product_arn +} + +output "stack_outputs" { + description = "CloudFormation stack outputs" + value = module.ecs_service.stack_outputs +} diff --git a/examples/rds/simple-rds.tf b/examples/rds/simple-rds.tf new file mode 100644 index 0000000..c416f51 --- /dev/null +++ b/examples/rds/simple-rds.tf @@ -0,0 +1,77 @@ +terraform { + required_version = ">= 1.0" + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 5.0" + } + } +} + +provider "aws" { + region = var.region +} + +variable "region" { + description = "AWS region" + type = string + default = "us-east-1" +} + +variable "provisioned_product_name" { + description = "Name for the RDS provisioned product" + type = string + default = "example-rds-postgres" +} + +variable "db_instance_class" { + description = "RDS instance class" + type = string + default = "db.t3.medium" +} + +variable "subnet_ids" { + description = "Subnet IDs for the RDS instance" + type = list(string) +} + +variable "vpc_id" { + description = "VPC ID for the RDS instance" + type = string +} + +module "rds_instance" { + source = "../../rds" + + provisioned_product_name = var.provisioned_product_name + portfolio_name_pattern = "edl-portfolio" + product_name_pattern = "rds-product" + + parameters = { + DBInstanceClass = var.db_instance_class + SubnetIds = join(",", var.subnet_ids) + VpcId = var.vpc_id + Engine = "postgres" + EngineVersion = "15.3" + } + + tags = { + Environment = "example" + ManagedBy = "Terraform" + } +} + +output "provisioned_product_id" { + description = "The ID of the provisioned product" + value = module.rds_instance.provisioned_product_id +} + +output "provisioned_product_arn" { + description = "The ARN of the provisioned product" + value = module.rds_instance.provisioned_product_arn +} + +output "stack_outputs" { + description = "CloudFormation stack outputs" + value = module.rds_instance.stack_outputs +} diff --git a/examples/s3/simple-s3.tf b/examples/s3/simple-s3.tf new file mode 100644 index 0000000..57c0626 --- /dev/null +++ b/examples/s3/simple-s3.tf @@ -0,0 +1,64 @@ +terraform { + required_version = ">= 1.0" + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 5.0" + } + } +} + +provider "aws" { + region = var.region +} + +variable "region" { + description = "AWS region" + type = string + default = "us-east-1" +} + +variable "provisioned_product_name" { + description = "Name for the S3 provisioned product" + type = string + default = "example-s3-bucket" +} + +variable "bucket_name" { + description = "S3 bucket name" + type = string +} + +module "s3_bucket" { + source = "../../s3" + + provisioned_product_name = var.provisioned_product_name + portfolio_name_pattern = "edl-portfolio" + product_name_pattern = "s3-product" + + parameters = { + BucketName = var.bucket_name + Versioning = "Enabled" + EncryptionEnabled = "true" + } + + tags = { + Environment = "example" + ManagedBy = "Terraform" + } +} + +output "provisioned_product_id" { + description = "The ID of the provisioned product" + value = module.s3_bucket.provisioned_product_id +} + +output "provisioned_product_arn" { + description = "The ARN of the provisioned product" + value = module.s3_bucket.provisioned_product_arn +} + +output "stack_outputs" { + description = "CloudFormation stack outputs" + value = module.s3_bucket.stack_outputs +}