diff --git a/.github/actions/trivy/action.yaml b/.github/actions/trivy/action.yaml
index be940ce..010386e 100644
--- a/.github/actions/trivy/action.yaml
+++ b/.github/actions/trivy/action.yaml
@@ -8,8 +8,13 @@ runs:
components_exit_code=0
modules_exit_code=0
- ./scripts/terraform/trivy.sh ./infrastructure/terraform/components || components_exit_code=$?
- ./scripts/terraform/trivy.sh ./infrastructure/terraform/modules || modules_exit_code=$?
+ if [ -d ./infrastructure/terraform/components ]; then
+ ./scripts/terraform/trivy.sh ./infrastructure/terraform/components || components_exit_code=$?
+ fi
+
+ if [ -d ./infrastructure/terraform/modules ]; then
+ ./scripts/terraform/trivy.sh ./infrastructure/terraform/modules || modules_exit_code=$?
+ fi
if [ $components_exit_code -ne 0 ] || [ $modules_exit_code -ne 0 ]; then
echo "Trivy misconfigurations detected."
diff --git a/infrastructure/modules/ssl/README.md b/infrastructure/modules/ssl/README.md
new file mode 100644
index 0000000..d7ecda3
--- /dev/null
+++ b/infrastructure/modules/ssl/README.md
@@ -0,0 +1,40 @@
+
+
+
+
+## Requirements
+
+| Name | Version |
+|------|---------|
+| [terraform](#requirement\_terraform) | >= 1.10.1 |
+| [tls](#requirement\_tls) | 4.1.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 tfscaffold component | `string` | n/a | yes |
+| [default\_tags](#input\_default\_tags) | A map of default tags to apply to all taggable resources within the component | `map(string)` | `{}` | no |
+| [environment](#input\_environment) | The name of the tfscaffold environment | `string` | n/a | yes |
+| [name](#input\_name) | A unique name to distinguish this module invocation from others within the same CSI scope | `string` | n/a | yes |
+| [project](#input\_project) | The name of the tfscaffold project | `string` | n/a | yes |
+| [region](#input\_region) | The AWS Region | `string` | n/a | yes |
+| [subject\_common\_name](#input\_subject\_common\_name) | Common name for certificate subject | `string` | n/a | yes |
+| [subject\_country](#input\_subject\_country) | Country for certificate subject | `string` | `"GB"` | no |
+| [subject\_locality](#input\_subject\_locality) | Locality for certificate subject | `string` | `"Leeds"` | no |
+| [subject\_organization](#input\_subject\_organization) | Organization for certificate subject | `string` | `"NHS England"` | no |
+| [subject\_organizational\_unit](#input\_subject\_organizational\_unit) | Organizational unit for certificate subject | `string` | `"NHS Notify"` | no |
+| [subject\_province](#input\_subject\_province) | Province for certificate subject | `string` | `"West Yorkshire"` | no |
+## Modules
+
+No modules.
+## Outputs
+
+| Name | Description |
+|------|-------------|
+| [cacert\_pem](#output\_cacert\_pem) | Truststore |
+| [server\_crt](#output\_server\_crt) | Server Certificate |
+| [server\_key](#output\_server\_key) | Server Key |
+
+
+
diff --git a/infrastructure/modules/ssl/locals.tf b/infrastructure/modules/ssl/locals.tf
new file mode 100644
index 0000000..398b1a4
--- /dev/null
+++ b/infrastructure/modules/ssl/locals.tf
@@ -0,0 +1,24 @@
+locals {
+ module = "ssl"
+
+ # Compound Scope Identifier
+ 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/ssl/outputs.tf b/infrastructure/modules/ssl/outputs.tf
new file mode 100644
index 0000000..4241a55
--- /dev/null
+++ b/infrastructure/modules/ssl/outputs.tf
@@ -0,0 +1,17 @@
+output "server_crt" {
+ description = "Server Certificate"
+ value = tls_locally_signed_cert.integration_testing_client_cert.cert_pem
+ sensitive = true
+}
+
+output "server_key" {
+ description = "Server Key"
+ value = tls_private_key.integration_testing_client_key.private_key_pem
+ sensitive = true
+}
+
+output "cacert_pem" {
+ description = "Truststore"
+ value = tls_self_signed_cert.ca_cert.cert_pem
+ sensitive = true
+}
diff --git a/infrastructure/modules/ssl/providers.tf b/infrastructure/modules/ssl/providers.tf
new file mode 100644
index 0000000..ad6ac72
--- /dev/null
+++ b/infrastructure/modules/ssl/providers.tf
@@ -0,0 +1,11 @@
+terraform {
+ required_providers {
+ aws = {
+ source = "hashicorp/aws"
+ }
+ tls = {
+ source = "hashicorp/tls"
+ version = "4.1.0"
+ }
+ }
+}
diff --git a/infrastructure/modules/ssl/ssm_parameter_ca_crt.tf b/infrastructure/modules/ssl/ssm_parameter_ca_crt.tf
new file mode 100644
index 0000000..c824b9d
--- /dev/null
+++ b/infrastructure/modules/ssl/ssm_parameter_ca_crt.tf
@@ -0,0 +1,9 @@
+resource "aws_ssm_parameter" "ca_crt" {
+ name = format("/%s/%s/${local.module}/ca-crt", var.project, var.environment)
+ type = "SecureString"
+ value = tls_self_signed_cert.ca_cert.cert_pem
+
+ lifecycle {
+ create_before_destroy = true
+ }
+}
diff --git a/infrastructure/modules/ssl/ssm_parameter_ca_key.tf b/infrastructure/modules/ssl/ssm_parameter_ca_key.tf
new file mode 100644
index 0000000..7c25260
--- /dev/null
+++ b/infrastructure/modules/ssl/ssm_parameter_ca_key.tf
@@ -0,0 +1,9 @@
+resource "aws_ssm_parameter" "ca_key" {
+ name = format("/%s/%s/${local.module}/ca-key", var.project, var.environment)
+ type = "SecureString"
+ value = tls_private_key.ca_key.private_key_pem
+
+ lifecycle {
+ create_before_destroy = true
+ }
+}
diff --git a/infrastructure/modules/ssl/ssm_parameter_server_crt.tf b/infrastructure/modules/ssl/ssm_parameter_server_crt.tf
new file mode 100644
index 0000000..d3cff53
--- /dev/null
+++ b/infrastructure/modules/ssl/ssm_parameter_server_crt.tf
@@ -0,0 +1,9 @@
+resource "aws_ssm_parameter" "server_crt" {
+ name = format("/%s/%s/${local.module}/server-crt", var.project, var.environment)
+ type = "SecureString"
+ value = tls_locally_signed_cert.integration_testing_client_cert.cert_pem
+
+ lifecycle {
+ create_before_destroy = true
+ }
+}
diff --git a/infrastructure/modules/ssl/ssm_parameter_server_key.tf b/infrastructure/modules/ssl/ssm_parameter_server_key.tf
new file mode 100644
index 0000000..c9cabcb
--- /dev/null
+++ b/infrastructure/modules/ssl/ssm_parameter_server_key.tf
@@ -0,0 +1,9 @@
+resource "aws_ssm_parameter" "server_key" {
+ name = format("/%s/%s/${local.module}/server-key", var.project, var.environment)
+ type = "SecureString"
+ value = tls_private_key.integration_testing_client_key.private_key_pem
+
+ lifecycle {
+ create_before_destroy = true
+ }
+}
diff --git a/infrastructure/modules/ssl/tls_cert_request_server_csr.tf b/infrastructure/modules/ssl/tls_cert_request_server_csr.tf
new file mode 100644
index 0000000..7f2f88f
--- /dev/null
+++ b/infrastructure/modules/ssl/tls_cert_request_server_csr.tf
@@ -0,0 +1,19 @@
+resource "tls_cert_request" "server_csr" {
+
+ private_key_pem = tls_private_key.integration_testing_client_key.private_key_pem
+
+ dns_names = [var.subject_common_name]
+
+ subject {
+ country = var.subject_country
+ province = var.subject_province
+ locality = var.subject_locality
+ common_name = var.subject_common_name
+ organization = var.subject_organization
+ organizational_unit = var.subject_organizational_unit
+ }
+
+ depends_on = [
+ tls_private_key.integration_testing_client_key,
+ ]
+}
diff --git a/infrastructure/modules/ssl/tls_locally_signed_cert_server_cert.tf b/infrastructure/modules/ssl/tls_locally_signed_cert_server_cert.tf
new file mode 100644
index 0000000..25c1b65
--- /dev/null
+++ b/infrastructure/modules/ssl/tls_locally_signed_cert_server_cert.tf
@@ -0,0 +1,21 @@
+resource "tls_locally_signed_cert" "integration_testing_client_cert" {
+ cert_request_pem = tls_cert_request.server_csr.cert_request_pem
+ ca_private_key_pem = tls_private_key.ca_key.private_key_pem
+ ca_cert_pem = tls_self_signed_cert.ca_cert.cert_pem
+
+ validity_period_hours = 8760
+
+ allowed_uses = [
+ "digital_signature",
+ "key_encipherment",
+ "server_auth",
+ "client_auth",
+ ]
+
+ depends_on = [
+ tls_private_key.ca_key,
+ tls_self_signed_cert.ca_cert,
+ tls_private_key.integration_testing_client_key,
+ tls_cert_request.server_csr
+ ]
+}
diff --git a/infrastructure/modules/ssl/tls_private_key_ca_key.tf b/infrastructure/modules/ssl/tls_private_key_ca_key.tf
new file mode 100644
index 0000000..e34a634
--- /dev/null
+++ b/infrastructure/modules/ssl/tls_private_key_ca_key.tf
@@ -0,0 +1,4 @@
+resource "tls_private_key" "ca_key" {
+ algorithm = "RSA"
+ rsa_bits = 4096
+}
diff --git a/infrastructure/modules/ssl/tls_private_key_server_key.tf b/infrastructure/modules/ssl/tls_private_key_server_key.tf
new file mode 100644
index 0000000..3dd8848
--- /dev/null
+++ b/infrastructure/modules/ssl/tls_private_key_server_key.tf
@@ -0,0 +1,4 @@
+resource "tls_private_key" "integration_testing_client_key" {
+ algorithm = "RSA"
+ rsa_bits = 4096
+}
diff --git a/infrastructure/modules/ssl/tls_self_signed_cert_ca_cert.tf b/infrastructure/modules/ssl/tls_self_signed_cert_ca_cert.tf
new file mode 100644
index 0000000..7c43574
--- /dev/null
+++ b/infrastructure/modules/ssl/tls_self_signed_cert_ca_cert.tf
@@ -0,0 +1,22 @@
+resource "tls_self_signed_cert" "ca_cert" {
+ private_key_pem = tls_private_key.ca_key.private_key_pem
+
+ is_ca_certificate = true
+
+ subject {
+ country = var.subject_country
+ province = var.subject_province
+ locality = var.subject_locality
+ common_name = var.subject_common_name
+ organization = var.subject_organization
+ organizational_unit = var.subject_organizational_unit
+ }
+
+ validity_period_hours = 17520
+
+ allowed_uses = [
+ "digital_signature",
+ "cert_signing",
+ "crl_signing",
+ ]
+}
diff --git a/infrastructure/modules/ssl/variables.tf b/infrastructure/modules/ssl/variables.tf
new file mode 100644
index 0000000..28b9b15
--- /dev/null
+++ b/infrastructure/modules/ssl/variables.tf
@@ -0,0 +1,82 @@
+##
+# Basic Required Variables for tfscaffold Modules
+##
+
+variable "project" {
+ type = string
+ description = "The name of the tfscaffold project"
+}
+
+variable "environment" {
+ type = string
+ description = "The name of the tfscaffold environment"
+}
+
+variable "component" {
+ type = string
+ description = "The name of the tfscaffold component"
+}
+
+variable "aws_account_id" {
+ type = string
+ description = "The AWS Account ID (numeric)"
+}
+
+variable "region" {
+ type = string
+ description = "The AWS Region"
+}
+
+##
+# tfscaffold variables specific to this module
+##
+
+variable "default_tags" {
+ type = map(string)
+ description = "A map of default tags to apply to all taggable resources within the component"
+ default = {}
+}
+
+##
+# Variables specific to this module
+##
+
+variable "name" {
+ type = string
+ description = "A unique name to distinguish this module invocation from others within the same CSI scope"
+}
+
+variable "subject_country" {
+ type = string
+ description = "Country for certificate subject"
+ default = "GB"
+}
+
+variable "subject_province" {
+ type = string
+ description = "Province for certificate subject"
+ default = "West Yorkshire"
+}
+
+variable "subject_locality" {
+ type = string
+ description = "Locality for certificate subject"
+ default = "Leeds"
+}
+
+variable "subject_common_name" {
+ type = string
+ description = "Common name for certificate subject"
+}
+
+variable "subject_organization" {
+ type = string
+ description = "Organization for certificate subject"
+ default = "NHS England"
+}
+
+variable "subject_organizational_unit" {
+ type = string
+ description = "Organizational unit for certificate subject"
+ default = "NHS Notify"
+}
diff --git a/infrastructure/modules/ssl/versions.tf b/infrastructure/modules/ssl/versions.tf
new file mode 100644
index 0000000..c43599a
--- /dev/null
+++ b/infrastructure/modules/ssl/versions.tf
@@ -0,0 +1,3 @@
+terraform {
+ required_version = ">= 1.10.1"
+}