diff --git a/.github/workflows/module-ci.yml b/.github/workflows/module-ci.yml new file mode 100644 index 0000000..b0c3ebf --- /dev/null +++ b/.github/workflows/module-ci.yml @@ -0,0 +1,64 @@ +name: Module CI Checks (Lab 4) + +on: + # Run on pushes to main branch + push: + branches: + - main + # Run on pull requests targeting main branch + pull_request: + branches: + - main + # Allows manual triggering from GitHub UI + workflow_dispatch: + +jobs: + test: + name: Lint, Validate, Test, Scan + runs-on: ubuntu-latest + + steps: + - name: Checkout Code + uses: actions/checkout@v4 + + - name: Setup Terraform + uses: hashicorp/setup-terraform@v3 + # with: + # terraform_version: "1.7.x" # Optional: Pin version + + - name: Terraform Format Check (Recursive) + working-directory: ./lab04 # Run from lab04 dir + run: terraform fmt -check -recursive + + - name: Terraform Init (for Root Validate) + working-directory: ./lab04 # Run from lab04 dir + run: terraform init # Needed for validate + + - name: Terraform Validate (Root Harness) + working-directory: ./lab04 # Run from lab04 dir + run: terraform validate # Checks if lab04/main.tf is valid + + - name: Init Module for Testing + working-directory: ./lab04/modules/sqs-secure # Go into module dir + env: # Add AWS credentials and region for test apply steps + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + AWS_DEFAULT_REGION: "us-west-2" + run: terraform init # Initialize providers needed for tests + + - name: Terraform Test Module + working-directory: ./lab04/modules/sqs-secure # Run test from module dir + env: # Add AWS credentials and region for test apply steps + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + AWS_DEFAULT_REGION: "us-west-2" + run: terraform test # Runs tests found in ./tests/ + + - name: Install Checkov + # Run pip install globally or in a virtual env + run: pip install checkov + + - name: Run Checkov Scan + working-directory: ./lab04 # Define where checkov runs FROM + # Point checkov specifically to the module directory + run: checkov -d ./modules/sqs-secure --quiet --skip-check CKV2_AWS_64 \ No newline at end of file diff --git a/lab01/main.tf b/lab01/main.tf new file mode 100644 index 0000000..7417566 --- /dev/null +++ b/lab01/main.tf @@ -0,0 +1,32 @@ +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.0" // Use an appropriate AWS provider version + } + random = { + source = "hashicorp/random" + version = "~> 3.1" + } + } + + # Backend is configured implicitly by HCP Terraform Workspace +} + +provider "aws" { + region = "us-west-2" // Ensure correct region +} + +resource "random_string" "suffix" { + length = 8 + special = false + upper = false +} + +resource "aws_s3_bucket" "learning_bucket" { + bucket = "tf-adv-lab01-${random_string.suffix.result}" # Construct unique name + + tags = { + Name = "TF Advanced Lab 1 Bucket" + } +} \ No newline at end of file diff --git a/lab04/main.tf b/lab04/main.tf index c14ef31..2be9ac3 100644 --- a/lab04/main.tf +++ b/lab04/main.tf @@ -4,9 +4,9 @@ terraform { source = "hashicorp/aws" version = "~> 5.0" } - # Random provider needed by module via S3 bucket name + # Random provider needed by module via S3 bucket name random = { - source = "hashicorp/random" + source = "hashicorp/random" version = "~> 3.1" } } diff --git a/lab04/modules/main.tf b/lab04/modules/main.tf index c14ef31..2be9ac3 100644 --- a/lab04/modules/main.tf +++ b/lab04/modules/main.tf @@ -4,9 +4,9 @@ terraform { source = "hashicorp/aws" version = "~> 5.0" } - # Random provider needed by module via S3 bucket name + # Random provider needed by module via S3 bucket name random = { - source = "hashicorp/random" + source = "hashicorp/random" version = "~> 3.1" } } diff --git a/lab04/modules/sqs-secure/main.tf b/lab04/modules/sqs-secure/main.tf index fdc6fb5..9e180fc 100644 --- a/lab04/modules/sqs-secure/main.tf +++ b/lab04/modules/sqs-secure/main.tf @@ -1,7 +1,7 @@ resource "aws_kms_key" "sqs_key" { - description = "KMS key for encrypting ${var.queue_name_prefix} SQS queues" + description = "KMS key for encrypting ${var.queue_name_prefix} SQS queues" enable_key_rotation = true - tags = var.tags + tags = var.tags } resource "aws_sqs_queue" "dlq" { diff --git a/lab04/modules/sqs-secure/outputs.tf b/lab04/modules/sqs-secure/outputs.tf index 701e11a..9bb70a1 100644 --- a/lab04/modules/sqs-secure/outputs.tf +++ b/lab04/modules/sqs-secure/outputs.tf @@ -11,7 +11,7 @@ output "main_queue_url" { output "dlq_arn" { description = "The ARN of the Dead Letter Queue (DLQ), if created." # Use try() to gracefully return null if the DLQ doesn't exist (count=0) - value = try(aws_sqs_queue.dlq[0].arn, null) + value = try(aws_sqs_queue.dlq[0].arn, null) } output "kms_key_arn" { diff --git a/lab04/modules/sqs-secure/tests/sqs_secure_test.tftest.hcl b/lab04/modules/sqs-secure/tests/sqs_secure_test.tftest.hcl index 070fb97..ec38023 100644 --- a/lab04/modules/sqs-secure/tests/sqs_secure_test.tftest.hcl +++ b/lab04/modules/sqs-secure/tests/sqs_secure_test.tftest.hcl @@ -1,96 +1,74 @@ variables { - queue_name_prefix = "lab3-test-defaults" + queue_name_prefix = "lab03-test-defaults" # enable_dlq defaults to true in the module # tags defaults to {} in the module } +# Simple test to ensure plan works properly run "plan_default_settings" { - # command = plan is the default, so it's optional here - - check "plan_produces_changes" { - assert { - # Verify that the plan is not empty - condition = length(resource_changes) > 0 - error_message = "Plan should propose at least one resource change with default settings." - } - } + command = plan + # No assert needed; success means plan executes without errors } +# Test to ensure ARNs are being assigned correctly run "apply_and_check_outputs" { command = apply - check "outputs_are_valid" { - assert { - condition = output.main_queue_arn != null && substr(output.main_queue_arn, 0, 12) == "arn:aws:sqs:" - error_message = "Main queue ARN should be a valid SQS ARN." - } - assert { - condition = output.kms_key_arn != null && substr(output.kms_key_arn, 0, 12) == "arn:aws:kms:" - error_message = "KMS key ARN should be a valid KMS ARN." - } - assert { - # DLQ is enabled by default, so its ARN should be present - condition = output.dlq_arn != null && substr(output.dlq_arn, 0, 12) == "arn:aws:sqs:" - error_message = "DLQ ARN should be a valid SQS ARN when DLQ is enabled." - } - # Could also add checks for main_queue_url format if needed + assert { + condition = output.main_queue_arn != null && substr(output.main_queue_arn, 0, 12) == "arn:aws:sqs:" + error_message = "Main queue ARN should be a valid SQS ARN." + } + assert { + condition = output.kms_key_arn != null && substr(output.kms_key_arn, 0, 12) == "arn:aws:kms:" + error_message = "KMS key ARN should be a valid KMS ARN." + } + assert { + # DLQ is enabled by default, so its ARN should be present + condition = output.dlq_arn != null && substr(output.dlq_arn, 0, 12) == "arn:aws:sqs:" + error_message = "DLQ ARN should be a valid SQS ARN when DLQ is enabled." } + # Could also add checks for main_queue_url format if needed + } -run "plan_dlq_disabled" { +# Test to ensure DLQ is not created when enable_dlq is false +run "apply_dlq_disabled" { + command = apply # Override variables specifically for this run variables { - queue_name_prefix = "lab3-test-no-dlq" + queue_name_prefix = "lab03-test-no-dlq" enable_dlq = false # Disable the DLQ for this test case } - - check "plan_creates_correct_resources" { - assert { - # Check that the DLQ resource change is absent - condition = !anytrue([for rc in resource_changes : rc.address == "module.test.aws_sqs_queue.dlq"]) - # Note: The address includes 'module.test' because terraform test wraps the module under test. - error_message = "DLQ resource should not be present in the plan when enable_dlq is false." - } - assert { - # Check that the main queue and KMS key *are* present - condition = anytrue([for rc in resource_changes : rc.address == "module.test.aws_sqs_queue.main"]) && \ - anytrue([for rc in resource_changes : rc.address == "module.test.aws_kms_key.sqs_key"]) - error_message = "Main queue and KMS key should be present in the plan." - } + assert { + # Check that given count is zero, the list of dlq objects should be empty + condition = length(aws_sqs_queue.dlq) == 0 + error_message = "DLQ should be an empty list when enable_dlq is false" } -} - -run "apply_dlq_disabled_outputs" { - command = apply - - # We need to reference the variables defined in the preceding run block. - # Terraform test currently reuses the *last specified* variables block if a run block - # doesn't define its own. So, this run will correctly use enable_dlq = false. - # Be mindful of this behavior in complex tests. - - check "outputs_reflect_dlq_disabled" { - assert { - condition = output.dlq_arn == null - error_message = "DLQ ARN output should be null when enable_dlq is false." - } - assert { - condition = output.main_queue_arn != null # Main queue should still exist - error_message = "Main queue ARN should still be present." - } + assert { + # Check that the output.dlq_arn is not set + condition = output.dlq_arn == null + error_message = "DLQ ARN output should be null when enable_dlq is false." + } + assert { + # Check that the main queue is still present + condition = aws_sqs_queue.main.name == "lab03-test-no-dlq" + error_message = "Main queue should be created with correct name" + } + assert { + # Check that the KMS key is still present + condition = aws_kms_key.sqs_key.arn != null + error_message = "KMS key should be created" } } run "fail_on_empty_prefix" { variables { queue_name_prefix = "" # Invalid input - enable_dlq = false # Keep other variables consistent if needed } + command = plan - # command = plan is default - - # Expect the plan to fail with the specific validation error message + # Test succeeds if validation for queue_name_prefix errors out expect_failures = [ - var.queue_name_prefix.validation[0].error_message, - # Can reference module variable validation messages directly. + var.queue_name_prefix ] } \ No newline at end of file diff --git a/lab05/dev/main.tf b/lab05/dev/main.tf new file mode 100644 index 0000000..1ee5eda --- /dev/null +++ b/lab05/dev/main.tf @@ -0,0 +1,48 @@ +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.0" + } + random = { + source = "hashicorp/random" + version = "~> 3.1" + } + } + # Backend configured by HCP Workspace +} + +# Variables will be populated by HCP Workspace Variables +variable "queue_prefix" { + type = string + description = "Prefix for queue names, provided by workspace." +} + +variable "environment_tag" { + type = string + description = "Tag value for the Environment tag, provided by workspace." +} + +provider "aws" { + region = "us-west-2" +} + +module "dev_queue" { + # Replace with your HCP Org Name + source = "app.terraform.io/tf-advanced-labs/sqs-secure/aws" + version = "~> 1.0.0" # Use constraint matching published version + + queue_name_prefix = var.queue_prefix # From workspace variable + enable_dlq = true # Keep DLQ enabled for dev + + tags = { + Project = "Advanced TF Course" + Environment = var.environment_tag # From workspace variable + GitOpsManaged = "true" # <<< Added Tag + Purpose = "GitOps Demo" # <<< Added Tag + Dept = "Engineering" # One more added tag + } +} + +# Optional: Define outputs if needed for cross-workspace dependencies later +# output "dev_queue_arn" { value = module.dev_queue.main_queue_arn } \ No newline at end of file diff --git a/lab05/prod/main.tf b/lab05/prod/main.tf new file mode 100644 index 0000000..876507a --- /dev/null +++ b/lab05/prod/main.tf @@ -0,0 +1,45 @@ +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.0" + } + random = { + source = "hashicorp/random" + version = "~> 3.1" + } + } + # Backend configured by HCP Workspace +} + +# Variables will be populated by HCP Workspace Variables +variable "queue_prefix" { + type = string + description = "Prefix for queue names, provided by workspace." +} + +variable "environment_tag" { + type = string + description = "Tag value for the Environment tag, provided by workspace." +} + +provider "aws" { + region = "us-west-2" +} + +module "prod_queue" { + # Replace with your HCP Org Name + source = "app.terraform.io/tf-advanced-labs/sqs-secure/aws" + version = "~> 1.0.0" # Use constraint matching published version + + queue_name_prefix = var.queue_prefix # From workspace variable + enable_dlq = true # Keep DLQ enabled for prod + + tags = { + Project = "Advanced TF Course" + Environment = var.environment_tag # From workspace variable + } +} + +# Optional: Define outputs if needed +# output "prod_queue_arn" { value = module.prod_queue.main_queue_arn } \ No newline at end of file