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

Issue:
trim policy assignment name
Should be no more than 24 characters


Discussion / hints:

Fix patch:
diff --git a/Makefile b/Makefile
index 4e8ec22..bfc1668 100644
--- a/Makefile
+++ b/Makefile
@@ -31,7 +31,7 @@ serve-docs: setup-dev
 	mkdocs serve --dev-addr "127.0.0.1:8001"
 
 .PHONY: build
-build: setup-env clean
+build: clean setup-env
 	python3 -m pip install --upgrade setuptools wheel
 	python3 -m setup -q sdist bdist_wheel
 
@@ -89,7 +89,7 @@ count-loc:
 .PHONY: terraform-demo
 terraform-demo: install
 	azure-guardrails --help
-	azure-guardrails generate-terraform --service all --subscription-name example --no-params > examples/terraform-demo/main.tf
+	azure-guardrails generate-terraform --service all --subscription example --no-params > examples/terraform-demo/main.tf
 
 .PHONY: update-policy-table
 update-policy-table: install
diff --git a/azure_guardrails/command/generate_terraform.py b/azure_guardrails/command/generate_terraform.py
index f112938..64ce116 100644
--- a/azure_guardrails/command/generate_terraform.py
+++ b/azure_guardrails/command/generate_terraform.py
@@ -146,11 +146,14 @@ def generate_terraform(
 
     parameter_requirement_str = ""
     if no_params:
-        parameter_requirement_str = "no-params"
+        parameter_requirement_str = "NP"
     elif params_required:
-        parameter_requirement_str = "params-required"
+        parameter_requirement_str = "PR"
     elif params_optional:
-        parameter_requirement_str = "params-optional"
+        parameter_requirement_str = "PO"
+
+    # Policy Initiative Category
+    category = "Testing"
 
     if service == "all":
         azure_policies = AzurePolicies(service_names=["all"], config=config)
@@ -167,6 +170,7 @@ def generate_terraform(
             subscription_name=subscription,
             management_group=management_group,
             enforcement_mode=enforcement_mode,
+            category=category
         )
     else:
         audit_only = False
@@ -179,6 +183,7 @@ def generate_terraform(
             management_group=management_group,
             enforcement_mode=enforcement_mode,
             parameter_requirement_str=parameter_requirement_str,
+            category=category
         )
     result = terraform_template.rendered()
     print(result)
diff --git a/azure_guardrails/shared/iam_definition.py b/azure_guardrails/shared/iam_definition.py
index 795e0ac..86cfe20 100644
--- a/azure_guardrails/shared/iam_definition.py
+++ b/azure_guardrails/shared/iam_definition.py
@@ -60,7 +60,6 @@ def skip_display_names(policy_definition: PolicyDefinition, config: Config = DEF
         return False
 
 
-
 class AzurePolicies:
     def __init__(
             self,
@@ -259,7 +258,12 @@ def get_all_policy_ids_sorted_by_service(self, no_params: bool = True, params_op
                     # If audit_only is not used, don't worry about it
                     if service_results:
                         results[service_name] = service_results
-        return results
+        # Trim the irrelevant services
+        policy_id_pairs = {}
+        for service_name, service_policies in results.items():
+            if service_name in self.service_names:
+                policy_id_pairs[service_name] = service_policies
+        return policy_id_pairs
 
     def compliance_coverage_data(self, no_params: bool = True, params_optional: bool = True, params_required: bool = True) -> dict:
         results = {}
diff --git a/azure_guardrails/shared/utils.py b/azure_guardrails/shared/utils.py
index 0e259f1..13f4232 100644
--- a/azure_guardrails/shared/utils.py
+++ b/azure_guardrails/shared/utils.py
@@ -104,3 +104,20 @@ def get_github_link(service_name: str, file_name: str) -> str:
         service_name = service_name.replace(" ", "%20")
     result = f"{github_link_prefix}/{service_name}/{file_name}"
     return result
+
+
+# shorten the name if it is over a certain length to avoid hitting limits
+
+def format_policy_name(name: str, parameter_requirement_str) -> str:
+    """
+    Shortens a name to 24 characters minimum to avoid hitting Policy Assignment limit.
+
+    Azure Policy Assignment names require 24 characters or less
+    """
+    # 21, because we want to append '-NP', '-OP', or '_RP'
+    if len(name) > 21:
+        name = name[0:21]
+    initiative_name = f"{name}-{parameter_requirement_str}"
+    initiative_name = initiative_name.replace("-", "_")
+    # initiative_name = initiative_name.lower()
+    return initiative_name
diff --git a/azure_guardrails/terraform/no-parameters/policy-initiative-no-params.tf b/azure_guardrails/terraform/no-parameters/policy-initiative-no-params.tf
index 9d74ef3..adf5936 100644
--- a/azure_guardrails/terraform/no-parameters/policy-initiative-no-params.tf
+++ b/azure_guardrails/terraform/no-parameters/policy-initiative-no-params.tf
@@ -1,9 +1,9 @@
 locals {
-  name_{{ t.name }} = "{{ t.initiative_name }}"
-  subscription_name_{{ t.name }} = "{{ t.subscription_name }}"
-  management_group_{{ t.name }} = "{{ t.management_group }}"
-  enforcement_mode_{{ t.name }} = {{ t.enforcement_mode }}
-  policy_ids_{{ t.name }} = [{% for service_name, service_policies in t.policy_id_pairs.items() %}
+  name_{{ t.label }} = "{{ t.initiative_name }}"
+  subscription_name_{{ t.label }} = "{{ t.subscription_name }}"
+  management_group_{{ t.label }} = "{{ t.management_group }}"
+  enforcement_mode_{{ t.label }} = {{ t.enforcement_mode }}
+  policy_ids_{{ t.label }} = [{% for service_name, service_policies in t.policy_id_pairs.items() %}
     # -----------------------------------------------------------------------------------------------------------------
     # {{ service_name }}
     # -----------------------------------------------------------------------------------------------------------------{% for policy_id, policy_details in service_policies.items() %}
@@ -16,14 +16,14 @@ locals {
 # Azure Policy name lookups:
 # Because the policies are built-in, we can just look up their IDs by their names.
 # ---------------------------------------------------------------------------------------------------------------------
-data "azurerm_policy_definition" "{{ t.name }}" {
-  count        = length(local.policy_ids_{{ t.name }})
-  name         = element(local.policy_ids_{{ t.name }}, count.index)
+data "azurerm_policy_definition" "{{ t.label }}" {
+  count        = length(local.policy_ids_{{ t.label }})
+  name         = element(local.policy_ids_{{ t.label }}, count.index)
 }
 
 locals {
-  {{ t.name }}_policy_definitions = flatten([tolist([
-    for definition in data.azurerm_policy_definition.{{ t.name }}.*.id :
+  {{ t.label }}_policy_definitions = flatten([tolist([
+    for definition in data.azurerm_policy_definition.{{ t.label }}.*.id :
     map("policyDefinitionId", definition)
     ])
   ])
@@ -32,33 +32,33 @@ locals {
 # ---------------------------------------------------------------------------------------------------------------------
 # Conditional data lookups: If the user supplies management group, look up the ID of the management group
 # ---------------------------------------------------------------------------------------------------------------------
-data "azurerm_management_group" "{{ t.name }}" {
-  count = local.management_group_{{ t.name }} != "" ? 1 : 0
-  display_name  = local.management_group_{{ t.name }}
+data "azurerm_management_group" "{{ t.label }}" {
+  count = local.management_group_{{ t.label }} != "" ? 1 : 0
+  display_name  = local.management_group_{{ t.label }}
 }
 
 ### If the user supplies subscription, look up the ID of the subscription
-data "azurerm_subscriptions" "{{ t.name }}" {
-  count                 = local.subscription_name_{{ t.name }} != "" ? 1 : 0
-  display_name_contains = local.subscription_name_{{ t.name }}
+data "azurerm_subscriptions" "{{ t.label }}" {
+  count                 = local.subscription_name_{{ t.label }} != "" ? 1 : 0
+  display_name_contains = local.subscription_name_{{ t.label }}
 }
 
 locals {
-  {{ t.name }}_scope = local.management_group_{{ t.name }} != "" ? data.azurerm_management_group.{{ t.name }}[0].id : element(data.azurerm_subscriptions.{{ t.name }}[0].subscriptions.*.id, 0)
+  {{ t.label }}_scope = local.management_group_{{ t.label }} != "" ? data.azurerm_management_group.{{ t.label }}[0].id : element(data.azurerm_subscriptions.{{ t.label }}[0].subscriptions.*.id, 0)
 }
 
 # ---------------------------------------------------------------------------------------------------------------------
 # Policy Initiative
 # ---------------------------------------------------------------------------------------------------------------------
-resource "azurerm_policy_set_definition" "{{ t.name }}" {
-  name                  = local.name_{{ t.name }}
+resource "azurerm_policy_set_definition" "{{ t.label }}" {
+  name                  = local.name_{{ t.label }}
   policy_type           = "Custom"
-  display_name          = local.name_{{ t.name }}
-  description           = local.name_{{ t.name }}
-  management_group_name = local.management_group_{{ t.name }} == "" ? null : local.management_group_{{ t.name }}
-  policy_definitions    = tostring(jsonencode(local.{{ t.name }}_policy_definitions))
+  display_name          = local.name_{{ t.label }}
+  description           = local.name_{{ t.label }}
+  management_group_name = local.management_group_{{ t.label }} == "" ? null : local.management_group_{{ t.label }}
+  policy_definitions    = tostring(jsonencode(local.{{ t.label }}_policy_definitions))
   metadata = tostring(jsonencode({
-    category = local.name_{{ t.name }}
+    category = local.name_{{ t.label }}
   }))
 }
 
@@ -66,32 +66,32 @@ resource "azurerm_policy_set_definition" "{{ t.name }}" {
 # Azure Policy Assignments
 # Apply the Policy Initiative to the specified scope
 # ---------------------------------------------------------------------------------------------------------------------
-resource "azurerm_policy_assignment" "{{ t.name }}" {
-  name                 = local.name_{{ t.name }}
-  policy_definition_id = azurerm_policy_set_definition.{{ t.name }}.id
-  scope                = local.{{ t.name }}_scope
-  enforcement_mode     = local.enforcement_mode_{{ t.name }}
+resource "azurerm_policy_assignment" "{{ t.label }}" {
+  name                 = local.name_{{ t.label }}
+  policy_definition_id = azurerm_policy_set_definition.{{ t.label }}.id
+  scope                = local.{{ t.label }}_scope
+  enforcement_mode     = local.enforcement_mode_{{ t.label }}
 }
 
 # ---------------------------------------------------------------------------------------------------------------------
 # Outputs
 # ---------------------------------------------------------------------------------------------------------------------
-output "{{ t.name }}_policy_assignment_ids" {
-  value       = azurerm_policy_assignment.{{ t.name }}.id
+output "{{ t.label }}_policy_assignment_ids" {
+  value       = azurerm_policy_assignment.{{ t.label }}.id
   description = "The IDs of the Policy Assignments."
 }
 
-output "{{ t.name }}_scope" {
-  value       = local.{{ t.name }}_scope
+output "{{ t.label }}_scope" {
+  value       = local.{{ t.label }}_scope
   description = "The target scope - either the management group or subscription, depending on which parameters were supplied"
 }
 
-output "{{ t.name }}_policy_set_definition_id" {
-  value       = azurerm_policy_set_definition.{{ t.name }}.id
+output "{{ t.label }}_policy_set_definition_id" {
+  value       = azurerm_policy_set_definition.{{ t.label }}.id
   description = "The ID of the Policy Set Definition."
 }
 
-output "{{ t.name }}_count_of_policies_applied" {
+output "{{ t.label }}_count_of_policies_applied" {
   description = "The number of Policies applied as part of the Policy Initiative"
-  value       = length(local.policy_ids_{{ t.name }})
+  value       = length(local.policy_ids_{{ t.label }})
 }
\ No newline at end of file
diff --git a/azure_guardrails/terraform/terraform.py b/azure_guardrails/terraform/terraform.py
index 546c0fd..5a2dc0c 100644
--- a/azure_guardrails/terraform/terraform.py
+++ b/azure_guardrails/terraform/terraform.py
@@ -1,7 +1,7 @@
 import os
 import json
 from typing import Union
-from jinja2 import Template, Environment, FileSystemLoader
+from jinja2 import Environment, FileSystemLoader
 from azure_guardrails.shared import utils
 
 
@@ -9,13 +9,14 @@ class TerraformTemplateNoParams:
     """Terraform Template for when there are no parameters"""
 
     def __init__(
-        self,
-        policy_id_pairs: dict,
-        subscription_name: str = "",
-        management_group: str = "",
-        enforcement_mode: bool = False,
+            self,
+            policy_id_pairs: dict,
+            subscription_name: str = "",
+            management_group: str = "",
+            enforcement_mode: bool = False,
+            category: str = "Testing"
     ):
-        self.name = "noparams"
+        self.label = "no_params"  # This is just used for naming Terraform resources and variables
         self.initiative_name = self._initiative_name(
             subscription_name=subscription_name, management_group=management_group
         )
@@ -26,7 +27,7 @@ def __init__(
             self.enforcement_string = "true"
         else:
             self.enforcement_string = "false"
-        self.category = "Testing"
+        self.category = category
 
     @staticmethod
     def _initiative_name(subscription_name: str, management_group: str) -> str:
@@ -34,20 +35,15 @@ def _initiative_name(subscription_name: str, management_group: str) -> str:
             raise Exception(
                 "Please supply a value for the subscription name or the management group"
             )
+        parameter_requirement_str = "NP"
         if subscription_name:
-            # shorten the name if it is over a certain length to avoid hitting limits
-            if len(subscription_name) > 55:
-                subscription_name = subscription_name[0:54]
-            initiative_name = f"{subscription_name}-noparams"
+            initiative_name = utils.format_policy_name(subscription_name, parameter_requirement_str)
         else:
-            if len(management_group) > 55:
-                management_group = management_group[0:54]
-            initiative_name = f"{management_group}-noparams"
-        initiative_name = initiative_name.replace("-", "_")
-        initiative_name = initiative_name.lower()
+            initiative_name = utils.format_policy_name(management_group, parameter_requirement_str)
         return initiative_name
 
-    def _policy_id_pairs(self, policy_id_pairs: dict) -> dict:
+    @staticmethod
+    def _policy_id_pairs(policy_id_pairs: dict) -> dict:
         example_input = {
             "API for FHIR": {
                 "051cba44-2429-45b9-9649-46cec11c7119": {
@@ -61,6 +57,7 @@ def _policy_id_pairs(self, policy_id_pairs: dict) -> dict:
             }
         }
         all_valid_services = utils.get_service_names()
+        # Just validate the input, that's all
         for service_name, service_policies in policy_id_pairs.items():
             if service_name not in all_valid_services:
                 raise Exception("The service provided is not a valid service")
@@ -73,7 +70,7 @@ def _policy_id_pairs(self, policy_id_pairs: dict) -> dict:
 
     def rendered(self) -> str:
         template_contents = dict(
-            name=self.name,
+            label=self.label,
             initiative_name=self.initiative_name,
             policy_id_pairs=self.policy_id_pairs,
             subscription_name=self.subscription_name,
@@ -156,13 +153,15 @@ def __repr__(self) -> str:
 
 class TerraformTemplateWithParams:
     """Terraform Template with Parameters"""
+
     def __init__(
-        self,
-        policy_id_pairs: dict,
-        parameter_requirement_str: str,
-        subscription_name: str = "",
-        management_group: str = "",
-        enforcement_mode: bool = False,
+            self,
+            policy_id_pairs: dict,
+            parameter_requirement_str: str,
+            subscription_name: str = "",
+            management_group: str = "",
+            enforcement_mode: bool = False,
+            category: str = "Testing"
     ):
         self.name = self._initiative_name(
             subscription_name=subscription_name, management_group=management_group,
@@ -171,7 +170,7 @@ def __init__(
         self.service_parameters = self._parameters(policy_id_pairs)
         self.subscription_name = subscription_name
         self.management_group = management_group
-        self.category = "Testing"
+        self.category = category
         self.policy_id_pairs = self._policy_id_pairs(policy_id_pairs)
         if enforcement_mode:
             self.enforcement_string = "true"
@@ -184,17 +183,11 @@ def _initiative_name(subscription_name: str, management_group: str, parameter_re
             raise Exception(
                 "Please supply a value for the subscription name or the management group"
             )
+
         if subscription_name:
-            # shorten the name if it is over a certain length to avoid hitting limits
-            if len(subscription_name) > 50:
-                subscription_name = subscription_name[0:50]
-            initiative_name = f"{subscription_name}-{parameter_requirement_str}"
+            initiative_name = utils.format_policy_name(subscription_name, parameter_requirement_str)
         else:
-            if len(management_group) > 50:
-                management_group = management_group[0:50]
-            initiative_name = f"{management_group}-{parameter_requirement_str}"
-        initiative_name = initiative_name.replace("-", "_")
-        initiative_name = initiative_name.lower()
+            initiative_name = utils.format_policy_name(management_group, parameter_requirement_str)
         return initiative_name
 
     @staticmethod
@@ -228,7 +221,7 @@ def _parameters(parameters: dict) -> dict:
 
     @staticmethod
     def _policy_id_pairs(policy_id_pairs) -> dict:
-        """To be used in Terraform locals.policy_names"""
+        # Just validate the input, that's all
         all_valid_services = utils.get_service_names()
         for service_name, service_policies in policy_id_pairs.items():
             if service_name not in all_valid_services:
diff --git a/examples/terraform-demo-with-parameters/main.tf b/examples/terraform-demo-with-parameters/main.tf
index 1e949fb..465373f 100644
--- a/examples/terraform-demo-with-parameters/main.tf
+++ b/examples/terraform-demo-with-parameters/main.tf
@@ -1,10 +1,10 @@
 locals {
-  name_example_params_optional = "example_params_optional"
-  subscription_name_example_params_optional = "example"
-  management_group_example_params_optional = ""
-  category_example_params_optional = "Testing"
-  enforcement_mode_example_params_optional = false
-  policy_ids_example_params_optional = [
+  name_example_PO = "example_PO"
+  subscription_name_example_PO = "example"
+  management_group_example_PO = ""
+  category_example_PO = "Testing"
+  enforcement_mode_example_PO = false
+  policy_ids_example_PO = [
     # -----------------------------------------------------------------------------------------------------------------
     # API Management
     # -----------------------------------------------------------------------------------------------------------------
@@ -105,50 +105,50 @@ locals {
 
   ]
   policy_definition_map = zipmap(
-    data.azurerm_policy_definition.example_params_optional_definition_lookups.*.display_name,
-    data.azurerm_policy_definition.example_params_optional_definition_lookups.*.id
+    data.azurerm_policy_definition.example_PO_definition_lookups.*.display_name,
+    data.azurerm_policy_definition.example_PO_definition_lookups.*.id
   )
 }
 
 # ---------------------------------------------------------------------------------------------------------------------
 # Conditional data lookups: If the user supplies management group, look up the ID of the management group
 # ---------------------------------------------------------------------------------------------------------------------
-data "azurerm_management_group" "example_params_optional" {
-  count = local.management_group_example_params_optional != "" ? 1 : 0
-  display_name  = local.management_group_example_params_optional
+data "azurerm_management_group" "example_PO" {
+  count = local.management_group_example_PO != "" ? 1 : 0
+  display_name  = local.management_group_example_PO
 }
 
 ### If the user supplies subscription, look up the ID of the subscription
-data "azurerm_subscriptions" "example_params_optional" {
-  count                 = local.subscription_name_example_params_optional != "" ? 1 : 0
-  display_name_contains = local.subscription_name_example_params_optional
+data "azurerm_subscriptions" "example_PO" {
+  count                 = local.subscription_name_example_PO != "" ? 1 : 0
+  display_name_contains = local.subscription_name_example_PO
 }
 
 locals {
-  scope = local.management_group_example_params_optional != "" ? data.azurerm_management_group.example_params_optional[0].id : element(data.azurerm_subscriptions.example_params_optional[0].subscriptions.*.id, 0)
+  scope = local.management_group_example_PO != "" ? data.azurerm_management_group.example_PO[0].id : element(data.azurerm_subscriptions.example_PO[0].subscriptions.*.id, 0)
 }
 
 # ---------------------------------------------------------------------------------------------------------------------
 # Azure Policy Definition Lookups
 # ---------------------------------------------------------------------------------------------------------------------
 
-data "azurerm_policy_definition" "example_params_optional_definition_lookups" {
-  count = length(local.policy_ids_example_params_optional)
-  name  = local.policy_ids_example_params_optional[count.index]
+data "azurerm_policy_definition" "example_PO_definition_lookups" {
+  count = length(local.policy_ids_example_PO)
+  name  = local.policy_ids_example_PO[count.index]
 }
 
 # ---------------------------------------------------------------------------------------------------------------------
 # Azure Policy Initiative Definition
 # ---------------------------------------------------------------------------------------------------------------------
 
-resource "azurerm_policy_set_definition" "example_params_optional" {
-  name                  = local.name_example_params_optional
+resource "azurerm_policy_set_definition" "example_PO" {
+  name                  = local.name_example_PO
   policy_type           = "Custom"
-  display_name          = local.name_example_params_optional
-  description           = local.name_example_params_optional
-  management_group_name = local.management_group_example_params_optional == "" ? null : local.management_group_example_params_optional
+  display_name          = local.name_example_PO
+  description           = local.name_example_PO
+  management_group_name = local.management_group_example_PO == "" ? null : local.management_group_example_PO
   metadata = tostring(jsonencode({
-    category = local.category_example_params_optional
+    category = local.category_example_PO
   }))
 
 
@@ -608,11 +608,11 @@ PARAMETERS
 # Azure Policy Assignments
 # Apply the Policy Initiative to the specified scope
 # ---------------------------------------------------------------------------------------------------------------------
-resource "azurerm_policy_assignment" "example_params_optional" {
-  name                 = local.name_example_params_optional
-  policy_definition_id = azurerm_policy_set_definition.example_params_optional.id
+resource "azurerm_policy_assignment" "example_PO" {
+  name                 = local.name_example_PO
+  policy_definition_id = azurerm_policy_set_definition.example_PO.id
   scope                = local.scope
-  enforcement_mode     = local.enforcement_mode_example_params_optional
+  enforcement_mode     = local.enforcement_mode_example_PO
   parameters = jsonencode({
     listOfAllowedSKUs = { "value" = ["Developer", "Premium", "Isolated"] }
 	JavaLatestVersion = { "value" = "11" }
@@ -635,17 +635,17 @@ resource "azurerm_policy_assignment" "example_params_optional" {
 # ---------------------------------------------------------------------------------------------------------------------
 # Outputs
 # ---------------------------------------------------------------------------------------------------------------------
-output "example_params_optional_policy_assignment_ids" {
-  value       = azurerm_policy_assignment.example_params_optional.id
+output "example_PO_policy_assignment_ids" {
+  value       = azurerm_policy_assignment.example_PO.id
   description = "The IDs of the Policy Assignments."
 }
 
-output "example_params_optional_scope" {
+output "example_PO_scope" {
   value       = local.scope
   description = "The target scope - either the management group or subscription, depending on which parameters were supplied"
 }
 
-output "example_params_optional_policy_set_definition_id" {
-  value       = azurerm_policy_set_definition.example_params_optional.id
+output "example_PO_policy_set_definition_id" {
+  value       = azurerm_policy_set_definition.example_PO.id
   description = "The ID of the Policy Set Definition."
 }
diff --git a/examples/terraform-demo/main.tf b/examples/terraform-demo/main.tf
index 9781b89..73326a6 100644
--- a/examples/terraform-demo/main.tf
+++ b/examples/terraform-demo/main.tf
@@ -1,9 +1,9 @@
 locals {
-  name_noparams = "example_noparams"
-  subscription_name_noparams = "example"
-  management_group_noparams = ""
-  enforcement_mode_noparams = false
-  policy_ids_noparams = [
+  name_no_params = "example_NP"
+  subscription_name_no_params = "example"
+  management_group_no_params = ""
+  enforcement_mode_no_params = false
+  policy_ids_no_params = [
     # -----------------------------------------------------------------------------------------------------------------
     # API for FHIR
     # -----------------------------------------------------------------------------------------------------------------
@@ -481,14 +481,14 @@ locals {
 # Azure Policy name lookups:
 # Because the policies are built-in, we can just look up their IDs by their names.
 # ---------------------------------------------------------------------------------------------------------------------
-data "azurerm_policy_definition" "noparams" {
-  count        = length(local.policy_ids_noparams)
-  name         = element(local.policy_ids_noparams, count.index)
+data "azurerm_policy_definition" "no_params" {
+  count        = length(local.policy_ids_no_params)
+  name         = element(local.policy_ids_no_params, count.index)
 }
 
 locals {
-  noparams_policy_definitions = flatten([tolist([
-    for definition in data.azurerm_policy_definition.noparams.*.id :
+  no_params_policy_definitions = flatten([tolist([
+    for definition in data.azurerm_policy_definition.no_params.*.id :
     map("policyDefinitionId", definition)
     ])
   ])
@@ -497,33 +497,33 @@ locals {
 # ---------------------------------------------------------------------------------------------------------------------
 # Conditional data lookups: If the user supplies management group, look up the ID of the management group
 # ---------------------------------------------------------------------------------------------------------------------
-data "azurerm_management_group" "noparams" {
-  count = local.management_group_noparams != "" ? 1 : 0
-  display_name  = local.management_group_noparams
+data "azurerm_management_group" "no_params" {
+  count = local.management_group_no_params != "" ? 1 : 0
+  display_name  = local.management_group_no_params
 }
 
 ### If the user supplies subscription, look up the ID of the subscription
-data "azurerm_subscriptions" "noparams" {
-  count                 = local.subscription_name_noparams != "" ? 1 : 0
-  display_name_contains = local.subscription_name_noparams
+data "azurerm_subscriptions" "no_params" {
+  count                 = local.subscription_name_no_params != "" ? 1 : 0
+  display_name_contains = local.subscription_name_no_params
 }
 
 locals {
-  noparams_scope = local.management_group_noparams != "" ? data.azurerm_management_group.noparams[0].id : element(data.azurerm_subscriptions.noparams[0].subscriptions.*.id, 0)
+  no_params_scope = local.management_group_no_params != "" ? data.azurerm_management_group.no_params[0].id : element(data.azurerm_subscriptions.no_params[0].subscriptions.*.id, 0)
 }
 
 # ---------------------------------------------------------------------------------------------------------------------
 # Policy Initiative
 # ---------------------------------------------------------------------------------------------------------------------
-resource "azurerm_policy_set_definition" "noparams" {
-  name                  = local.name_noparams
+resource "azurerm_policy_set_definition" "no_params" {
+  name                  = local.name_no_params
   policy_type           = "Custom"
-  display_name          = local.name_noparams
-  description           = local.name_noparams
-  management_group_name = local.management_group_noparams == "" ? null : local.management_group_noparams
-  policy_definitions    = tostring(jsonencode(local.noparams_policy_definitions))
+  display_name          = local.name_no_params
+  description           = local.name_no_params
+  management_group_name = local.management_group_no_params == "" ? null : local.management_group_no_params
+  policy_definitions    = tostring(jsonencode(local.no_params_policy_definitions))
   metadata = tostring(jsonencode({
-    category = local.name_noparams
+    category = local.name_no_params
   }))
 }
 
@@ -531,33 +531,32 @@ resource "azurerm_policy_set_definition" "noparams" {
 # Azure Policy Assignments
 # Apply the Policy Initiative to the specified scope
 # ---------------------------------------------------------------------------------------------------------------------
-resource "azurerm_policy_assignment" "noparams" {
-  name                 = local.name_noparams
-  policy_definition_id = azurerm_policy_set_definition.noparams.id
-  scope                = local.noparams_scope
-  enforcement_mode     = local.enforcement_mode_noparams
+resource "azurerm_policy_assignment" "no_params" {
+  name                 = local.name_no_params
+  policy_definition_id = azurerm_policy_set_definition.no_params.id
+  scope                = local.no_params_scope
+  enforcement_mode     = local.enforcement_mode_no_params
 }
 
 # ---------------------------------------------------------------------------------------------------------------------
 # Outputs
 # ---------------------------------------------------------------------------------------------------------------------
-output "noparams_policy_assignment_ids" {
-  value       = azurerm_policy_assignment.noparams.id
+output "no_params_policy_assignment_ids" {
+  value       = azurerm_policy_assignment.no_params.id
   description = "The IDs of the Policy Assignments."
 }
 
-output "noparams_scope" {
-  value       = local.noparams_scope
+output "no_params_scope" {
+  value       = local.no_params_scope
   description = "The target scope - either the management group or subscription, depending on which parameters were supplied"
 }
 
-output "noparams_policy_set_definition_id" {
-  value       = azurerm_policy_set_definition.noparams.id
+output "no_params_policy_set_definition_id" {
+  value       = azurerm_policy_set_definition.no_params.id
   description = "The ID of the Policy Set Definition."
 }
 
-output "noparams_count_of_policies_applied" {
+output "no_params_count_of_policies_applied" {
   description = "The number of Policies applied as part of the Policy Initiative"
-  value       = length(local.policy_ids_noparams)
+  value       = length(local.policy_ids_no_params)
 }
-


Test patch:
diff --git a/test/files/policy_id_pairs_kv.json b/test/files/policy_id_pairs_kv.json
new file mode 100644
index 0000000..69de4c0
--- /dev/null
+++ b/test/files/policy_id_pairs_kv.json
@@ -0,0 +1,237 @@
+{
+    "Key Vault": {
+        "Azure Key Vault Managed HSM should have purge protection enabled": {
+            "short_id": "c39ba22d-4428-4149-b981-70acb31fc383",
+            "display_name": "Azure Key Vault Managed HSM should have purge protection enabled"
+        },
+        "Key vaults should have purge protection enabled": {
+            "short_id": "0b60c0b2-2dc2-4e1c-b5c9-abbed971de53",
+            "display_name": "Key vaults should have purge protection enabled"
+        },
+        "Key vaults should have soft delete enabled": {
+            "short_id": "1e66c121-a66a-4b1f-9b83-0fd99bf0fc2d",
+            "display_name": "Key vaults should have soft delete enabled"
+        },
+        "Resource logs in Azure Key Vault Managed HSM should be enabled": {
+            "short_id": "a2a5b911-5617-447e-a49e-59dbe0e0434b",
+            "display_name": "Resource logs in Azure Key Vault Managed HSM should be enabled",
+            "parameters": {
+                "requiredRetentionDays": {
+                    "name": "requiredRetentionDays",
+                    "type": "String",
+                    "description": "The required resource logs retention in days",
+                    "display_name": "Required retention (days)",
+                    "default_value": "365"
+                }
+            }
+        },
+        "Resource logs in Key Vault should be enabled": {
+            "short_id": "cf820ca0-f99e-4f3e-84fb-66e913812d21",
+            "display_name": "Resource logs in Key Vault should be enabled",
+            "parameters": {
+                "requiredRetentionDays": {
+                    "name": "requiredRetentionDays",
+                    "type": "String",
+                    "description": "The required resource logs retention in days",
+                    "display_name": "Required retention (days)",
+                    "default_value": "365"
+                }
+            }
+        },
+        "[Preview]: Certificates should be issued by the specified integrated certificate authority": {
+            "short_id": "8e826246-c976-48f6-b03e-619bb92b3d82",
+            "display_name": "[Preview]: Certificates should be issued by the specified integrated certificate authority",
+            "parameters": {
+                "allowedCAs": {
+                    "name": "allowedCAs",
+                    "type": "Array",
+                    "description": "The list of allowed certificate authorities supported by Azure Key Vault.",
+                    "display_name": "Allowed Azure Key Vault Supported CAs",
+                    "default_value": [
+                        "DigiCert",
+                        "GlobalSign"
+                    ],
+                    "allowed_values": [
+                        "DigiCert",
+                        "GlobalSign"
+                    ]
+                }
+            }
+        },
+        "[Preview]: Certificates should be issued by the specified non-integrated certificate authority": {
+            "short_id": "a22f4a40-01d3-4c7d-8071-da157eeff341",
+            "display_name": "[Preview]: Certificates should be issued by the specified non-integrated certificate authority"
+        },
+        "[Preview]: Certificates should have the specified lifetime action triggers": {
+            "short_id": "12ef42cb-9903-4e39-9c26-422d29570417",
+            "display_name": "[Preview]: Certificates should have the specified lifetime action triggers"
+        },
+        "[Preview]: Certificates should have the specified maximum validity period": {
+            "short_id": "0a075868-4c26-42ef-914c-5bc007359560",
+            "display_name": "[Preview]: Certificates should have the specified maximum validity period",
+            "parameters": {
+                "maximumValidityInMonths": {
+                    "name": "maximumValidityInMonths",
+                    "type": "Integer",
+                    "description": "The limit to how long a certificate may be valid for. Certificates with lengthy validity periods aren't best practice.",
+                    "display_name": "The maximum validity in months",
+                    "default_value": 12
+                }
+            }
+        },
+        "[Preview]: Certificates should not expire within the specified number of days": {
+            "short_id": "f772fb64-8e40-40ad-87bc-7706e1949427",
+            "display_name": "[Preview]: Certificates should not expire within the specified number of days"
+        },
+        "[Preview]: Certificates should use allowed key types": {
+            "short_id": "1151cede-290b-4ba0-8b38-0ad145ac888f",
+            "display_name": "[Preview]: Certificates should use allowed key types",
+            "parameters": {
+                "allowedKeyTypes": {
+                    "name": "allowedKeyTypes",
+                    "type": "Array",
+                    "description": "The list of allowed certificate key types.",
+                    "display_name": "Allowed key types",
+                    "default_value": [
+                        "RSA",
+                        "RSA-HSM"
+                    ],
+                    "allowed_values": [
+                        "RSA",
+                        "RSA-HSM",
+                        "EC",
+                        "EC-HSM"
+                    ]
+                }
+            }
+        },
+        "[Preview]: Certificates using RSA cryptography should have the specified minimum key size": {
+            "short_id": "cee51871-e572-4576-855c-047c820360f0",
+            "display_name": "[Preview]: Certificates using RSA cryptography should have the specified minimum key size"
+        },
+        "[Preview]: Certificates using elliptic curve cryptography should have allowed curve names": {
+            "short_id": "bd78111f-4953-4367-9fd5-7e08808b54bf",
+            "display_name": "[Preview]: Certificates using elliptic curve cryptography should have allowed curve names",
+            "parameters": {
+                "allowedECNames": {
+                    "name": "allowedECNames",
+                    "type": "Array",
+                    "description": "The list of allowed curve names for elliptic curve cryptography certificates.",
+                    "display_name": "Allowed elliptic curve names",
+                    "default_value": [
+                        "P-256",
+                        "P-256K",
+                        "P-384",
+                        "P-521"
+                    ],
+                    "allowed_values": [
+                        "P-256",
+                        "P-256K",
+                        "P-384",
+                        "P-521"
+                    ]
+                }
+            }
+        },
+        "[Preview]: Firewall should be enabled on Key Vault": {
+            "short_id": "55615ac9-af46-4a59-874e-391cc3dfb490",
+            "display_name": "[Preview]: Firewall should be enabled on Key Vault"
+        },
+        "[Preview]: Key Vault keys should have an expiration date": {
+            "short_id": "152b15f7-8e1f-4c1f-ab71-8c010ba5dbc0",
+            "display_name": "[Preview]: Key Vault keys should have an expiration date"
+        },
+        "[Preview]: Key Vault secrets should have an expiration date": {
+            "short_id": "98728c90-32c7-4049-8429-847dc0f4fe37",
+            "display_name": "[Preview]: Key Vault secrets should have an expiration date"
+        },
+        "[Preview]: Keys should be backed by a hardware security module (HSM)": {
+            "short_id": "587c79fe-dd04-4a5e-9d0b-f89598c7261b",
+            "display_name": "[Preview]: Keys should be backed by a hardware security module (HSM)"
+        },
+        "[Preview]: Keys should be the specified cryptographic type RSA or EC": {
+            "short_id": "75c4f823-d65c-4f29-a733-01d0077fdbcb",
+            "display_name": "[Preview]: Keys should be the specified cryptographic type RSA or EC",
+            "parameters": {
+                "allowedKeyTypes": {
+                    "name": "allowedKeyTypes",
+                    "type": "Array",
+                    "description": "The list of allowed key types",
+                    "display_name": "Allowed key types",
+                    "default_value": [
+                        "RSA",
+                        "RSA-HSM",
+                        "EC",
+                        "EC-HSM"
+                    ],
+                    "allowed_values": [
+                        "RSA",
+                        "RSA-HSM",
+                        "EC",
+                        "EC-HSM"
+                    ]
+                }
+            }
+        },
+        "[Preview]: Keys should have more than the specified number of days before expiration": {
+            "short_id": "5ff38825-c5d8-47c5-b70e-069a21955146",
+            "display_name": "[Preview]: Keys should have more than the specified number of days before expiration"
+        },
+        "[Preview]: Keys should have the specified maximum validity period": {
+            "short_id": "49a22571-d204-4c91-a7b6-09b1a586fbc9",
+            "display_name": "[Preview]: Keys should have the specified maximum validity period"
+        },
+        "[Preview]: Keys should not be active for longer than the specified number of days": {
+            "short_id": "c26e4b24-cf98-4c67-b48b-5a25c4c69eb9",
+            "display_name": "[Preview]: Keys should not be active for longer than the specified number of days"
+        },
+        "[Preview]: Keys using RSA cryptography should have a specified minimum key size": {
+            "short_id": "82067dbb-e53b-4e06-b631-546d197452d9",
+            "display_name": "[Preview]: Keys using RSA cryptography should have a specified minimum key size"
+        },
+        "[Preview]: Keys using elliptic curve cryptography should have the specified curve names": {
+            "short_id": "ff25f3c8-b739-4538-9d07-3d6d25cfb255",
+            "display_name": "[Preview]: Keys using elliptic curve cryptography should have the specified curve names",
+            "parameters": {
+                "allowedECNames": {
+                    "name": "allowedECNames",
+                    "type": "Array",
+                    "description": "The list of allowed curve names for elliptic curve cryptography certificates.",
+                    "display_name": "Allowed elliptic curve names",
+                    "default_value": [
+                        "P-256",
+                        "P-256K",
+                        "P-384",
+                        "P-521"
+                    ],
+                    "allowed_values": [
+                        "P-256",
+                        "P-256K",
+                        "P-384",
+                        "P-521"
+                    ]
+                }
+            }
+        },
+        "[Preview]: Private endpoint should be configured for Key Vault": {
+            "short_id": "5f0bc445-3935-4915-9981-011aa2b46147",
+            "display_name": "[Preview]: Private endpoint should be configured for Key Vault"
+        },
+        "[Preview]: Secrets should have content type set": {
+            "short_id": "75262d3e-ba4a-4f43-85f8-9f72c090e5e3",
+            "display_name": "[Preview]: Secrets should have content type set"
+        },
+        "[Preview]: Secrets should have more than the specified number of days before expiration": {
+            "short_id": "b0eb591a-5e70-4534-a8bf-04b9c489584a",
+            "display_name": "[Preview]: Secrets should have more than the specified number of days before expiration"
+        },
+        "[Preview]: Secrets should have the specified maximum validity period": {
+            "short_id": "342e8053-e12e-4c44-be01-c3c2f318400f",
+            "display_name": "[Preview]: Secrets should have the specified maximum validity period"
+        },
+        "[Preview]: Secrets should not be active for longer than the specified number of days": {
+            "short_id": "e8d99835-8a06-45ae-a8e0-87a91941ccfe",
+            "display_name": "[Preview]: Secrets should not be active for longer than the specified number of days"
+        }
+    }
+}
\ No newline at end of file
diff --git a/test/terraform/test_terraform.py b/test/files/stash/test_terraform.py
similarity index 100%
rename from test/terraform/test_terraform.py
rename to test/files/stash/test_terraform.py
diff --git a/test/terraform/test_terraform_v2.py b/test/files/stash/test_terraform_v2.py
similarity index 100%
rename from test/terraform/test_terraform_v2.py
rename to test/files/stash/test_terraform_v2.py
diff --git a/test/shared/test_iam_definition.py b/test/shared/test_iam_definition.py
index 7f43e8d..1a8361e 100644
--- a/test/shared/test_iam_definition.py
+++ b/test/shared/test_iam_definition.py
@@ -3,11 +3,12 @@
 import json
 from azure_guardrails.shared.iam_definition import AzurePolicies
 from azure_guardrails.guardrails.policy_definition import PolicyDefinition
-
+from azure_guardrails.shared import utils
 
 class IamDefinitionTestCase(unittest.TestCase):
     def setUp(self) -> None:
         self.azure_policies = AzurePolicies()
+        self.kv_azure_policies = AzurePolicies(service_names=["Key Vault"])
 
     def test_policy_ids(self):
         # print(self.azure_policies.policy_ids())
@@ -62,7 +63,6 @@ def test_get_all_display_names_sorted_by_service(self):
         results = self.azure_policies.get_all_display_names_sorted_by_service()
         print(json.dumps(results, indent=4))
 
-
     def test_get_all_policy_ids_sorted_by_service(self):
         results = self.azure_policies.get_all_policy_ids_sorted_by_service(no_params=True)
         # print(json.dumps(results, indent=4))
@@ -107,6 +107,17 @@ def test_get_all_policy_ids_sorted_by_service(self):
         # print(json.dumps(container_registry_result.get(cmk_message), indent=4))
         self.assertDictEqual(container_registry_result.get(cmk_message), expected_result)
 
+    def test_policy_id_pairs_single_service(self):
+        policy_id_pairs = self.kv_azure_policies.get_all_policy_ids_sorted_by_service(no_params=True)
+        # print(json.dumps(policy_id_pairs, indent=4))
+        kv_policy_names = list(policy_id_pairs.get("Key Vault").keys())
+        expected_results_file = os.path.join(os.path.dirname(__file__), os.path.pardir, "files", "policy_id_pairs_kv.json")
+        expected_results = utils.read_json_file(expected_results_file)
+        # print(json.dumps(expected_results, indent=4))
+        # print(kv_policy_names)
+        for policy_name, policy_details in expected_results["Key Vault"].items():
+            self.assertTrue(policy_name in kv_policy_names)
+
     def test_compliance_coverage_data_azure_policies(self):
         results = self.azure_policies.compliance_coverage_data()
         print(json.dumps(results, indent=4))
diff --git a/test/terraform/test_terraform_no_params.py b/test/terraform/test_terraform_no_params.py
new file mode 100644
index 0000000..1cf436a
--- /dev/null
+++ b/test/terraform/test_terraform_no_params.py
@@ -0,0 +1,76 @@
+import unittest
+import json
+import os
+from azure_guardrails.terraform.terraform import TerraformTemplateNoParams
+from azure_guardrails.shared.iam_definition import AzurePolicies
+from azure_guardrails.shared.config import get_default_config
+from azure_guardrails.shared import utils
+
+
+class TerraformTemplateNoParamsTestCase(unittest.TestCase):
+    def setUp(self) -> None:
+        exclude_services = []
+        subscription = "example"
+        management_group = ""
+        enforcement_mode = False
+        category = "Testing"
+
+        config = get_default_config(exclude_services=exclude_services)
+        # All Services
+        self.azure_policies = AzurePolicies(service_names=["all"], config=config)
+        self.policy_id_pairs = self.azure_policies.get_all_policy_ids_sorted_by_service(
+            no_params=True
+        )
+        self.terraform_template = TerraformTemplateNoParams(
+            policy_id_pairs=self.policy_id_pairs,
+            subscription_name=subscription,
+            management_group=management_group,
+            enforcement_mode=enforcement_mode,
+            category=category
+        )
+        # Key Vault only
+        self.kv_azure_policies = AzurePolicies(service_names=["Key Vault"], config=config)
+        kv_policy_id_pairs = self.kv_azure_policies.get_all_policy_ids_sorted_by_service(
+            no_params=True
+        )
+        self.kv_terraform_template = TerraformTemplateNoParams(
+            policy_id_pairs=kv_policy_id_pairs,
+            subscription_name=subscription,
+            management_group=management_group,
+            enforcement_mode=enforcement_mode,
+            category=category
+        )
+
+    def test_terraform_key_vault(self):
+        self.assertEqual("example_NP", self.kv_terraform_template.initiative_name)
+        print(self.terraform_template.initiative_name)
+        self.assertEqual("no_params", self.kv_terraform_template.label)
+        print(self.terraform_template.label)
+        self.assertEqual("example", self.kv_terraform_template.subscription_name)
+        self.assertEqual("", self.kv_terraform_template.management_group)
+        policy_id_pairs = self.kv_azure_policies.get_all_policy_ids_sorted_by_service(no_params=True)
+        # print(json.dumps(policy_id_pairs, indent=4))
+        kv_policy_names = list(policy_id_pairs.get("Key Vault").keys())
+        expected_results_file = os.path.join(os.path.dirname(__file__), os.path.pardir, "files", "policy_id_pairs_kv.json")
+        expected_results = utils.read_json_file(expected_results_file)
+        # print(json.dumps(expected_results, indent=4))
+        # print(kv_policy_names)
+        for policy_name, policy_details in expected_results["Key Vault"].items():
+            self.assertTrue(policy_name in kv_policy_names)
+
+    def test_terraform_name_length(self):
+        tmp_terraform_template = TerraformTemplateNoParams(
+            policy_id_pairs=self.policy_id_pairs,
+            subscription_name="ThisSubscriptionNameIsTooDamnLong",
+            management_group="",
+            enforcement_mode=False,
+            category="Testing"
+        )
+        print(tmp_terraform_template.initiative_name)
+        print(len(tmp_terraform_template.initiative_name))
+        self.assertTrue(tmp_terraform_template.initiative_name == "ThisSubscriptionNameI_NP")
+        self.assertTrue(len(tmp_terraform_template.initiative_name) <= 24)
+    #
+    # def test_terraform_name_enforcement(self):
+    #     print()
+    #     # TODO: Test Enforcement in the name?
diff --git a/test/terraform/test_terraform_with_params.py b/test/terraform/test_terraform_with_params.py
new file mode 100644
index 0000000..e69de29