Repository: salesforce/cloud-guardrails (language: python)

Issue:
Feature: Policies with Parameters
Right now, it does not support the use of Policies with Parameters - just policies that have `Effect` parameter only.


Discussion / hints:

Fix patch:
diff --git a/.gitignore b/.gitignore
index 33df6e7..a3ca6f3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -70,4 +70,5 @@ exclusions.yml
 config.yml
 **/state.tf
 azure-guardrails-*
-**/~$*.xlsx
\ No newline at end of file
+**/~$*.xlsx
+research.py
\ No newline at end of file
diff --git a/azure_guardrails/command/generate_terraform.py b/azure_guardrails/command/generate_terraform.py
index bb09aa6..e6ae731 100644
--- a/azure_guardrails/command/generate_terraform.py
+++ b/azure_guardrails/command/generate_terraform.py
@@ -9,13 +9,12 @@
 import click
 from colorama import Fore, Back
 from azure_guardrails import set_log_level, set_stream_logger
-from azure_guardrails.logic.terraform import get_terraform_template
+from azure_guardrails.logic.terraform import get_terraform_template, TerraformTemplate
 from azure_guardrails.shared import utils, validate
 from azure_guardrails.shared.compliance_data import ComplianceCoverage
 from azure_guardrails.shared.config import get_default_config, get_config_from_file
 from azure_guardrails.logic.services import Services, Service
 
-
 logger = logging.getLogger(__name__)
 
 supported_services_argument_values = utils.get_service_names()
