Skip to content

Terraform/Pulumi output serializer #9

@nielsweistra

Description

@nielsweistra

Ticket ID: CP-PB-009

Build Terraform and Pulumi serializers

Background

PolicyBuilder generates ARM JSON by default. For teams using Terraform or Pulumi we need serializers that convert the same policy objects to native HCL or Python IaC. This avoids duplication: the policy logic lives in PolicyBuilder and the serializer produces the target format.

Files

  • src/itl_policybuilder/serializers/terraform.py
  • src/itl_policybuilder/serializers/pulumi.py

Terraform serializer

"""
Serialize ARM policy objects to Terraform HCL.
"""
import json


def policy_to_terraform(policy: dict) -> str:
    """Convert ARM policyDefinition to azurerm_policy_definition HCL."""
    props = policy["properties"]
    name = policy["name"]
    rule_json = json.dumps(props["policyRule"], indent=2)

    return f'''resource "azurerm_policy_definition" "{name}" {{
  name         = "{name}"
  policy_type  = "Custom"
  mode         = "All"
  display_name = "{props["displayName"]}"
  description  = "{props.get("description", "")}"

  policy_rule = <<POLICY_RULE
{rule_json}
POLICY_RULE
}}
'''


def initiative_to_terraform(initiative: dict) -> str:
    """Convert ARM policySetDefinition to azurerm_policy_set_definition HCL."""
    props = initiative["properties"]
    name = initiative["name"]
    refs = props.get("policyDefinitions", [])

    policy_ref_blocks = ""
    for ref in refs:
        policy_def_id = ref["policyDefinitionId"]
        policy_ref_blocks += f'''
  policy_definition_reference {{
    policy_definition_id = "{policy_def_id}"
  }}'''

    return f'''resource "azurerm_policy_set_definition" "{name}" {{
  name         = "{name}"
  policy_type  = "Custom"
  display_name = "{props["displayName"]}"
  description  = "{props.get("description", "")}"
{policy_ref_blocks}
}}
'''


def assignment_to_terraform(assignment: dict, scope_var: str = "var.subscription_id") -> str:
    """Convert ARM policyAssignment to azurerm_subscription_policy_assignment HCL."""
    props = assignment["properties"]
    name = assignment["name"]

    return f'''resource "azurerm_subscription_policy_assignment" "{name}" {{
  name                 = "{name}"
  display_name         = "{props["displayName"]}"
  subscription_id      = "/subscriptions/${{{scope_var}}}"
  policy_definition_id = azurerm_policy_set_definition.{props.get("policyDefinitionId", name)}.id
  enforce              = true
}}
'''

Pulumi serializer

"""
Serialize ARM policy objects to Pulumi Python code.
"""
import json


def policy_to_pulumi(policy: dict) -> str:
    """Convert ARM policyDefinition to azure_native.authorization.PolicyDefinition."""
    props = policy["properties"]
    name = policy["name"]
    rule = json.dumps(props["policyRule"])

    return f'''import pulumi_azure_native as azure_native

{name.replace("-", "_")} = azure_native.authorization.PolicyDefinition(
    resource_name="{name}",
    policy_definition_name="{name}",
    policy_type="Custom",
    mode="All",
    display_name="{props["displayName"]}",
    policy_rule={rule},
)
'''


def initiative_to_pulumi(initiative: dict) -> str:
    """Convert ARM policySetDefinition to azure_native.authorization.PolicySetDefinition."""
    props = initiative["properties"]
    name = initiative["name"]
    refs = props.get("policyDefinitions", [])

    refs_python = json.dumps(refs, indent=4)

    return f'''import pulumi_azure_native as azure_native

{name.replace("-", "_")} = azure_native.authorization.PolicySetDefinition(
    resource_name="{name}",
    policy_set_definition_name="{name}",
    policy_type="Custom",
    display_name="{props["displayName"]}",
    policy_definitions={refs_python},
)
'''

CLI

# Generate Terraform for Defender initiative
python -m itl_policybuilder serialize \
    --format terraform \
    --initiative defender \
    --output output/terraform/

# Generate Pulumi for Foundation initiative
python -m itl_policybuilder serialize \
    --format pulumi \
    --initiative foundation \
    --output output/pulumi/

Output structure

output/terraform/
|-- enable-defender-virtualmachines.tf
|-- enable-defender-sqlservers.tf
|-- ...
`-- initiative-all-plans.tf

output/pulumi/
|-- enable-defender-virtualmachines.py
|-- ...
`-- initiative-all-plans.py

Acceptance Criteria

  • policy_to_terraform() generates valid HCL with heredoc for policy rule
  • initiative_to_terraform() generates policy_definition_reference blocks
  • assignment_to_terraform() references policy set definition by resource ID
  • policy_to_pulumi() generates valid Python with azure_native.authorization
  • initiative_to_pulumi() generates valid Python
  • CLI serialize command accepts --format terraform|pulumi
  • All Defender plan policies serialize without errors

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions