diff --git a/README.md b/README.md index 014f486..58fe6fc 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,53 @@ -# nsx_blueprints +# NSX Security Blueprints -Repo of NSX Security Blueprints for applications \ No newline at end of file +Python-based automation scripts for deploying NSX tags, security groups, and security policies for enterprise applications. + +## Supported NSX Versions +- NSX 4.x +- NSX 9.x + +## Structure + +``` +nsx_blueprints/ +├── shared/ # Shared utilities, NSX API client, constants +├── infra/ # Infrastructure policy (DNS, NTP, AD) — Infrastructure DFW category +├── epic/ # EPIC EHR application blueprint +├── horizon_view/ # Omnissa Horizon View (VDI) blueprint +└── _template/ # Generic blueprint template for new applications +``` + +## Blueprints + +| Blueprint | DFW Category | Description | +|---|---|---| +| [Infrastructure](infra/) | Infrastructure | Shared services — DNS, NTP, Active Directory security groups and policy | +| [EPIC](epic/) | Application | EPIC Electronic Health Records — tags, security groups, and DFW policy | +| [Horizon View](horizon_view/) | Application | Omnissa Horizon View VDI — tags, security groups, and DFW policy | +| [Template](\_template/) | Application | Generic starting point for new application blueprints | + +## Conventions + +### Tag Naming +`|` — e.g. `EPIC|Web`, `EPIC|App`, `EPIC|DB` + +### Security Group Naming +`SG--` — e.g. `SG-EPIC-Web`, `SG-EPIC-DB` + +### Policy / Rule Naming +- Policy: `Policy-` +- Rules: descriptive action-first names, e.g. `Allow-HTTPS-Web`, `Deny-All` + +## Prerequisites +- Python 3.10+ +- `requests` library +- NSX Manager credentials with appropriate RBAC permissions + +## Usage + +Each blueprint directory contains its own `README.md` with application-specific setup and run instructions. + +```bash +pip install -r requirements.txt +python epic/deploy.py --nsx-manager --username --password +``` diff --git a/_template/README.md b/_template/README.md new file mode 100644 index 0000000..0f8b778 --- /dev/null +++ b/_template/README.md @@ -0,0 +1,82 @@ +# [AppName] — NSX Security Blueprint Template + +Generic template for creating NSX tags, security groups, and a Distributed Firewall (DFW) policy for a new application. Copy this directory and replace `[AppName]` throughout. + +## Application Overview + +_Describe the application, its role in the environment, and the tiers that need to be segmented._ + +## Tiers & Tags + +| Tag | Scope | Purpose | +|---|---|---| +| `[AppName]\|Web` | `AppName` | Web / presentation tier | +| `[AppName]\|App` | `AppName` | Application tier | +| `[AppName]\|DB` | `AppName` | Database tier | + +_Add or remove tiers as needed._ + +## Security Groups + +| Group | Membership Criteria | Purpose | +|---|---|---| +| `SG-[AppName]-Web` | Tag: `[AppName]\|Web` | Web-tier VMs | +| `SG-[AppName]-App` | Tag: `[AppName]\|App` | Application-tier VMs | +| `SG-[AppName]-DB` | Tag: `[AppName]\|DB` | Database-tier VMs | +| `SG-[AppName]-All` | All groups above | All app workloads — used in lockdown rule | + +## DFW Policy: `Policy-[AppName]` + +| # | Rule Name | Source | Destination | Service | Action | Applied To | +|---|---|---|---|---|---|---| +| 1 | Allow-HTTPS-Web | Any | SG-[AppName]-Web | HTTPS (443) | Allow | DFW | +| 2 | Allow-MSSQL-DB | SG-[AppName]-App | SG-[AppName]-DB | MSSQL (1433) | Allow | DFW | +| 3 | Allow-SSH-DB | SG-[AppName]-Mgmt | SG-[AppName]-DB | SSH (22) | Allow | DFW | +| 4 | Lockdown-[AppName] | Any | SG-[AppName]-All | Any | Deny | SG-[AppName]-All | + +> **Lockdown rule** (last rule): set **Applied To** to `SG-[AppName]-All` so the deny is scoped to this application's workloads only and does not affect other traffic on the DFW. + +## Infrastructure Policy + +An infrastructure-category policy (`Policy-Infra-[AppName]`) should be used for cross-cutting concerns such as: +- DNS / NTP access from app workloads +- AD/LDAP access +- Monitoring and backup agent traffic +- Syslog forwarding + +_This policy lives under the **Infrastructure** DFW category and is documented separately._ + +## Files + +``` +_template/ +├── README.md +├── deploy.py # Main entry point — orchestrates tags, groups, policy +├── tags.py # Tag creation logic +├── security_groups.py # Security group creation logic +└── policy.py # DFW policy and rule creation logic +``` + +## Usage + +```bash +python /deploy.py \ + --nsx-manager \ + --username \ + --password +``` + +### Optional flags +| Flag | Default | Description | +|---|---|---| +| `--dry-run` | False | Print planned changes without applying them | +| `--verify-ssl` | True | Disable with `--no-verify-ssl` for lab environments | + +## Customization Checklist + +- [ ] Replace all `[AppName]` placeholders +- [ ] Add/remove tiers to match actual application architecture +- [ ] Adjust services/ports in the policy table +- [ ] Define which group gets SSH admin access (Rule 3 source) +- [ ] Review lockdown rule — confirm Applied To is correct +- [ ] Add infrastructure policy rules as needed diff --git a/epic/README.md b/epic/README.md new file mode 100644 index 0000000..03c4ff3 --- /dev/null +++ b/epic/README.md @@ -0,0 +1,64 @@ +# EPIC EHR — NSX Security Blueprint + +Automates NSX tags, security groups, and Distributed Firewall (DFW) policy for an EPIC Electronic Health Records deployment. + +## Application Overview + +EPIC is a multi-tier healthcare EHR platform. This blueprint segments the EPIC workloads into distinct security zones using NSX micro-segmentation. + +## Tiers & Tags + +| Tag | Scope | Purpose | +|---|---|---| +| `EPIC\|Web` | `AppName` | EPIC web/presentation tier | +| `EPIC\|App` | `AppName` | EPIC application tier | +| `EPIC\|DB` | `AppName` | EPIC database tier | +| `EPIC\|Mgmt` | `AppName` | EPIC management/Hyperspace servers | + +## Security Groups + +| Group | Membership Criteria | Purpose | +|---|---|---| +| `SG-EPIC-Web` | Tag: `EPIC\|Web` | Web-tier VMs | +| `SG-EPIC-App` | Tag: `EPIC\|App` | Application-tier VMs | +| `SG-EPIC-DB` | Tag: `EPIC\|DB` | Database-tier VMs | +| `SG-EPIC-Mgmt` | Tag: `EPIC\|Mgmt` | Management/Hyperspace VMs | +| `SG-EPIC-All` | Tag: `EPIC\|*` (all above) | All EPIC workloads — used in lockdown rules | + +## DFW Policy: `Policy-EPIC` + +| # | Rule Name | Source | Destination | Service | Action | +|---|---|---|---|---|---| +| 1 | Allow-HTTPS-Web | Any | SG-EPIC-Web | HTTPS (443) | Allow | +| 2 | Allow-App-to-DB | SG-EPIC-App | SG-EPIC-DB | MSSQL (1433) | Allow | +| 3 | Allow-SSH-DB | SG-EPIC-Mgmt | SG-EPIC-DB | SSH (22) | Allow | +| 4 | Allow-Web-to-App | SG-EPIC-Web | SG-EPIC-App | HTTP/HTTPS | Allow | +| 5 | Lockdown-EPIC | Any | SG-EPIC-All | Any | Deny | + +> Rule 5 (Lockdown) has **Applied To** set to `SG-EPIC-All` to scope the deny to EPIC workloads only. + +## Files + +``` +epic/ +├── README.md +├── deploy.py # Main entry point — orchestrates tags, groups, policy +├── tags.py # Tag creation logic +├── security_groups.py # Security group creation logic +└── policy.py # DFW policy and rule creation logic +``` + +## Usage + +```bash +python epic/deploy.py \ + --nsx-manager \ + --username \ + --password +``` + +### Optional flags +| Flag | Default | Description | +|---|---|---| +| `--dry-run` | False | Print planned changes without applying them | +| `--verify-ssl` | True | Disable with `--no-verify-ssl` for lab environments | diff --git a/horizon_view/README.md b/horizon_view/README.md new file mode 100644 index 0000000..c8171fb --- /dev/null +++ b/horizon_view/README.md @@ -0,0 +1,74 @@ +# Omnissa Horizon View — NSX Security Blueprint + +Automates NSX tags, security groups, and Distributed Firewall (DFW) policy for an Omnissa Horizon View (formerly VMware Horizon) VDI deployment. + +## Application Overview + +Omnissa Horizon View provides virtual desktop and published application delivery. This blueprint segments the Horizon infrastructure and desktop pools using NSX micro-segmentation. + +## Tiers & Tags + +| Tag | Scope | Purpose | +|---|---|---| +| `Horizon\|ConnectionServer` | `AppName` | Horizon Connection Servers | +| `Horizon\|EnrollmentServer` | `AppName` | Enrollment/True SSO servers | +| `Horizon\|UAG` | `AppName` | Unified Access Gateways (external access) | +| `Horizon\|Composer` | `AppName` | Horizon Composer / Instant Clone domain join servers | +| `Horizon\|VDI` | `AppName` | Virtual desktop pool VMs | +| `Horizon\|RDSH` | `AppName` | Published application / RDSH session hosts | +| `Horizon\|Mgmt` | `AppName` | Horizon management / admin jump hosts | + +## Security Groups + +| Group | Membership Criteria | Purpose | +|---|---|---| +| `SG-Horizon-ConnectionServer` | Tag: `Horizon\|ConnectionServer` | Connection Server VMs | +| `SG-Horizon-EnrollmentServer` | Tag: `Horizon\|EnrollmentServer` | Enrollment Server VMs | +| `SG-Horizon-UAG` | Tag: `Horizon\|UAG` | Unified Access Gateway VMs | +| `SG-Horizon-Composer` | Tag: `Horizon\|Composer` | Composer / IC join VMs | +| `SG-Horizon-VDI` | Tag: `Horizon\|VDI` | Virtual desktop VMs | +| `SG-Horizon-RDSH` | Tag: `Horizon\|RDSH` | RDSH session host VMs | +| `SG-Horizon-Mgmt` | Tag: `Horizon\|Mgmt` | Management / admin VMs | +| `SG-Horizon-All` | All groups above | All Horizon workloads — used in lockdown rules | + +## DFW Policy: `Policy-Horizon` + +| # | Rule Name | Source | Destination | Service | Action | +|---|---|---|---|---|---| +| 1 | Allow-HTTPS-UAG | Any | SG-Horizon-UAG | HTTPS (443) | Allow | +| 2 | Allow-Blast-UAG | Any | SG-Horizon-UAG | Blast (8443, 443) | Allow | +| 3 | Allow-UAG-to-CS | SG-Horizon-UAG | SG-Horizon-ConnectionServer | HTTPS (443) | Allow | +| 4 | Allow-Client-to-CS | Any | SG-Horizon-ConnectionServer | HTTPS (443), Blast (8443) | Allow | +| 5 | Allow-CS-to-VDI | SG-Horizon-ConnectionServer | SG-Horizon-VDI | Blast (22443), PCoIP (4172) | Allow | +| 6 | Allow-CS-to-RDSH | SG-Horizon-ConnectionServer | SG-Horizon-RDSH | Blast (22443), RDP (3389) | Allow | +| 7 | Allow-SSH-Mgmt | SG-Horizon-Mgmt | SG-Horizon-All | SSH (22) | Allow | +| 8 | Allow-Enrollment-CS | SG-Horizon-ConnectionServer | SG-Horizon-EnrollmentServer | HTTPS (443) | Allow | +| 9 | Lockdown-Horizon | Any | SG-Horizon-All | Any | Deny | + +> Rule 9 (Lockdown) has **Applied To** set to `SG-Horizon-All` to scope the deny to Horizon workloads only. + +## Files + +``` +horizon_view/ +├── README.md +├── deploy.py # Main entry point — orchestrates tags, groups, policy +├── tags.py # Tag creation logic +├── security_groups.py # Security group creation logic +└── policy.py # DFW policy and rule creation logic +``` + +## Usage + +```bash +python horizon_view/deploy.py \ + --nsx-manager \ + --username \ + --password +``` + +### Optional flags +| Flag | Default | Description | +|---|---|---| +| `--dry-run` | False | Print planned changes without applying them | +| `--verify-ssl` | True | Disable with `--no-verify-ssl` for lab environments | diff --git a/infra/README.md b/infra/README.md new file mode 100644 index 0000000..c0fe597 --- /dev/null +++ b/infra/README.md @@ -0,0 +1,94 @@ +# Infrastructure — NSX Security Blueprint + +Automates NSX security groups and DFW policies for shared infrastructure services used across all application blueprints. Lives under the NSX **Infrastructure** DFW category. + +## Scope + +Infrastructure groups and policies cover cross-cutting services that every application needs to reach — DNS, NTP, Active Directory, monitoring, backup, syslog, etc. By placing these rules in the **Infrastructure** category they evaluate before application-level policies and remain centrally managed. + +## Tag Convention + +| Scope | Value | Purpose | +|---|---|---| +| `Infra` | `DNS Server` | Authoritative / recursive DNS servers | +| `Infra` | `NTP Server` | NTP time sources | +| `Infra` | `Domain Controller` | Active Directory Domain Controllers | + +Apply these tags to VMs in NSX (or via vCenter tag sync) so that new servers are automatically picked up by the dynamic group membership criteria. + +## Security Groups + +Managed by `security_groups.py`. Each group uses a **union** of three member sources so the groups can be built up incrementally: + +| Group | Dynamic (Tag) | Static IPs | Explicit VMs | +|---|---|---|---| +| `SG-Infra-DNS-Servers` | `Infra:DNS Server` | ✓ | ✓ | +| `SG-Infra-NTP-Servers` | `Infra:NTP Server` | ✓ | ✓ | +| `SG-Infra-Domain-Controllers` | `Infra:Domain Controller` | ✓ | ✓ | + +> The `GROUP_PREFIX` (`SG-Infra-`) is configurable at the top of each script. + +## DFW Policy: `Policy-Infra` + +_Defined in `policy.py` — coming soon._ + +Planned rules (Infrastructure category, evaluated before application policies): + +| # | Rule Name | Source | Destination | Service | Action | +|---|---|---|---|---|---| +| 1 | Allow-DNS-UDP | Any | SG-Infra-DNS-Servers | DNS/UDP (53) | Allow | +| 2 | Allow-DNS-TCP | Any | SG-Infra-DNS-Servers | DNS/TCP (53) | Allow | +| 3 | Allow-NTP | Any | SG-Infra-NTP-Servers | NTP/UDP (123) | Allow | +| 4 | Allow-LDAP | Any | SG-Infra-Domain-Controllers | LDAP (389), LDAPS (636) | Allow | +| 5 | Allow-Kerberos | Any | SG-Infra-Domain-Controllers | Kerberos (88/UDP+TCP) | Allow | +| 6 | Allow-SMB-DC | Any | SG-Infra-Domain-Controllers | SMB (445) | Allow | + +## Files + +``` +infra/ +├── README.md +├── security_groups.py # Creates/updates infrastructure security groups +└── policy.py # DFW policy and rules (coming soon) +``` + +## Usage + +### Prerequisites +```bash +pip install requests +``` + +### Populate member data +Open `security_groups.py` and edit the `GROUPS` dictionary: +- `ip_addresses` — static IPs or CIDRs for servers not yet tagged +- `vm_ext_ids` — vCenter MoRef IDs (e.g. `"vm-1042"`) for explicit VM membership +- `tag_value` — already set; drives dynamic membership via NSX tag conditions + +### Dry run (no changes) +```bash +python infra/security_groups.py \ + --nsx-manager \ + --username admin \ + --password \ + --dry-run +``` + +### Apply +```bash +python infra/security_groups.py \ + --nsx-manager \ + --username admin \ + --password +``` + +### Lab / self-signed cert environments +Add `--no-verify-ssl` to either command above. + +## Customization + +| Variable | Location | Default | Purpose | +|---|---|---|---| +| `GROUP_PREFIX` | top of `security_groups.py` | `SG-Infra-` | Prefix on all group names and IDs | +| `TAG_SCOPE` | top of `security_groups.py` | `Infra` | NSX tag scope for infrastructure tags | +| `DOMAIN` | top of `security_groups.py` | `default` | NSX Policy domain | diff --git a/infra/security_groups.py b/infra/security_groups.py new file mode 100644 index 0000000..71c122e --- /dev/null +++ b/infra/security_groups.py @@ -0,0 +1,266 @@ +#!/usr/bin/env python3 +""" +NSX Infrastructure Security Groups +Creates or updates NSX security groups for shared infrastructure servers. + +Edit the CONFIGURATION section below before running, then: + python infra/security_groups.py --nsx-manager --username admin --password + +Add --dry-run to preview the API payloads without making any changes. +""" + +import argparse +import json +import sys +import urllib3 +import requests +from typing import Any + +urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) + +# ── CONFIGURATION ────────────────────────────────────────────────────────────── + +# Prefix applied to every group's display name and ID. +# Change this to match your environment's naming standard. +GROUP_PREFIX = "SG-Infra-" + +# NSX tag scope used for all infrastructure server tags. +# Tag format: scope=TAG_SCOPE, value= e.g. scope="Infra", value="DNS Server" +TAG_SCOPE = "Infra" + +# NSX Policy domain. "default" is correct for most environments. +DOMAIN = "default" + +# Group definitions. +# +# Each entry key becomes part of the group ID (URL-safe, no spaces). +# Fields: +# display_name : suffix appended to GROUP_PREFIX → "SG-Infra-DNS-Servers" +# tag_value : tag value (under TAG_SCOPE) that dynamically adds matching VMs +# ip_addresses : static IPs or CIDR ranges to include (list of strings) +# vm_ext_ids : vCenter VM External IDs / MoRefs to include (list of strings) +# Find these in vCenter (e.g. "vm-1042") or via the NSX VM inventory API. +# +# Members from all three sources are OR'd together in the group expression. + +GROUPS: dict[str, dict[str, Any]] = { + "DNS": { + "display_name": "DNS-Servers", + "tag_value": "DNS Server", + "ip_addresses": [ + # "192.168.10.53", + # "192.168.10.54", + ], + "vm_ext_ids": [ + # "vm-1001", + ], + }, + "NTP": { + "display_name": "NTP-Servers", + "tag_value": "NTP Server", + "ip_addresses": [ + # "192.168.10.123", + # "192.168.10.124", + ], + "vm_ext_ids": [ + # "vm-1002", + ], + }, + "DC": { + "display_name": "Domain-Controllers", + "tag_value": "Domain Controller", + "ip_addresses": [ + # "192.168.10.10", + # "192.168.10.11", + ], + "vm_ext_ids": [ + # "vm-1003", + # "vm-1004", + ], + }, +} + +# ── END CONFIGURATION ────────────────────────────────────────────────────────── + + +def group_id(key: str) -> str: + """Derive a stable, URL-safe NSX group ID from the config key and prefix.""" + prefix = GROUP_PREFIX.rstrip("-").replace(" ", "-") + return f"{prefix}-{key}" + + +def build_expressions(cfg: dict[str, Any]) -> list[dict[str, Any]]: + """ + Build the NSX Policy expression list for a group. + + Each source type (IPs, tag-matched VMs, explicit VMs) is OR'd together. + Tag matching uses a NestedExpression to enforce both scope AND value so + that tags from other scopes with the same value are not inadvertently included. + + Expression order matters in the NSX API — the ConjunctionOperator must sit + between two non-operator expressions; the first expression must never be + a ConjunctionOperator. + """ + expressions: list[dict[str, Any]] = [] + + def _or(): + """Append an OR separator between expression blocks.""" + if expressions: + expressions.append({ + "resource_type": "ConjunctionOperator", + "conjunction_operator": "OR", + }) + + # ── Static IP addresses / CIDRs ────────────────────────────────────────── + ip_list = [ip for ip in cfg.get("ip_addresses", []) if ip] + if ip_list: + _or() + expressions.append({ + "resource_type": "IPAddressExpression", + "ip_addresses": ip_list, + }) + + # ── Dynamic tag membership (scope=TAG_SCOPE AND value=tag_value) ───────── + # NestedExpression combines two Condition objects with AND: + # Condition 1 — matches the tag SCOPE field (scope_operator triggers this) + # Condition 2 — matches the tag VALUE field + tag_value = cfg.get("tag_value", "").strip() + if tag_value: + _or() + expressions.append({ + "resource_type": "NestedExpression", + "expressions": [ + { + "resource_type": "Condition", + "member_type": "VirtualMachine", + "key": "Tag", + "operator": "EQUALS", + "value": TAG_SCOPE, + # scope_operator tells NSX to match this Condition against + # the tag's scope field instead of the tag's value field. + "scope_operator": "EQUALS", + }, + { + "resource_type": "ConjunctionOperator", + "conjunction_operator": "AND", + }, + { + "resource_type": "Condition", + "member_type": "VirtualMachine", + "key": "Tag", + "operator": "EQUALS", + "value": tag_value, + }, + ], + }) + + # ── Explicit VMs by vCenter External ID / MoRef ────────────────────────── + ext_ids = [v for v in cfg.get("vm_ext_ids", []) if v] + if ext_ids: + _or() + expressions.append({ + "resource_type": "ExternalIDExpression", + "member_type": "VirtualMachine", + "external_ids": ext_ids, + }) + + return expressions + + +def build_group_payload(key: str, cfg: dict[str, Any]) -> dict[str, Any]: + """Assemble the full NSX Policy group PATCH body.""" + expressions = build_expressions(cfg) + if not expressions: + raise ValueError( + f"Group '{key}' has no members defined " + "(add ip_addresses, tag_value, or vm_ext_ids in CONFIGURATION)." + ) + + return { + "id": group_id(key), + "display_name": f"{GROUP_PREFIX}{cfg['display_name']}", + "description": ( + f"Infrastructure {cfg['display_name'].replace('-', ' ')} — " + f"managed by nsx_blueprints/infra/security_groups.py" + ), + "expression": expressions, + } + + +def patch_group( + session: requests.Session, + base_url: str, + key: str, + payload: dict[str, Any], + dry_run: bool, +) -> None: + url = ( + f"{base_url}/policy/api/v1/infra/domains/{DOMAIN}/groups/{group_id(key)}" + ) + + print(f"\n{'[DRY RUN] ' if dry_run else ''}Group: {payload['display_name']}") + print(f" URL : PATCH {url}") + print(f" Body: {json.dumps(payload, indent=4)}") + + if dry_run: + return + + resp = session.patch(url, json=payload) + if resp.status_code in (200, 201): + print(f" ✓ {resp.status_code} — group created/updated successfully.") + else: + print(f" ✗ {resp.status_code} — {resp.text}", file=sys.stderr) + resp.raise_for_status() + + +def build_session(username: str, password: str, verify_ssl: bool) -> requests.Session: + s = requests.Session() + s.auth = (username, password) + s.verify = verify_ssl + s.headers.update({"Content-Type": "application/json"}) + return s + + +def parse_args() -> argparse.Namespace: + p = argparse.ArgumentParser( + description="Create/update NSX infrastructure security groups." + ) + p.add_argument("--nsx-manager", required=True, metavar="HOST", + help="NSX Manager IP or FQDN (e.g. 192.168.1.10)") + p.add_argument("--username", required=True, + help="NSX Manager username") + p.add_argument("--password", required=True, + help="NSX Manager password") + p.add_argument("--dry-run", action="store_true", + help="Print payloads without calling the API") + p.add_argument("--no-verify-ssl", action="store_true", + help="Disable SSL certificate verification (useful in lab environments)") + return p.parse_args() + + +def main() -> None: + args = parse_args() + base_url = f"https://{args.nsx_manager.rstrip('/')}" + session = build_session(args.username, args.password, not args.no_verify_ssl) + + errors: list[str] = [] + for key, cfg in GROUPS.items(): + try: + payload = build_group_payload(key, cfg) + patch_group(session, base_url, key, payload, args.dry_run) + except ValueError as exc: + print(f" ⚠ Skipping '{key}': {exc}") + except requests.HTTPError as exc: + errors.append(f"{key}: {exc}") + + if errors: + print("\nErrors encountered:", file=sys.stderr) + for err in errors: + print(f" - {err}", file=sys.stderr) + sys.exit(1) + + print("\nDone." if not args.dry_run else "\nDry run complete — no changes were made.") + + +if __name__ == "__main__": + main() diff --git a/shared/README.md b/shared/README.md new file mode 100644 index 0000000..39b9f56 --- /dev/null +++ b/shared/README.md @@ -0,0 +1,14 @@ +# Shared Utilities + +Common modules used across all NSX blueprints. + +## Planned Modules + +| File | Purpose | +|---|---| +| `nsx_client.py` | Authenticated `requests` wrapper for NSX Manager REST API | +| `constants.py` | Shared service definitions, port numbers, category names | +| `models.py` | Dataclasses for Tag, SecurityGroup, Rule, Policy | +| `utils.py` | Helpers: retry logic, response validation, ID resolution | + +These modules are imported by each blueprint's `tags.py`, `security_groups.py`, and `policy.py`.