@@ -123,7 +122,8 @@ def generate_terraform(service: str, with_parameters: bool, target_name: str, ta
         set_stream_logger(level=log_level)
 
     if not config_file:
-        logger.info("You did not supply an config file. Consider creating one to exclude different policies. We will use the default one.")
+        logger.info(
+            "You did not supply an config file. Consider creating one to exclude different policies. We will use the default one.")
         config = get_default_config()
     else:
         config = get_config_from_file(config_file=config_file)
@@ -135,6 +135,13 @@ def generate_terraform(service: str, with_parameters: bool, target_name: str, ta
     else:
         management_group = target_name
 
+    # # TODO: Move this back properly
+    # if with_parameters:
+    #     services = Services(config=config)
+    #     policy_names = services.get_display_names_sorted_by_service(with_parameters=with_parameters)
+    #     # Get the parameters for each of them
+    #
+    # else:
     if generate_summary:
         if service == "all":
             services = Services(config=config)
@@ -148,13 +155,30 @@ def generate_terraform(service: str, with_parameters: bool, target_name: str, ta
     else:
         if service == "all":
             services = Services(config=config)
-            policy_names = services.get_display_names_sorted_by_service(with_parameters=with_parameters)
-            result = get_terraform_template(name=policy_set_name, policy_names=policy_names, subscription_name=subscription_name,
-                                            management_group=management_group, enforcement_mode=enforcement_mode, module_source=terraform_module_source)
         else:
-            services = Service(service_name=service, config=config)
-            policy_names = services.get_display_names_sorted_by_service(with_parameters=with_parameters)
+            services = Services(service_names=[service], config=config)
+        if with_parameters:
+            display_names = services.get_display_names_by_service_with_parameters(has_defaults=True)
+            terraform_template = TerraformTemplate(name=policy_set_name, parameters=display_names,
+                                                   subscription_name=subscription_name,
+                                                   management_group=management_group,
+                                                   enforcement_mode=enforcement_mode,
+                                                   module_source=terraform_module_source)
+            result = terraform_template.rendered()
+        else:
+            display_names = services.get_display_names_sorted_by_service(with_parameters=with_parameters)
+            result = get_terraform_template(name=policy_set_name, policy_names=display_names,
+                                            subscription_name=subscription_name,
+                                            management_group=management_group, enforcement_mode=enforcement_mode,
+                                            module_source=terraform_module_source)
+            # services = Service(service_name=service, config=config)
 
-            result = get_terraform_template(name=policy_set_name, policy_names=policy_names, subscription_name=subscription_name,
-                                            management_group=management_group, enforcement_mode=enforcement_mode, module_source=terraform_module_source)
+            # display_names = services.get_display_names_sorted_by_service(with_parameters=with_parameters)
+            # result = get_terraform_template(name=policy_set_name, policy_names=display_names,
+            #                                 subscription_name=subscription_name,
+            #                                 management_group=management_group, enforcement_mode=enforcement_mode,
+            #                                 module_source=terraform_module_source)
+        # policy_names = services.get_display_names_sorted_by_service(with_parameters=with_parameters)
+        # result = get_terraform_template(name=policy_set_name, policy_names=policy_names, subscription_name=subscription_name,
+        #                                 management_group=management_group, enforcement_mode=enforcement_mode, module_source=terraform_module_source)
         print(result)
diff --git a/azure_guardrails/logic/parameters/policy-set-with-parameters.tf b/azure_guardrails/logic/parameters/policy-set-with-parameters.tf
new file mode 100644
index 0000000..3397709
--- /dev/null
+++ b/azure_guardrails/logic/parameters/policy-set-with-parameters.tf
@@ -0,0 +1,59 @@
+variable "name" { default = "{{ t.name }}" }
+variable "subscription_name" { default = "{{ t.subscription_name }}" }
+variable "management_group" { default = "{{ t.management_group }}" }
+variable "enforcement_mode" { default = {{ t.enforcement_mode }} }
+
+variable "category" {
+  type    = string
+  default = "Testing"
+}
+provider "azurerm" {
+  features {}
+}
+
+locals {
+  policy_names = [{% for service_name, policies_with_params in t.sorted_policies.items() %}
+    # -----------------------------------------------------------------------------------------------------------------
+    # {{ service_name }}
+    # -----------------------------------------------------------------------------------------------------------------{% for key, value in policies_with_params.items() %}
+    "{{ key }}",{% endfor %}{% endfor %}
+  ]
+  policy_definition_map = zipmap(
+    data.azurerm_policy_definition.definition_lookups.*.display_name,
+    data.azurerm_policy_definition.definition_lookups.*.id
+  )
+}
+
+data "azurerm_policy_definition" "definition_lookups" {
+  count        = length(local.policy_names)
+  display_name = local.policy_names[count.index]
+}
+
+resource "azurerm_policy_set_definition" "guardrails" {
+  name                  = var.name
+  policy_type           = "Custom"
+  display_name          = var.name
+  description           = var.name
+  management_group_name = var.management_group == "" ? null : var.management_group
+  metadata = tostring(jsonencode({
+    category = var.category
+  }))
+
+  {% for service_name, policies_with_params in t.policy_definition_reference_parameters.items() %}
+  policy_definition_reference {
+    policy_definition_id = lookup(local.policy_definition_map, "{{ service_name }}")
+    parameter_values = jsonencode({ {% for key in policies_with_params %}
+      {{ key }} = { "value" : "[parameters('{{ key }}')]" }{% endfor %}
+    })
+    reference_id = null
+  }
+  {% endfor %}
+
+  parameters = <<PARAMETERS
+{{ t.all_parameters }}
+PARAMETERS
+}
+
+output "id" {
+  value = azurerm_policy_set_definition.guardrails.id
+}
\ No newline at end of file
diff --git a/azure_guardrails/logic/policy_definition.py b/azure_guardrails/logic/policy_definition.py
index 124646f..c1bf7c8 100644
--- a/azure_guardrails/logic/policy_definition.py
+++ b/azure_guardrails/logic/policy_definition.py
@@ -19,6 +19,7 @@ def __init__(self, policy_content: dict):
         self.category = policy_content.get("properties").get("metadata").get("category", None)
         self.properties = Properties(properties_json=policy_content.get("properties"))
         self.display_name = self.properties.display_name
+        self.parameters = self.properties.parameters
 
     def __repr__(self):
         return json.dumps(self.content)
@@ -47,14 +48,17 @@ def includes_parameters(self) -> bool:
         return result
 
     @property
-    def includes_parameters_without_default_values(self) -> bool:
+    def parameters_have_defaults(self) -> bool:
         """Determines if the policy requires parameters that do not have defaultValues"""
-        result = False
+        result = True
         for parameter in self.properties.parameters:
             if parameter.name == "effect":
-                pass
-            if not parameter.default_value:
-                result = True
+                continue
+            # TODO: This currently allows empty lists, which will throw an error when you try to apply it.
+            # We should allow you to print out the options to a YAML file and fill it out like a form.
+            # So right now, it will create a long Kubernetes policy, but it will have lots of empty lists that we have to fill out. Oh well.
+            if not parameter.default_value and parameter.default_value != [] and parameter.default_value != "":
+                result = False
                 break
         return result
 
@@ -115,6 +119,17 @@ def __init__(self, name, parameter_json):
         self.name = name
         self.parameter_json = parameter_json
         self.type = self.parameter_json.get("type")
+        # Do some weird stuff because in this case, [] vs None has different implications
+        if "defaultValue" in str(self.parameter_json):
+            default_value = self.parameter_json.get("defaultValue", None)
+            if default_value:
+                self.default_value = default_value
+            else:
+                if self.type == "Array":
+                    self.default_value = []
+                else:
+                    self.default_value = None
+
         self.default_value = self.parameter_json.get("defaultValue", None)
         self.allowed_values = self.parameter_json.get("allowedValues", None)
 
@@ -126,6 +141,29 @@ def __init__(self, name, parameter_json):
         self.strong_type = self.metadata_json.get("strongType", None)
         self.assign_permissions = self.metadata_json.get("assignPermissions", None)
 
+    def __repr__(self):
+        return json.dumps(self.json())
+
+    def json(self) -> dict:
+        result = dict(
+            name=self.name,
+            type=self.type,
+            description=self.description,
+            display_name=self.display_name,
+        )
+        # Return default value only if it has a value, or if it is an empty list or empty string
+        if self.default_value or self.default_value == [] or self.default_value == "":
+            result["default_value"] = self.default_value
+        if self.allowed_values:
+            result["allowed_values"] = self.allowed_values
+        if self.category:
+            result["category"] = self.category
+        if self.strong_type:
+            result["strong_type"] = self.strong_type
+        if self.assign_permissions:
+            result["assign_permissions"] = self.assign_permissions
+        return result
+
     def _allowed_values(self):
         allowed_values = self.parameter_json.get("allowedValues", None)
         allowed_values = [x.lower() for x in allowed_values]
diff --git a/azure_guardrails/logic/services.py b/azure_guardrails/logic/services.py
index 731514e..efb071f 100644
--- a/azure_guardrails/logic/services.py
+++ b/azure_guardrails/logic/services.py
@@ -3,7 +3,7 @@
 from typing import List
 from azure_guardrails.shared import utils
 from azure_guardrails.shared.config import DEFAULT_CONFIG, Config
-from azure_guardrails.logic.policy_definition import PolicyDefinition
+from azure_guardrails.logic.policy_definition import PolicyDefinition, Parameter
 
 logger = logging.getLogger(__name__)
 
@@ -89,11 +89,47 @@ def get_display_names_sorted_by_service(self, with_parameters: bool = False, wit
             display_names[self.service_name] = service_display_names
         return display_names
 
+    def get_policy_definition_parameters(self, display_name: str, has_defaults: bool = True) -> dict:
+        """Return a dictionary of parameters objects"""
+        parameters = {}
+        for policy_definition in self.policy_definitions:
+            if policy_definition.display_name == display_name:
+                if has_defaults:
+                    if policy_definition.parameters_have_defaults:
+                        for parameter in policy_definition.parameters:
+                            if parameter.name == "effect":
+                                continue
+                            if not parameter.default_value and parameter.default_value != [] and parameter.default_value != "":
+                                # If it doesn't have default values, then we want to skip it and return an empty dict
+                                return {}
+                            parameters[parameter.name] = parameter.json()
+                else:
+                    for parameter in policy_definition.parameters:
+                        if parameter.name == "effect":
+                            continue
+                        parameters[parameter.name] = parameter.json()
+                break
+        return parameters
+
+    def get_display_names_by_service_with_parameters(self, has_defaults: bool = True) -> dict:
+        # TODO: Figure out if I should change these methods?
+        service_display_names = self.get_display_names(with_parameters=True,
+                                                       with_modify_capabilities=False,
+                                                       all_policies=False)
+        service_display_names = list(dict.fromkeys(service_display_names))  # remove duplicates
+        service_parameters = {}
+        for service_display_name in service_display_names:
+            parameters = self.get_policy_definition_parameters(display_name=service_display_name, has_defaults=has_defaults)
+            if parameters:
+                service_parameters[service_display_name] = parameters
+        return service_parameters
+
 
 class Services:
-    def __init__(self, config: Config = DEFAULT_CONFIG):
-        service_names = utils.get_service_names()
-        service_names.sort()
+    default_service_names = utils.get_service_names()
+    default_service_names.sort()
+
+    def __init__(self, service_names: list = default_service_names, config: Config = DEFAULT_CONFIG):
         self.service_names = service_names
         self.config = config
         self.services = self._services()
@@ -128,3 +164,11 @@ def get_display_names_sorted_by_service(self, with_parameters: bool = False, wit
             if service_display_names:
                 display_names[service.service_name] = service_display_names
         return display_names
+
+    def get_display_names_by_service_with_parameters(self, has_defaults: bool = True) -> dict:
+        display_names = {}
+        for service in self.services:
+            service_display_names_with_params = service.get_display_names_by_service_with_parameters(has_defaults=has_defaults)
+            if service_display_names_with_params:
+                display_names[service.service_name] = service_display_names_with_params
+        return display_names
diff --git a/azure_guardrails/logic/terraform.py b/azure_guardrails/logic/terraform.py
index a59190b..a758a24 100644
--- a/azure_guardrails/logic/terraform.py
+++ b/azure_guardrails/logic/terraform.py
@@ -1,6 +1,12 @@
 import os
+import json
+import logging
 from jinja2 import Template, Environment, FileSystemLoader
 from azure_guardrails.shared import utils
+from azure_guardrails import set_log_level, set_stream_logger
+logger = logging.getLogger(__name__)
+
+set_stream_logger("jinja2", logging.DEBUG)
 
 
 def get_terraform_template(name: str, policy_names: dict, subscription_name: str = "",
@@ -24,3 +30,108 @@ def get_terraform_template(name: str, policy_names: dict, subscription_name: str
     env = Environment(loader=FileSystemLoader(template_path))  # nosec
     template = env.get_template("policy-set-with-builtins.tf")
     return template.render(t=template_contents)
+
+
+# https://github.com/terraform-providers/terraform-provider-azurerm/issues/8030#issuecomment-670289469
+def get_terraform_template_with_default_parameters(name: str, policy_names: dict, subscription_name: str = "",
+                                                   management_group: str = "", enforcement_mode: bool = False,
+                                                   module_source: str = utils.DEFAULT_TERRAFORM_MODULE_SOURCE) -> str:
+    if subscription_name == "" and management_group == "":
+        raise Exception("Please supply a value for the subscription name or the management group")
+    if enforcement_mode:
+        enforcement_string = "true"
+    else:
+        enforcement_string = "false"
+    template_contents = dict(
+        name=name,
+        policy_names=policy_names,
+        subscription_name=subscription_name,
+        management_group=management_group,
+        enforcement_mode=enforcement_string,
+        module_source=module_source
+    )
+    template_path = os.path.join(os.path.dirname(__file__), "parameters")
+    env = Environment(loader=FileSystemLoader(template_path))  # nosec
+    template = env.get_template("policy-set-with-parameters.tf")
+    return template.render(t=template_contents)
+
+
+class TerraformTemplate:
+    """Terraform Template with Parameters"""
+
+    def __init__(self, name: str,
+                 parameters: dict,
+                 subscription_name: str = "",
+                 management_group: str = "", enforcement_mode: bool = False,
+                 module_source: str = utils.DEFAULT_TERRAFORM_MODULE_SOURCE):
+        self.name = name
+        self.service_parameters = self._parameters(parameters)
+        self.module_source = module_source
+
+        if subscription_name == "" and management_group == "":
+            raise Exception("Please supply a value for the subscription name or the management group")
+        self.subscription_name = subscription_name
+        self.management_group = management_group
+        if enforcement_mode:
+            self.enforcement_string = "true"
+        else:
+            self.enforcement_string = "false"
+
+    def _parameters(self, parameters) -> dict:
+        """Separated this out just in case we need to do more processing"""
+        results = {}
+        for service_name, policy in parameters.items():
+            for display_name, parameters in policy.items():
+                # This will allow us to avoid duplicate parameters
+                parameter_details = {display_name: parameters}
+                if service_name in results:
+                    results[service_name].update(parameter_details)
+                else:
+                    results[service_name] = parameter_details
+                # if display_name not in results.get(service_name):
+        return results
+
+    def get_policy_parameters(self, service_name, policy_name):
+        return self.service_parameters.get(service_name).get(policy_name)
+
+    def policy_names(self) -> list:
+        result = []
+        for service, policies_with_params in self.service_parameters.items():
+            result.extend(list(policies_with_params.keys()))
+        return result
+
+    def all_parameters(self) -> dict:
+        result = {}
+        for service, policies_with_params in self.service_parameters.items():
+            for policy, parameters in policies_with_params.items():
+                for parameter in parameters:
+                    result[parameter] = parameters[parameter]
+        return result
+
+    def policy_definition_reference_parameters(self) -> dict:
+        result = {}
+        for service, policies_with_params in self.service_parameters.items():
+            for key in policies_with_params:
+                parameter_names = list(policies_with_params[key].keys())
+                result[key] = parameter_names
+        return result
+
+    def rendered(self) -> str:
+        all_parameters = json.dumps(self.all_parameters(), indent=4)
+        template_contents = dict(
+            name=self.name,
+            policy_definition_reference_parameters=self.policy_definition_reference_parameters(),
+            all_parameters=all_parameters,
+            sorted_policies=self.service_parameters,
+            subscription_name=self.subscription_name,
+            management_group=self.management_group,
+            enforcement_mode=self.enforcement_string,
+            module_source=self.module_source
+        )
+        template_path = os.path.join(os.path.dirname(__file__), "parameters")
+        env = Environment(loader=FileSystemLoader(template_path))  # nosec
+        env.filters['debug'] = print
+        template = env.get_template("policy-set-with-parameters.tf")
+        result = template.render(t=template_contents)
+        return result
+
diff --git a/azure_guardrails/shared/compliance_data.py b/azure_guardrails/shared/compliance_data.py
index 8823f31..a37c5ca 100644
--- a/azure_guardrails/shared/compliance_data.py
+++ b/azure_guardrails/shared/compliance_data.py
@@ -221,8 +221,7 @@ def markdown_table(self) -> str:
         results = []
         for policy_definition_name in self.matching_metadata:
             name = policy_definition_name.replace("[Preview]: ", "")
-            benchmark_strings = []
-            benchmark_strings.append("<ul>")
+            benchmark_strings = ["<ul>"]
             for benchmark in self.matching_metadata[policy_definition_name]:
                 service_name = self.policy_compliance_data.policy_definition_metadata[name][benchmark].service_name
                 github_link = self.policy_compliance_data.policy_definition_metadata[name][benchmark].github_link
diff --git a/azure_guardrails/shared/config.py b/azure_guardrails/shared/config.py
index f2964e6..a869ff4 100644
--- a/azure_guardrails/shared/config.py
+++ b/azure_guardrails/shared/config.py
@@ -7,7 +7,6 @@
 
 logger = logging.getLogger(__name__)
 
-
 DEFAULT_CONFIG_FILE = os.path.abspath(os.path.join(
     os.path.dirname(__file__),
     "default-config.yml"
@@ -139,11 +138,14 @@ def get_default_config() -> Config:
     exclude_policies = config_cfg.get("exclude_policies", None)
     exclude_services = config_cfg.get("exclude_services", None)
     match_only_keywords = config_cfg.get("match_only_keywords", None)
-    config = Config(exclude_policies=exclude_policies, exclude_services=exclude_services, match_only_keywords=match_only_keywords)
+    config = Config(exclude_policies=exclude_policies, exclude_services=exclude_services,
+                    match_only_keywords=match_only_keywords)
     return config
 
+
 DEFAULT_CONFIG = get_default_config()
 
+
 def get_config_from_file(config_file: str) -> Config:
     with open(config_file, "r") as yaml_file:
         try:
@@ -153,5 +155,6 @@ def get_config_from_file(config_file: str) -> Config:
     exclude_policies = config_cfg.get("exclude_policies", None)
     exclude_services = config_cfg.get("exclude_services", None)
     match_only_keywords = config_cfg.get("match_only_keywords", None)
-    config = Config(exclude_policies=exclude_policies, exclude_services=exclude_services, match_only_keywords=match_only_keywords)
+    config = Config(exclude_policies=exclude_policies, exclude_services=exclude_services,
+                    match_only_keywords=match_only_keywords)
     return config
diff --git a/azure_guardrails/shared/terraform/policy-initiative-with-parameters/data.tf b/azure_guardrails/shared/terraform/policy-initiative-with-parameters/data.tf
new file mode 100644
index 0000000..7bcee4b
--- /dev/null
+++ b/azure_guardrails/shared/terraform/policy-initiative-with-parameters/data.tf
@@ -0,0 +1,30 @@
+data "azurerm_policy_definition" "allowed_host_paths" {
+  display_name = "Kubernetes cluster pod hostPath volumes should only use allowed host paths"
+}
+
+variable "category" {}
+variable "name" {}
+variable "management_group" {}
+
+resource "azurerm_policy_set_definition" "guardrails" {
+  name                  = var.name
+  policy_type           = "Custom"
+  display_name          = var.name
+  description           = var.name
+  management_group_name = var.management_group == "" ? null : var.management_group
+//  parameters = local.parameters
+  parameters = data.azurerm_policy_definition.allowed_host_paths.parameters
+  policy_definition_reference    = tostring(jsonencode(local.policy_definitions))
+  metadata = tostring(jsonencode({
+    category = var.category
+  }))
+}
+
+
+locals {
+    parameters  = <<PARAMETERS
+  {
+        "category": "Tenant Level Policy"
+  }
+  PARAMETERS
+}
\ No newline at end of file
diff --git a/azure_guardrails/shared/terraform/policy-initiative-with-parameters/example/main.tf b/azure_guardrails/shared/terraform/policy-initiative-with-parameters/example/main.tf
new file mode 100644
index 0000000..b9b2d1e
--- /dev/null
+++ b/azure_guardrails/shared/terraform/policy-initiative-with-parameters/example/main.tf
@@ -0,0 +1,68 @@
+variable "name" { default = "example" }
+variable "subscription_name" { default = "" }
+variable "management_group" { default = "" }
+variable "enforcement_mode" { default = false }
+
+variable "category" {
+  type    = string
+  default = "Testing"
+}
+provider "azurerm" {
+  features {}
+}
+
+locals {
+  policy_names = [
+    "Do not allow privileged containers in Kubernetes cluster",
+    "Enforce internal load balancers in Kubernetes cluster"
+  ]
+  policy_definition_map = zipmap(
+    data.azurerm_policy_definition.definition_lookups.*.display_name,
+    data.azurerm_policy_definition.definition_lookups.*.id
+  )
+}
+
+data "azurerm_policy_definition" "definition_lookups" {
+  count = length(local.policy_names)
+  display_name = local.policy_names[count.index]
+}
+
+resource "azurerm_policy_set_definition" "guardrails" {
+  name                  = var.name
+  policy_type           = "Custom"
+  display_name          = var.name
+  description           = var.name
+  management_group_name = var.management_group == "" ? null : var.management_group
+  metadata = tostring(jsonencode({
+    category = var.category
+  }))
+  parameters = <<PARAMETERS
+{
+"excludedNamespaces": {
+        "type": "Array",
+        "metadata": {
+          "displayName": "Namespace exclusions",
+          "description": "List of Kubernetes namespaces to exclude from policy evaluation."
+        },
+        "defaultValue": ["kube-system", "gatekeeper-system", "azure-arc"]
+      },
+      "namespaces": {
+        "type": "Array",
+        "metadata": {
+          "displayName": "Namespace inclusions",
+          "description": "List of Kubernetes namespaces to only include in policy evaluation. An empty list means the policy is applied to all resources in all namespaces."
+        },
+        "defaultValue": []
+      }
+}
+PARAMETERS
+  policy_definition_reference {
+    policy_definition_id = lookup(local.policy_definition_map, "Do not allow privileged containers in Kubernetes cluster")
+    parameter_values = jsonencode({
+      excludedNamespaces = { "value" : "[parameters('excludedNamespaces')]" }
+      namespaces         = { "value" : "[parameters('namespaces')]" }
+    })
+    reference_id = null
+  }
+
+}
diff --git a/examples/terraform-demo-with-parameters/main.tf b/examples/terraform-demo-with-parameters/main.tf
new file mode 100644
index 0000000..bb84783
--- /dev/null
+++ b/examples/terraform-demo-with-parameters/main.tf
@@ -0,0 +1,451 @@
+variable "name" { default = "example" }
+variable "subscription_name" { default = "example" }
+variable "management_group" { default = "" }
+variable "enforcement_mode" { default = false }
+
+variable "category" {
+  type    = string
+  default = "Testing"
+}
+provider "azurerm" {
+  features {}
+}
+
+locals {
+  policy_names = [
+    # -----------------------------------------------------------------------------------------------------------------
+    # Kubernetes
+    # -----------------------------------------------------------------------------------------------------------------
+    "Do not allow privileged containers in Kubernetes cluster",
+    "Enforce internal load balancers in Kubernetes cluster",
+    "Kubernetes cluster containers should not share host process ID or host IPC namespace",
+    "Kubernetes cluster containers should only use allowed AppArmor profiles",
+    "Kubernetes cluster containers should only use allowed ProcMountType",
+    "Kubernetes cluster containers should only use allowed capabilities",
+    "Kubernetes cluster containers should only use allowed seccomp profiles",
+    "Kubernetes cluster containers should run with a read only root file system",
+    "Kubernetes cluster pod FlexVolume volumes should only use allowed drivers",
+    "Kubernetes cluster pod hostPath volumes should only use allowed host paths",
+    "Kubernetes cluster pods and containers should only run with approved user and group IDs",
+    "Kubernetes cluster pods and containers should only use allowed SELinux options",
+    "Kubernetes cluster pods should only use allowed volume types",
+    "Kubernetes clusters should be accessible only over HTTPS",
+    "Kubernetes clusters should not allow container privilege escalation",
+    "[Preview]: Kubernetes cluster services should only use allowed external IPs",
+    "[Preview]: Kubernetes clusters should disable automounting API credentials",
+    "[Preview]: Kubernetes clusters should not grant CAP_SYS_ADMIN security capabilities",
+    "[Preview]: Kubernetes clusters should not use specific security capabilities",
+    "[Preview]: Kubernetes clusters should not use the default namespace",
+  ]
+  policy_definition_map = zipmap(
+    data.azurerm_policy_definition.definition_lookups.*.display_name,
+    data.azurerm_policy_definition.definition_lookups.*.id
+  )
+}
+
+data "azurerm_policy_definition" "definition_lookups" {
+  count        = length(local.policy_names)
+  display_name = local.policy_names[count.index]
+}
+
+resource "azurerm_policy_set_definition" "guardrails" {
+  name                  = var.name
+  policy_type           = "Custom"
+  display_name          = var.name
+  description           = var.name
+  management_group_name = var.management_group == "" ? null : var.management_group
+  metadata = tostring(jsonencode({
+    category = var.category
+  }))
+
+
+  policy_definition_reference {
+    policy_definition_id = lookup(local.policy_definition_map, "Do not allow privileged containers in Kubernetes cluster")
+    parameter_values = jsonencode({
+      excludedNamespaces = { "value" : "[parameters('excludedNamespaces')]" }
+      namespaces = { "value" : "[parameters('namespaces')]" }
+    })
+    reference_id = null
+  }
+
+  policy_definition_reference {
+    policy_definition_id = lookup(local.policy_definition_map, "Enforce internal load balancers in Kubernetes cluster")
+    parameter_values = jsonencode({
+      excludedNamespaces = { "value" : "[parameters('excludedNamespaces')]" }
+      namespaces = { "value" : "[parameters('namespaces')]" }
+    })
+    reference_id = null
+  }
+
+  policy_definition_reference {
+    policy_definition_id = lookup(local.policy_definition_map, "Kubernetes cluster containers should not share host process ID or host IPC namespace")
+    parameter_values = jsonencode({
+      excludedNamespaces = { "value" : "[parameters('excludedNamespaces')]" }
+      namespaces = { "value" : "[parameters('namespaces')]" }
+    })
+    reference_id = null
+  }
+
+  policy_definition_reference {
+    policy_definition_id = lookup(local.policy_definition_map, "Kubernetes cluster containers should only use allowed AppArmor profiles")
+    parameter_values = jsonencode({
+      excludedNamespaces = { "value" : "[parameters('excludedNamespaces')]" }
+      namespaces = { "value" : "[parameters('namespaces')]" }
+      allowedProfiles = { "value" : "[parameters('allowedProfiles')]" }
+    })
+    reference_id = null
+  }
+
+  policy_definition_reference {
+    policy_definition_id = lookup(local.policy_definition_map, "Kubernetes cluster containers should only use allowed ProcMountType")
+    parameter_values = jsonencode({
+      excludedNamespaces = { "value" : "[parameters('excludedNamespaces')]" }
+      namespaces = { "value" : "[parameters('namespaces')]" }
+      procMountType = { "value" : "[parameters('procMountType')]" }
+    })
+    reference_id = null
+  }
+
+  policy_definition_reference {
+    policy_definition_id = lookup(local.policy_definition_map, "Kubernetes cluster containers should only use allowed capabilities")
+    parameter_values = jsonencode({
+      excludedNamespaces = { "value" : "[parameters('excludedNamespaces')]" }
+      namespaces = { "value" : "[parameters('namespaces')]" }
+      allowedCapabilities = { "value" : "[parameters('allowedCapabilities')]" }
+      requiredDropCapabilities = { "value" : "[parameters('requiredDropCapabilities')]" }
+    })
+    reference_id = null
+  }
+
+  policy_definition_reference {
+    policy_definition_id = lookup(local.policy_definition_map, "Kubernetes cluster containers should only use allowed seccomp profiles")
+    parameter_values = jsonencode({
+      excludedNamespaces = { "value" : "[parameters('excludedNamespaces')]" }
+      namespaces = { "value" : "[parameters('namespaces')]" }
+      allowedProfiles = { "value" : "[parameters('allowedProfiles')]" }
+    })
+    reference_id = null
+  }
+
+  policy_definition_reference {
+    policy_definition_id = lookup(local.policy_definition_map, "Kubernetes cluster containers should run with a read only root file system")
+    parameter_values = jsonencode({
+      excludedNamespaces = { "value" : "[parameters('excludedNamespaces')]" }
+      namespaces = { "value" : "[parameters('namespaces')]" }
+    })
+    reference_id = null
+  }
+
+  policy_definition_reference {
+    policy_definition_id = lookup(local.policy_definition_map, "Kubernetes cluster pod FlexVolume volumes should only use allowed drivers")
+    parameter_values = jsonencode({
+      excludedNamespaces = { "value" : "[parameters('excludedNamespaces')]" }
+      namespaces = { "value" : "[parameters('namespaces')]" }
+      allowedFlexVolumeDrivers = { "value" : "[parameters('allowedFlexVolumeDrivers')]" }
+    })
+    reference_id = null
+  }
+
+  policy_definition_reference {
+    policy_definition_id = lookup(local.policy_definition_map, "Kubernetes cluster pod hostPath volumes should only use allowed host paths")
+    parameter_values = jsonencode({
+      excludedNamespaces = { "value" : "[parameters('excludedNamespaces')]" }
+      namespaces = { "value" : "[parameters('namespaces')]" }
+      allowedHostPaths = { "value" : "[parameters('allowedHostPaths')]" }
+    })
+    reference_id = null
+  }
+
+  policy_definition_reference {
+    policy_definition_id = lookup(local.policy_definition_map, "Kubernetes cluster pods and containers should only run with approved user and group IDs")
+    parameter_values = jsonencode({
+      excludedNamespaces = { "value" : "[parameters('excludedNamespaces')]" }
+      namespaces = { "value" : "[parameters('namespaces')]" }
+      runAsUserRule = { "value" : "[parameters('runAsUserRule')]" }
+      runAsUserRanges = { "value" : "[parameters('runAsUserRanges')]" }
+      runAsGroupRule = { "value" : "[parameters('runAsGroupRule')]" }
+      runAsGroupRanges = { "value" : "[parameters('runAsGroupRanges')]" }
+      supplementalGroupsRule = { "value" : "[parameters('supplementalGroupsRule')]" }
+      supplementalGroupsRanges = { "value" : "[parameters('supplementalGroupsRanges')]" }
+      fsGroupRule = { "value" : "[parameters('fsGroupRule')]" }
+      fsGroupRanges = { "value" : "[parameters('fsGroupRanges')]" }
+    })
+    reference_id = null
+  }
+
+  policy_definition_reference {
+    policy_definition_id = lookup(local.policy_definition_map, "Kubernetes cluster pods and containers should only use allowed SELinux options")
+    parameter_values = jsonencode({
+      excludedNamespaces = { "value" : "[parameters('excludedNamespaces')]" }
+      namespaces = { "value" : "[parameters('namespaces')]" }
+      allowedSELinuxOptions = { "value" : "[parameters('allowedSELinuxOptions')]" }
+    })
+    reference_id = null
+  }
+
+  policy_definition_reference {
+    policy_definition_id = lookup(local.policy_definition_map, "Kubernetes cluster pods should only use allowed volume types")
+    parameter_values = jsonencode({
+      excludedNamespaces = { "value" : "[parameters('excludedNamespaces')]" }
+      namespaces = { "value" : "[parameters('namespaces')]" }
+      allowedVolumeTypes = { "value" : "[parameters('allowedVolumeTypes')]" }
+    })
+    reference_id = null
+  }
+
+  policy_definition_reference {
+    policy_definition_id = lookup(local.policy_definition_map, "Kubernetes clusters should be accessible only over HTTPS")
+    parameter_values = jsonencode({
+      excludedNamespaces = { "value" : "[parameters('excludedNamespaces')]" }
+      namespaces = { "value" : "[parameters('namespaces')]" }
+    })
+    reference_id = null
+  }
+
+  policy_definition_reference {
+    policy_definition_id = lookup(local.policy_definition_map, "Kubernetes clusters should not allow container privilege escalation")
+    parameter_values = jsonencode({
+      excludedNamespaces = { "value" : "[parameters('excludedNamespaces')]" }
+      namespaces = { "value" : "[parameters('namespaces')]" }
+    })
+    reference_id = null
+  }
+
+  policy_definition_reference {
+    policy_definition_id = lookup(local.policy_definition_map, "[Preview]: Kubernetes cluster services should only use allowed external IPs")
+    parameter_values = jsonencode({
+      excludedNamespaces = { "value" : "[parameters('excludedNamespaces')]" }
+      namespaces = { "value" : "[parameters('namespaces')]" }
+      allowedExternalIPs = { "value" : "[parameters('allowedExternalIPs')]" }
+    })
+    reference_id = null
+  }
+
+  policy_definition_reference {
+    policy_definition_id = lookup(local.policy_definition_map, "[Preview]: Kubernetes clusters should disable automounting API credentials")
+    parameter_values = jsonencode({
+      excludedNamespaces = { "value" : "[parameters('excludedNamespaces')]" }
+      namespaces = { "value" : "[parameters('namespaces')]" }
+    })
+    reference_id = null
+  }
+
+  policy_definition_reference {
+    policy_definition_id = lookup(local.policy_definition_map, "[Preview]: Kubernetes clusters should not grant CAP_SYS_ADMIN security capabilities")
+    parameter_values = jsonencode({
+      excludedNamespaces = { "value" : "[parameters('excludedNamespaces')]" }
+      namespaces = { "value" : "[parameters('namespaces')]" }
+    })
+    reference_id = null
+  }
+
+  policy_definition_reference {
+    policy_definition_id = lookup(local.policy_definition_map, "[Preview]: Kubernetes clusters should not use specific security capabilities")
+    parameter_values = jsonencode({
+      excludedNamespaces = { "value" : "[parameters('excludedNamespaces')]" }
+      namespaces = { "value" : "[parameters('namespaces')]" }
+      disallowedCapabilities = { "value" : "[parameters('disallowedCapabilities')]" }
+    })
+    reference_id = null
+  }
+
+  policy_definition_reference {
+    policy_definition_id = lookup(local.policy_definition_map, "[Preview]: Kubernetes clusters should not use the default namespace")
+    parameter_values = jsonencode({
+      excludedNamespaces = { "value" : "[parameters('excludedNamespaces')]" }
+      namespaces = { "value" : "[parameters('namespaces')]" }
+    })
+    reference_id = null
+  }
+
+
+  parameters = <<PARAMETERS
+{
+    "excludedNamespaces": {
+        "name": "excludedNamespaces",
+        "type": "Array",
+        "description": "List of Kubernetes namespaces to exclude from policy evaluation.",
+        "display_name": "Namespace exclusions",
+        "default_value": [
+            "kube-system",
+            "gatekeeper-system",
+            "azure-arc"
+        ]
+    },
+    "namespaces": {
+        "name": "namespaces",
+        "type": "Array",
+        "description": "List of Kubernetes namespaces to only include in policy evaluation. An empty list means the policy is applied to all resources in all namespaces.",
+        "display_name": "Namespace inclusions",
+        "default_value": []
+    },
+    "allowedProfiles": {
+        "name": "allowedProfiles",
+        "type": "Array",
+        "description": "The list of seccomp profiles that containers are allowed to use. E.g. 'runtime/default;docker/default'. Provide empty list as input to block everything.",
+        "display_name": "Allowed seccomp profiles",
+        "default_value": []
+    },
+    "procMountType": {
+        "name": "procMountType",
+        "type": "String",
+        "description": "The ProcMountType that containers are allowed to use in the cluster.",
+        "display_name": "ProcMountType",
+        "default_value": "Default",
+        "allowed_values": [
+            "Unmasked",
+            "Default"
+        ]
+    },
+    "allowedCapabilities": {
+        "name": "allowedCapabilities",
+        "type": "Array",
+        "description": "The list of capabilities that are allowed to be added to a container. Provide empty list as input to block everything.",
+        "display_name": "Allowed capabilities",
+        "default_value": []
+    },
+    "requiredDropCapabilities": {
+        "name": "requiredDropCapabilities",
+        "type": "Array",
+        "description": "The list of capabilities that must be dropped by a container.",
+        "display_name": "Required drop capabilities",
+        "default_value": []
+    },
+    "allowedFlexVolumeDrivers": {
+        "name": "allowedFlexVolumeDrivers",
+        "type": "Array",
+        "description": "The list of drivers that FlexVolume volumes are allowed to use. Provide empty list as input to block everything.",
+        "display_name": "Allowed FlexVolume drivers",
+        "default_value": []
+    },
+    "allowedHostPaths": {
+        "name": "allowedHostPaths",
+        "type": "Object",
+        "description": "The host paths allowed for pod hostPath volumes to use. Provide an empty paths list to block all host paths.",
+        "display_name": "Allowed host paths",
+        "default_value": {
+            "paths": []
+        }
+    },
+    "runAsUserRule": {
+        "name": "runAsUserRule",
+        "type": "String",
+        "description": "The 'RunAsUser' rule that containers are allowed to run with.",
+        "display_name": "Run as user rule",
+        "default_value": "MustRunAsNonRoot",
+        "allowed_values": [
+            "MustRunAs",
+            "MustRunAsNonRoot",
+            "RunAsAny"
+        ]
+    },
+    "runAsUserRanges": {
+        "name": "runAsUserRanges",
+        "type": "Object",
+        "description": "The user ID ranges that are allowed for containers to use.",
+        "display_name": "Allowed user ID ranges",
+        "default_value": {
+            "ranges": []
+        }
+    },
+    "runAsGroupRule": {
+        "name": "runAsGroupRule",
+        "type": "String",
+        "description": "The 'RunAsGroup' rule that containers are allowed to run with.",
+        "display_name": "Run as group rule",
+        "default_value": "RunAsAny",
+        "allowed_values": [
+            "MustRunAs",
+            "MayRunAs",
+            "RunAsAny"
+        ]
+    },
+    "runAsGroupRanges": {
+        "name": "runAsGroupRanges",
+        "type": "Object",
+        "description": "The group ID ranges that are allowed for containers to use.",
+        "display_name": "Allowed group ID ranges",
+        "default_value": {
+            "ranges": []
+        }
+    },
+    "supplementalGroupsRule": {
+        "name": "supplementalGroupsRule",
+        "type": "String",
+        "description": "The 'SupplementalGroups' rule that containers are allowed to run with.",
+        "display_name": "Supplemental group rule",
+        "default_value": "RunAsAny",
+        "allowed_values": [
+            "MustRunAs",
+            "MayRunAs",
+            "RunAsAny"
+        ]
+    },
+    "supplementalGroupsRanges": {
+        "name": "supplementalGroupsRanges",
+        "type": "Object",
+        "description": "The supplemental group ID ranges that are allowed for containers to use.",
+        "display_name": "Allowed supplemental group ID ranges",
+        "default_value": {
+            "ranges": []
+        }
+    },
+    "fsGroupRule": {
+        "name": "fsGroupRule",
+        "type": "String",
+        "description": "The 'FSGroup' rule that containers are allowed to run with.",
+        "display_name": "File system group rule",
+        "default_value": "RunAsAny",
+        "allowed_values": [
+            "MustRunAs",
+            "MayRunAs",
+            "RunAsAny"
+        ]
+    },
+    "fsGroupRanges": {
+        "name": "fsGroupRanges",
+        "type": "Object",
+        "description": "The file system group ranges that are allowed for pods to use.",
+        "display_name": "Allowed file system group ID ranges",
+        "default_value": {
+            "ranges": []
+        }
+    },
+    "allowedSELinuxOptions": {
+        "name": "allowedSELinuxOptions",
+        "type": "Object",
+        "description": "The allowed configurations for pod and container level SELinux Options. Provide empty options list as input to block everything.",
+        "display_name": "Allowed SELinux options",
+        "default_value": {
+            "options": []
+        }
+    },
+    "allowedVolumeTypes": {
+        "name": "allowedVolumeTypes",
+        "type": "Array",
+        "description": "The list of volume types that can be used by a pod. Provide empty list as input to block everything.",
+        "display_name": "Allowed volume types",
+        "default_value": []
+    },
+    "allowedExternalIPs": {
+        "name": "allowedExternalIPs",
+        "type": "Array",
+        "description": "List of External IPs that services are allowed to use. Empty array means all external IPs are disallowed.",
+        "display_name": "Allowed External IPs",
+        "default_value": []
+    },
+    "disallowedCapabilities": {
+        "name": "disallowedCapabilities",
+        "type": "Array",
+        "description": "List of capabilities that containers are not able to use",
+        "display_name": "Blocked capabilities",
+        "default_value": []
+    }
+}
+PARAMETERS
+}
+
+
+output "id" {
+  value = azurerm_policy_set_definition.guardrails.id
+}
\ No newline at end of file
diff --git a/requirements.txt b/requirements.txt
index 859b894..2dcf792 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -5,4 +5,5 @@ colorama==0.4.4
 tabulate==0.8.9
 # Scrapers
 beautifulsoup4==4.9.3
-requests==2.25.1
\ No newline at end of file
+requests==2.25.1
+ruamel.yaml
\ No newline at end of file


Test patch:
diff --git a/test/command/test_generate_terraform.py b/test/command/test_generate_terraform.py
index 3cac007..8aeac62 100644
--- a/test/command/test_generate_terraform.py
+++ b/test/command/test_generate_terraform.py
@@ -28,7 +28,7 @@ def test_generate_terraform_command_with_config(self):
         """command.generate_terraform: with config file"""
         result = self.runner.invoke(generate_terraform, ["--service", "all", "--config-file", default_config_file])
         self.assertTrue(result.exit_code == 0)
-        print(result.output)
+        # print(result.output)
 
     def test_generate_terraform_with_explicit_matches(self):
         """command.generate_terraform: with config file that matches keywords"""
diff --git a/test/command/test_generate_terraform_with_csv_summary.py b/test/command/test_generate_terraform_with_csv_summary.py
index de8bcd2..dd4ae4d 100644
--- a/test/command/test_generate_terraform_with_csv_summary.py
+++ b/test/command/test_generate_terraform_with_csv_summary.py
@@ -17,4 +17,4 @@ def test_csv_summary(self):
         # policy_names = service.get_display_names_sorted_by_service(with_parameters=with_parameters)
 
         results = get_compliance_table()
-        print(json.dumps(results, indent=4))
+        # print(json.dumps(results, indent=4))
diff --git a/test/files/kubernetes_parameters.yml b/test/files/kubernetes_parameters.yml
new file mode 100644
index 0000000..b4928fa
--- /dev/null
+++ b/test/files/kubernetes_parameters.yml
@@ -0,0 +1,493 @@
+Do not allow privileged containers in Kubernetes cluster:
+  excludedNamespaces:
+    name: excludedNamespaces
+    type: Array
+    description: List of Kubernetes namespaces to exclude from policy evaluation.
+    display_name: Namespace exclusions
+    default_value:
+    - kube-system
+    - gatekeeper-system
+    - azure-arc
+  namespaces:
+    name: namespaces
+    type: Array
+    description: List of Kubernetes namespaces to only include in policy evaluation.
+      An empty list means the policy is applied to all resources in all namespaces.
+    display_name: Namespace inclusions
+    default_value: []
+Enforce internal load balancers in Kubernetes cluster:
+  excludedNamespaces:
+    name: excludedNamespaces
+    type: Array
+    description: List of Kubernetes namespaces to exclude from policy evaluation.
+    display_name: Namespace exclusions
+    default_value:
+    - kube-system
+    - gatekeeper-system
+    - azure-arc
+  namespaces:
+    name: namespaces
+    type: Array
+    description: List of Kubernetes namespaces to only include in policy evaluation.
+      An empty list means the policy is applied to all resources in all namespaces.
+    display_name: Namespace inclusions
+    default_value: []
+Kubernetes cluster containers should not share host process ID or host IPC namespace:
+  excludedNamespaces:
+    name: excludedNamespaces
+    type: Array
+    description: List of Kubernetes namespaces to exclude from policy evaluation.
+    display_name: Namespace exclusions
+    default_value:
+    - kube-system
+    - gatekeeper-system
+    - azure-arc
+  namespaces:
+    name: namespaces
+    type: Array
+    description: List of Kubernetes namespaces to only include in policy evaluation.
+      An empty list means the policy is applied to all resources in all namespaces.
+    display_name: Namespace inclusions
+    default_value: []
+Kubernetes cluster containers should only use allowed AppArmor profiles:
+  excludedNamespaces:
+    name: excludedNamespaces
+    type: Array
+    description: List of Kubernetes namespaces to exclude from policy evaluation.
+    display_name: Namespace exclusions
+    default_value:
+    - kube-system
+    - gatekeeper-system
+    - azure-arc
+  namespaces:
+    name: namespaces
+    type: Array
+    description: List of Kubernetes namespaces to only include in policy evaluation.
+      An empty list means the policy is applied to all resources in all namespaces.
+    display_name: Namespace inclusions
+    default_value: []
+  allowedProfiles:
+    name: allowedProfiles
+    type: Array
+    description: The list of AppArmor profiles that containers are allowed to use.
+      E.g. 'runtime/default;docker/default'. Provide empty list as input to block
+      everything.
+    display_name: Allowed AppArmor profiles
+    default_value: []
+Kubernetes cluster containers should only use allowed ProcMountType:
+  excludedNamespaces:
+    name: excludedNamespaces
+    type: Array
+    description: List of Kubernetes namespaces to exclude from policy evaluation.
+    display_name: Namespace exclusions
+    default_value:
+    - kube-system
+    - gatekeeper-system
+    - azure-arc
+  namespaces:
+    name: namespaces
+    type: Array
+    description: List of Kubernetes namespaces to only include in policy evaluation.
+      An empty list means the policy is applied to all resources in all namespaces.
+    display_name: Namespace inclusions
+    default_value: []
+  procMountType:
+    name: procMountType
+    type: String
+    description: The ProcMountType that containers are allowed to use in the cluster.
+    display_name: ProcMountType
+    default_value: Default
+    allowed_values:
+    - Unmasked
+    - Default
+Kubernetes cluster containers should only use allowed capabilities:
+  excludedNamespaces:
+    name: excludedNamespaces
+    type: Array
+    description: List of Kubernetes namespaces to exclude from policy evaluation.
+    display_name: Namespace exclusions
+    default_value:
+    - kube-system
+    - gatekeeper-system
+    - azure-arc
+  namespaces:
+    name: namespaces
+    type: Array
+    description: List of Kubernetes namespaces to only include in policy evaluation.
+      An empty list means the policy is applied to all resources in all namespaces.
+    display_name: Namespace inclusions
+    default_value: []
+  allowedCapabilities:
+    name: allowedCapabilities
+    type: Array
+    description: The list of capabilities that are allowed to be added to a container.
+      Provide empty list as input to block everything.
+    display_name: Allowed capabilities
+    default_value: []
+  requiredDropCapabilities:
+    name: requiredDropCapabilities
+    type: Array
+    description: The list of capabilities that must be dropped by a container.
+    display_name: Required drop capabilities
+    default_value: []
+Kubernetes cluster containers should only use allowed seccomp profiles:
+  excludedNamespaces:
+    name: excludedNamespaces
+    type: Array
+    description: List of Kubernetes namespaces to exclude from policy evaluation.
+    display_name: Namespace exclusions
+    default_value:
+    - kube-system
+    - gatekeeper-system
+    - azure-arc
+  namespaces:
+    name: namespaces
+    type: Array
+    description: List of Kubernetes namespaces to only include in policy evaluation.
+      An empty list means the policy is applied to all resources in all namespaces.
+    display_name: Namespace inclusions
+    default_value: []
+  allowedProfiles:
+    name: allowedProfiles
+    type: Array
+    description: The list of seccomp profiles that containers are allowed to use.
+      E.g. 'runtime/default;docker/default'. Provide empty list as input to block
+      everything.
+    display_name: Allowed seccomp profiles
+    default_value: []
+Kubernetes cluster containers should run with a read only root file system:
+  excludedNamespaces:
+    name: excludedNamespaces
+    type: Array
+    description: List of Kubernetes namespaces to exclude from policy evaluation.
+    display_name: Namespace exclusions
+    default_value:
+    - kube-system
+    - gatekeeper-system
+    - azure-arc
+  namespaces:
+    name: namespaces
+    type: Array
+    description: List of Kubernetes namespaces to only include in policy evaluation.
+      An empty list means the policy is applied to all resources in all namespaces.
+    display_name: Namespace inclusions
+    default_value: []
+Kubernetes cluster pod FlexVolume volumes should only use allowed drivers:
+  excludedNamespaces:
+    name: excludedNamespaces
+    type: Array
+    description: List of Kubernetes namespaces to exclude from policy evaluation.
+    display_name: Namespace exclusions
+    default_value:
+    - kube-system
+    - gatekeeper-system
+    - azure-arc
+  namespaces:
+    name: namespaces
+    type: Array
+    description: List of Kubernetes namespaces to only include in policy evaluation.
+      An empty list means the policy is applied to all resources in all namespaces.
+    display_name: Namespace inclusions
+    default_value: []
+  allowedFlexVolumeDrivers:
+    name: allowedFlexVolumeDrivers
+    type: Array
+    description: The list of drivers that FlexVolume volumes are allowed to use. Provide
+      empty list as input to block everything.
+    display_name: Allowed FlexVolume drivers
+    default_value: []
+Kubernetes cluster pod hostPath volumes should only use allowed host paths:
+  excludedNamespaces:
+    name: excludedNamespaces
+    type: Array
+    description: List of Kubernetes namespaces to exclude from policy evaluation.
+    display_name: Namespace exclusions
+    default_value:
+    - kube-system
+    - gatekeeper-system
+    - azure-arc
+  namespaces:
+    name: namespaces
+    type: Array
+    description: List of Kubernetes namespaces to only include in policy evaluation.
+      An empty list means the policy is applied to all resources in all namespaces.
+    display_name: Namespace inclusions
+    default_value: []
+  allowedHostPaths:
+    name: allowedHostPaths
+    type: Object
+    description: The host paths allowed for pod hostPath volumes to use. Provide an
+      empty paths list to block all host paths.
+    display_name: Allowed host paths
+    default_value:
+      paths: []
+Kubernetes cluster pods and containers should only run with approved user and group IDs:
+  excludedNamespaces:
+    name: excludedNamespaces
+    type: Array
+    description: List of Kubernetes namespaces to exclude from policy evaluation.
+    display_name: Namespace exclusions
+    default_value:
+    - kube-system
+    - gatekeeper-system
+    - azure-arc
+  namespaces:
+    name: namespaces
+    type: Array
+    description: List of Kubernetes namespaces to only include in policy evaluation.
+      An empty list means the policy is applied to all resources in all namespaces.
+    display_name: Namespace inclusions
+    default_value: []
+  runAsUserRule:
+    name: runAsUserRule
+    type: String
+    description: The 'RunAsUser' rule that containers are allowed to run with.
+    display_name: Run as user rule
+    default_value: MustRunAsNonRoot
+    allowed_values:
+    - MustRunAs
+    - MustRunAsNonRoot
+    - RunAsAny
+  runAsUserRanges:
+    name: runAsUserRanges
+    type: Object
+    description: The user ID ranges that are allowed for containers to use.
+    display_name: Allowed user ID ranges
+    default_value:
+      ranges: []
+  runAsGroupRule:
+    name: runAsGroupRule
+    type: String
+    description: The 'RunAsGroup' rule that containers are allowed to run with.
+    display_name: Run as group rule
+    default_value: RunAsAny
+    allowed_values:
+    - MustRunAs
+    - MayRunAs
+    - RunAsAny
+  runAsGroupRanges:
+    name: runAsGroupRanges
+    type: Object
+    description: The group ID ranges that are allowed for containers to use.
+    display_name: Allowed group ID ranges
+    default_value:
+      ranges: []
+  supplementalGroupsRule:
+    name: supplementalGroupsRule
+    type: String
+    description: The 'SupplementalGroups' rule that containers are allowed to run
+      with.
+    display_name: Supplemental group rule
+    default_value: RunAsAny
+    allowed_values:
+    - MustRunAs
+    - MayRunAs
+    - RunAsAny
+  supplementalGroupsRanges:
+    name: supplementalGroupsRanges
+    type: Object
+    description: The supplemental group ID ranges that are allowed for containers
+      to use.
+    display_name: Allowed supplemental group ID ranges
+    default_value:
+      ranges: []
+  fsGroupRule:
+    name: fsGroupRule
+    type: String
+    description: The 'FSGroup' rule that containers are allowed to run with.
+    display_name: File system group rule
+    default_value: RunAsAny
+    allowed_values:
+    - MustRunAs
+    - MayRunAs
+    - RunAsAny
+  fsGroupRanges:
+    name: fsGroupRanges
+    type: Object
+    description: The file system group ranges that are allowed for pods to use.
+    display_name: Allowed file system group ID ranges
+    default_value:
+      ranges: []
+Kubernetes cluster pods and containers should only use allowed SELinux options:
+  excludedNamespaces:
+    name: excludedNamespaces
+    type: Array
+    description: List of Kubernetes namespaces to exclude from policy evaluation.
+    display_name: Namespace exclusions
+    default_value:
+    - kube-system
+    - gatekeeper-system
+    - azure-arc
+  namespaces:
+    name: namespaces
+    type: Array
+    description: List of Kubernetes namespaces to only include in policy evaluation.
+      An empty list means the policy is applied to all resources in all namespaces.
+    display_name: Namespace inclusions
+    default_value: []
+  allowedSELinuxOptions:
+    name: allowedSELinuxOptions
+    type: Object
+    description: The allowed configurations for pod and container level SELinux Options.
+      Provide empty options list as input to block everything.
+    display_name: Allowed SELinux options
+    default_value:
+      options: []
+Kubernetes cluster pods should only use allowed volume types:
+  excludedNamespaces:
+    name: excludedNamespaces
+    type: Array
+    description: List of Kubernetes namespaces to exclude from policy evaluation.
+    display_name: Namespace exclusions
+    default_value:
+    - kube-system
+    - gatekeeper-system
+    - azure-arc
+  namespaces:
+    name: namespaces
+    type: Array
+    description: List of Kubernetes namespaces to only include in policy evaluation.
+      An empty list means the policy is applied to all resources in all namespaces.
+    display_name: Namespace inclusions
+    default_value: []
+  allowedVolumeTypes:
+    name: allowedVolumeTypes
+    type: Array
+    description: The list of volume types that can be used by a pod. Provide empty
+      list as input to block everything.
+    display_name: Allowed volume types
+    default_value: []
+Kubernetes clusters should be accessible only over HTTPS:
+  excludedNamespaces:
+    name: excludedNamespaces
+    type: Array
+    description: List of Kubernetes namespaces to exclude from policy evaluation.
+    display_name: Namespace exclusions
+    default_value:
+    - kube-system
+    - gatekeeper-system
+    - azure-arc
+  namespaces:
+    name: namespaces
+    type: Array
+    description: List of Kubernetes namespaces to only include in policy evaluation.
+      An empty list means the policy is applied to all resources in all namespaces.
+    display_name: Namespace inclusions
+    default_value: []
+Kubernetes clusters should not allow container privilege escalation:
+  excludedNamespaces:
+    name: excludedNamespaces
+    type: Array
+    description: List of Kubernetes namespaces to exclude from policy evaluation.
+    display_name: Namespace exclusions
+    default_value:
+    - kube-system
+    - gatekeeper-system
+    - azure-arc
+  namespaces:
+    name: namespaces
+    type: Array
+    description: List of Kubernetes namespaces to only include in policy evaluation.
+      An empty list means the policy is applied to all resources in all namespaces.
+    display_name: Namespace inclusions
+    default_value: []
+'[Preview]: Kubernetes cluster services should only use allowed external IPs':
+  excludedNamespaces:
+    name: excludedNamespaces
+    type: Array
+    description: List of Kubernetes namespaces to exclude from policy evaluation.
+      Providing a value for this parameter is optional.
+    display_name: Namespace exclusions
+    default_value:
+    - kube-system
+    - gatekeeper-system
+    - azure-arc
+  namespaces:
+    name: namespaces
+    type: Array
+    description: List of Kubernetes namespaces to only include in policy evaluation.
+      An empty list means the policy is applied to all resources in all namespaces.
+    display_name: Namespace inclusions
+    default_value: []
+  allowedExternalIPs:
+    name: allowedExternalIPs
+    type: Array
+    description: List of External IPs that services are allowed to use. Empty array
+      means all external IPs are disallowed.
+    display_name: Allowed External IPs
+    default_value: []
+'[Preview]: Kubernetes clusters should disable automounting API credentials':
+  excludedNamespaces:
+    name: excludedNamespaces
+    type: Array
+    description: List of Kubernetes namespaces to exclude from policy evaluation.
+    display_name: Namespace exclusions
+    default_value:
+    - kube-system
+    - gatekeeper-system
+    - azure-arc
+  namespaces:
+    name: namespaces
+    type: Array
+    description: List of Kubernetes namespaces to only include in policy evaluation.
+      An empty list means the policy is applied to all resources in all namespaces.
+    display_name: Namespace inclusions
+    default_value: []
+'[Preview]: Kubernetes clusters should not grant CAP_SYS_ADMIN security capabilities':
+  excludedNamespaces:
+    name: excludedNamespaces
+    type: Array
+    description: List of Kubernetes namespaces to exclude from policy evaluation.
+    display_name: Namespace exclusions
+    default_value:
+    - kube-system
+    - gatekeeper-system
+    - azure-arc
+  namespaces:
+    name: namespaces
+    type: Array
+    description: List of Kubernetes namespaces to only include in policy evaluation.
+      An empty list means the policy is applied to all resources in all namespaces.
+    display_name: Namespace inclusions
+    default_value: []
+'[Preview]: Kubernetes clusters should not use specific security capabilities':
+  excludedNamespaces:
+    name: excludedNamespaces
+    type: Array
+    description: List of Kubernetes namespaces to exclude from policy evaluation.
+    display_name: Namespace exclusions
+    default_value:
+    - kube-system
+    - gatekeeper-system
+    - azure-arc
+  namespaces:
+    name: namespaces
+    type: Array
+    description: List of Kubernetes namespaces to only include in policy evaluation.
+      An empty list means the policy is applied to all resources in all namespaces.
+    display_name: Namespace inclusions
+    default_value: []
+  disallowedCapabilities:
+    name: disallowedCapabilities
+    type: Array
+    description: List of capabilities that containers are not able to use
+    display_name: Blocked capabilities
+    default_value: []
+'[Preview]: Kubernetes clusters should not use the default namespace':
+  excludedNamespaces:
+    name: excludedNamespaces
+    type: Array
+    description: List of Kubernetes namespaces to exclude from policy evaluation.
+    display_name: Namespace exclusions
+    default_value:
+    - kube-system
+    - gatekeeper-system
+    - azure-arc
+  namespaces:
+    name: namespaces
+    type: Array
+    description: List of Kubernetes namespaces to only include in policy evaluation.
+      An empty list means the policy is applied to all resources in all namespaces.
+    display_name: Namespace inclusions
+    default_value: []
+
diff --git a/test/logic/service-parameters-template.yml b/test/logic/service-parameters-template.yml
new file mode 100644
index 0000000..fd6d05d
--- /dev/null
+++ b/test/logic/service-parameters-template.yml
@@ -0,0 +1,3 @@
+{% for service_entry in t.service_entries %}
+{{ service_entry }}
+{% endfor %}
\ No newline at end of file
diff --git a/test/logic/test_policy_definition.py b/test/logic/test_policy_definition.py
index 2982bbd..2b4bdeb 100644
--- a/test/logic/test_policy_definition.py
+++ b/test/logic/test_policy_definition.py
@@ -4,7 +4,7 @@
 from azure_guardrails.logic.services import Service, Services
 
 
-class CommonTestCase(unittest.TestCase):
+class PolicyDefinitionTestCase(unittest.TestCase):
     def setUp(self):
         self.policy_json = utils.get_policy_json(service_name="Automation", filename="Automation_AuditUnencryptedVars_Audit.json")
 
@@ -15,23 +15,3 @@ def test_policy_loading(self):
         self.assertTrue(policy.category == "Automation")
         self.assertListEqual(policy.allowed_effects, ['audit', 'deny', 'disabled'])
         self.assertFalse = policy.modifies_resources
-
-    def test_services(self):
-        services = Services()
-        # print(f"Service Names: {', '.join(services.service_names)}")
-
-        display_names = services.get_display_names(all_policies=True)
-        print(f"All Policies: {len(display_names)}")
-
-        display_names = services.get_display_names()
-        print(f"No parameters or modification: {len(display_names)}")
-
-        display_names = services.get_display_names(with_parameters=True)
-        print(f"With Parameters only: {len(display_names)}")
-
-        display_names = services.get_display_names(with_parameters=False, with_modify_capabilities=True)
-        print(f"With Modify capabilities only: {len(display_names)}")
-
-        display_names = services.get_display_names(with_parameters=True, with_modify_capabilities=True)
-        print(f"With Modify capabilities and Parameters only: {len(display_names)}")
-
diff --git a/test/logic/test_services.py b/test/logic/test_services.py
new file mode 100644
index 0000000..2569a21
--- /dev/null
+++ b/test/logic/test_services.py
@@ -0,0 +1,100 @@
+import unittest
+from azure_guardrails.shared import utils
+from azure_guardrails.logic.policy_definition import Parameter, PolicyDefinition, Properties
+from azure_guardrails.logic.services import Service, Services
+import yaml
+import ruamel.yaml
+import json
+import os
+
+example_config_file = os.path.abspath(os.path.join(
+    os.path.dirname(__file__),
+    "service-parameters-template.yml"
+))
+
+
+class ServiceTestCase(unittest.TestCase):
+    def test_service_get_display_names_by_service_with_parameters(self):
+        services = Service(service_name="Kubernetes")
+        display_names = services.get_display_names_by_service_with_parameters(has_defaults=True)
+        expected_keys = [
+            "Do not allow privileged containers in Kubernetes cluster",
+            "Enforce internal load balancers in Kubernetes cluster",
+            "Kubernetes cluster containers should not share host process ID or host IPC namespace",
+            "Kubernetes cluster containers should only use allowed AppArmor profiles",
+            "Kubernetes cluster containers should only use allowed ProcMountType",
+            "Kubernetes cluster containers should only use allowed capabilities",
+            "Kubernetes cluster containers should only use allowed seccomp profiles",
+            "Kubernetes cluster containers should run with a read only root file system",
+            "Kubernetes cluster pod FlexVolume volumes should only use allowed drivers",
+            "Kubernetes cluster pod hostPath volumes should only use allowed host paths",
+            "Kubernetes cluster pods and containers should only run with approved user and group IDs",
+            "Kubernetes cluster pods and containers should only use allowed SELinux options",
+            "Kubernetes cluster pods should only use allowed volume types",
+            "Kubernetes clusters should be accessible only over HTTPS",
+            "Kubernetes clusters should not allow container privilege escalation",
+            "[Preview]: Kubernetes cluster services should only use allowed external IPs",
+            "[Preview]: Kubernetes clusters should disable automounting API credentials",
+            "[Preview]: Kubernetes clusters should not grant CAP_SYS_ADMIN security capabilities",
+            "[Preview]: Kubernetes clusters should not use specific security capabilities",
+            "[Preview]: Kubernetes clusters should not use the default namespace"
+        ]
+        keys = list(display_names.keys())
+        keys.sort()
+        # print(json.dumps(keys, indent=4))
+        # Doing this in a loop to future-proof the unit test
+        for expected_key in expected_keys:
+            self.assertTrue(expected_key in keys)
+        # print(ruamel.yaml.dump(display_names, Dumper=ruamel.yaml.RoundTripDumper))
+
+        from jinja2 import Template, Environment, FileSystemLoader
+        policies = {"Kubernetes": display_names}
+        header_format_string = """# ---------------------------------------------------------------------------------------------------------------------
+# {{ service_name }}
+# ---------------------------------------------------------------------------------------------------------------------
+
+{{ content }}
+"""
+        header_template = Template(header_format_string)
+        service_entries = []
+        for service_name, service_policies in policies.items():
+            pretty_policies = ruamel.yaml.dump(service_policies, Dumper=ruamel.yaml.RoundTripDumper)
+            rendered = header_template.render(dict(service_name=service_name, content=pretty_policies))
+            service_entries.append(rendered)
+        template_contents = dict(
+            service_entries=service_entries,
+        )
+        template_path = os.path.join(os.path.dirname(__file__))
+        env = Environment(loader=FileSystemLoader(template_path))  # nosec
+        template = env.get_template("service-parameters-template.yml")
+        result = template.render(t=template_contents)
+        print(result)
+
+
+class ServicesTestCase(unittest.TestCase):
+    def setUp(self):
+        self.policy_json = utils.get_policy_json(service_name="Automation", filename="Automation_AuditUnencryptedVars_Audit.json")
+        self.services = Services()
+
+    def test_services(self):
+        services = Services()
+        # print(f"Service Names: {', '.join(services.service_names)}")
+
+        display_names = services.get_display_names(all_policies=True)
+        print(f"All Policies: {len(display_names)}")
+
+        display_names = services.get_display_names()
+        print(f"No parameters or modification: {len(display_names)}")
+
+        display_names = services.get_display_names(with_parameters=True)
+        print(f"With Parameters only: {len(display_names)}")
+
+        display_names = services.get_display_names(with_parameters=False, with_modify_capabilities=True)
+        print(f"With Modify capabilities only: {len(display_names)}")
+
+        display_names = services.get_display_names(with_parameters=True, with_modify_capabilities=True)
+        print(f"With Modify capabilities and Parameters only: {len(display_names)}")
+
+    def test_get_display_names_by_service_with_parameters(self):
+        results = self.services.get_display_names_sorted_by_service(with_parameters=True)
+        print(json.dumps(results, indent=4))
diff --git a/test/logic/test_terraform.py b/test/logic/test_terraform.py
index 3deaca2..24d3508 100644
--- a/test/logic/test_terraform.py
+++ b/test/logic/test_terraform.py
@@ -1,6 +1,7 @@
 import unittest
+import json
 from azure_guardrails.shared import utils
-from azure_guardrails.logic.terraform import get_terraform_template
+from azure_guardrails.logic.terraform import get_terraform_template, TerraformTemplate
 from azure_guardrails.logic.services import Services, Service
 
 
@@ -12,9 +13,10 @@ def test_terraform_single_service(self):
         subscription_name = "example-subscription"
         management_group = ""
         enforcement_mode = False
-        result = get_terraform_template(name=policy_set_name, policy_names=policy_names, subscription_name=subscription_name,
+        result = get_terraform_template(name=policy_set_name, policy_names=policy_names,
+                                        subscription_name=subscription_name,
                                         management_group=management_group, enforcement_mode=enforcement_mode)
-        print(result)
+        # print(result)
 
     def test_terraform_all_services(self):
         services = Services()
@@ -23,6 +25,113 @@ def test_terraform_all_services(self):
         management_group = ""
         enforcement_mode = False
         display_names = services.get_display_names_sorted_by_service(with_parameters=False)
-        result = get_terraform_template(name=policy_set_name, policy_names=display_names, subscription_name=subscription_name,
+        result = get_terraform_template(name=policy_set_name, policy_names=display_names,
+                                        subscription_name=subscription_name,
                                         management_group=management_group, enforcement_mode=enforcement_mode)
-        print(result)
+        # print(result)
+
+class TerraformTemplateClassTestCase(unittest.TestCase):
+    def setUp(self):
+        self.example_parameters = {
+            "Kubernetes": {
+                "Do not allow privileged containers in Kubernetes cluster": {
+                    "excludedNamespaces": {
+                        "type": "Array",
+                        "metadata": {
+                            "displayName": "Namespace exclusions",
+                            "description": "List of Kubernetes namespaces to exclude from policy evaluation."
+                        },
+                        "defaultValue": ["kube-system", "gatekeeper-system", "azure-arc"]
+                    },
+                    "namespaces": {
+                        "type": "Array",
+                        "metadata": {
+                            "displayName": "Namespace inclusions",
+                            "description": "List of Kubernetes namespaces to only include in policy evaluation. An empty list means the policy is applied to all resources in all namespaces."
+                        },
+                        "defaultValue": []
+                    }
+                },
+                "Kubernetes cluster containers should only use allowed capabilities": {
+                    "excludedNamespaces": {
+                        "type": "Array",
+                        "metadata": {
+                            "displayName": "Namespace exclusions",
+                            "description": "List of Kubernetes namespaces to exclude from policy evaluation."
+                        },
+                        "defaultValue": ["kube-system", "gatekeeper-system", "azure-arc"]
+                    },
+                    "namespaces": {
+                        "type": "Array",
+                        "metadata": {
+                            "displayName": "Namespace inclusions",
+                            "description": "List of Kubernetes namespaces to only include in policy evaluation. An empty list means the policy is applied to all resources in all namespaces."
+                        },
+                        "defaultValue": []
+                    },
+                    "allowedCapabilities": {
+                        "type": "Array",
+                        "metadata": {
+                            "displayName": "Allowed capabilities",
+                            "description": "The list of capabilities that are allowed to be added to a container. Provide empty list as input to block everything."
+                        },
+                        "defaultValue": []
+                    },
+                    "requiredDropCapabilities": {
+                        "type": "Array",
+                        "metadata": {
+                            "displayName": "Required drop capabilities",
+                            "description": "The list of capabilities that must be dropped by a container."
+                        },
+                        "defaultValue": []
+                    }
+                }
+            }
+        }
+        self.terraform_template = TerraformTemplate(name="example", parameters=self.example_parameters, subscription_name="example")
+
+    def test_get_policy_parameters(self):
+        results = self.terraform_template.get_policy_parameters("Kubernetes", "Kubernetes cluster containers should only use allowed capabilities")
+        # print(json.dumps(results, indent=4))
+        # print(json.dumps(list(results.keys()), indent=4))
+        parameters = list(results.keys())
+        expected_parameters = [
+            "excludedNamespaces",
+            "namespaces",
+            "allowedCapabilities",
+            "requiredDropCapabilities"
+        ]
+        # print(terraform_template.service_parameters)
+        self.assertListEqual(parameters, expected_parameters)
+
+    def test_policy_names(self):
+        expected_policy_list = [
+            'Do not allow privileged containers in Kubernetes cluster',
+            'Kubernetes cluster containers should only use allowed capabilities'
+        ]
+        # print(terraform_template.policy_names())
+        self.assertListEqual(self.terraform_template.policy_names(), expected_policy_list)
+
+    def test_all_parameters(self):
+        expected_parameters = [
+            "excludedNamespaces",
+            "namespaces",
+            "allowedCapabilities",
+            "requiredDropCapabilities"
+        ]
+        results = self.terraform_template.all_parameters()
+        print(json.dumps(results, indent=4))
+        self.assertListEqual(list(results.keys()), expected_parameters)
+
+    def test_policy_definition_reference_parameters(self):
+        expected_results = {
+            'Do not allow privileged containers in Kubernetes cluster': ['excludedNamespaces', 'namespaces'],
+            'Kubernetes cluster containers should only use allowed capabilities': ['excludedNamespaces', 'namespaces', 'allowedCapabilities', 'requiredDropCapabilities']
+        }
+        print(self.terraform_template.policy_definition_reference_parameters())
+        self.assertDictEqual(self.terraform_template.policy_definition_reference_parameters(), expected_results)
+
+    def test_terraform_template_render(self):
+        # for service_name, policies_with_params in self.terraform_template.policy_definition_reference_parameters()
+        results = self.terraform_template.rendered()
+        print(results)
diff --git a/test/shared/test_compliance_data.py b/test/shared/test_compliance_data.py
index ce849db..e1c5583 100644
--- a/test/shared/test_compliance_data.py
+++ b/test/shared/test_compliance_data.py
@@ -18,7 +18,7 @@ def test_policy_definition_names(self):
     def test_get_benchmark_data_matching_policy_definition(self):
         display_name = "A maximum of 3 owners should be designated for your subscription"
         results = self.policy_compliance_data.get_benchmark_data_matching_policy_definition(display_name)
-        print(json.dumps(results, indent=4))
+        # print(json.dumps(results, indent=4))
 
 
 class ComplianceCoverageTestCase(unittest.TestCase):
@@ -32,7 +32,7 @@ def test_compliance_coverage_response(self):
         results = compliance_coverage.matching_metadata
         # print(json.dumps(results, indent=4))
         markdown_table = compliance_coverage.markdown_table()
-        print(markdown_table)
+        # print(markdown_table)
         # print(tabulate(results, headers=headers, tablefmt="github"))
 
     def test_compliance_coverage_response_full(self):
@@ -42,5 +42,5 @@ def test_compliance_coverage_response_full(self):
         # results = compliance_coverage.matching_metadata
         # print(json.dumps(results, indent=4))
         markdown_table = compliance_coverage.markdown_table()
-        print(markdown_table)
+        # print(markdown_table)
         # print(tabulate(results, headers=headers, tablefmt="github"))
diff --git a/test/shared/test_config.py b/test/shared/test_config.py
index 530e822..d59ff32 100644
--- a/test/shared/test_config.py
+++ b/test/shared/test_config.py
@@ -18,7 +18,7 @@
 class ConfigTestCase(unittest.TestCase):
     def test_default_config_template(self):
         result = get_config_template()
-        print(result)
+        # print(result)
 
     def test_config_object(self):
         exclude_policies = {}
@@ -75,8 +75,8 @@ def test_read_config_file(self):
 
     def test_read_config(self):
         default_config_template = get_config_template()
-        print(default_config_template)
+        # print(default_config_template)
 
     def test_default_config(self):
         config = get_default_config()
-        print(config)
+        # print(config)