400 lines
14 KiB
YAML
400 lines
14 KiB
YAML
---
|
|
# ansible/playbooks/day1_configure_netbox_catalog.yml
|
|
#
|
|
# Creates the tag taxonomy and custom fields in NetBox for service catalog
|
|
# documentation. Run once (idempotent — uses name-based checks).
|
|
#
|
|
# Usage (from ansible/ directory):
|
|
# ansible-playbook playbooks/day1_configure_netbox_catalog.yml
|
|
#
|
|
# Requires:
|
|
# - vault_netbox_token in Ansible Vault
|
|
# - netbox reachable at http://fire-station.local.mk-labs.cloud
|
|
|
|
- name: Configure NetBox service catalog taxonomy
|
|
hosts: localhost
|
|
gather_facts: false
|
|
|
|
vars_files:
|
|
- "{{ playbook_dir }}/../group_vars/all/vault"
|
|
|
|
vars:
|
|
netbox_url: "http://fire-station.local.mk-labs.cloud"
|
|
netbox_token: "{{ vault_netbox_token }}"
|
|
netbox_api: "{{ netbox_url }}/api"
|
|
headers:
|
|
Authorization: "Token {{ netbox_token }}"
|
|
Content-Type: "application/json"
|
|
Accept: "application/json"
|
|
|
|
# ── Tag definitions ───────────────────────────────────────────────────
|
|
tags:
|
|
# Infrastructure type
|
|
- name: k8s
|
|
slug: k8s
|
|
color: "2196f3" # blue
|
|
description: "Workload running in the fastpass Kubernetes cluster"
|
|
- name: vm
|
|
slug: vm
|
|
color: "4caf50" # green
|
|
description: "Traditional VM or LXC on Proxmox"
|
|
# Service tier
|
|
- name: platform
|
|
slug: platform
|
|
color: "9c27b0" # purple
|
|
description: "Platform/infrastructure service (not user-facing)"
|
|
- name: application
|
|
slug: application
|
|
color: "ff9800" # orange
|
|
description: "User-facing application workload"
|
|
# Service categories
|
|
- name: monitoring
|
|
slug: monitoring
|
|
color: "607d8b" # grey
|
|
description: "Metrics, logging, alerting"
|
|
- name: auth
|
|
slug: auth
|
|
color: "607d8b"
|
|
description: "Authentication and SSO"
|
|
- name: gitops
|
|
slug: gitops
|
|
color: "607d8b"
|
|
description: "GitOps and CI/CD"
|
|
- name: dashboard
|
|
slug: dashboard
|
|
color: "607d8b"
|
|
description: "Dashboard and portal services"
|
|
- name: storage
|
|
slug: storage
|
|
color: "607d8b"
|
|
description: "Storage and file services"
|
|
- name: dns
|
|
slug: dns
|
|
color: "607d8b"
|
|
description: "DNS and name resolution"
|
|
- name: automation
|
|
slug: automation
|
|
color: "607d8b"
|
|
description: "Automation and orchestration"
|
|
- name: networking
|
|
slug: networking
|
|
color: "607d8b"
|
|
description: "Network infrastructure services"
|
|
- name: inference
|
|
slug: inference
|
|
color: "607d8b"
|
|
description: "AI/ML inference workloads"
|
|
|
|
# ── Custom field definitions ──────────────────────────────────────────
|
|
# object_types use app_label.model format
|
|
custom_fields:
|
|
- name: hostnames
|
|
label: Hostnames
|
|
type: longtext
|
|
object_types:
|
|
- ipam.ipaddress
|
|
- virtualization.virtualmachine
|
|
description: "All DNS names that resolve to this service (comma-separated)"
|
|
ui_visible: always
|
|
ui_editable: yes
|
|
|
|
- name: namespace
|
|
label: Namespace
|
|
type: text
|
|
object_types:
|
|
- ipam.ipaddress
|
|
- virtualization.virtualmachine
|
|
description: "Kubernetes namespace (blank for VM-based services)"
|
|
ui_visible: always
|
|
ui_editable: yes
|
|
|
|
- name: managed_by
|
|
label: Managed By
|
|
type: select
|
|
object_types:
|
|
- ipam.ipaddress
|
|
- virtualization.virtualmachine
|
|
description: "How this service is managed"
|
|
choices:
|
|
- ArgoCD
|
|
- Ansible
|
|
- Manual
|
|
ui_visible: always
|
|
ui_editable: yes
|
|
|
|
- name: thematic_name
|
|
label: Thematic Name
|
|
type: text
|
|
object_types:
|
|
- ipam.ipaddress
|
|
- virtualization.virtualmachine
|
|
description: "Disney/Magic Kingdom thematic hostname for this service"
|
|
ui_visible: always
|
|
ui_editable: yes
|
|
|
|
tasks:
|
|
|
|
# ── Tags ─────────────────────────────────────────────────────────────
|
|
|
|
- name: Fetch existing tags
|
|
ansible.builtin.uri:
|
|
url: "{{ netbox_api }}/extras/tags/?limit=200"
|
|
method: GET
|
|
headers: "{{ headers }}"
|
|
return_content: true
|
|
status_code: 200
|
|
register: existing_tags_response
|
|
|
|
- name: Set existing tag slugs fact
|
|
ansible.builtin.set_fact:
|
|
existing_tag_slugs: "{{ existing_tags_response.json.results | map(attribute='slug') | list }}"
|
|
|
|
- name: Create tags
|
|
ansible.builtin.uri:
|
|
url: "{{ netbox_api }}/extras/tags/"
|
|
method: POST
|
|
headers: "{{ headers }}"
|
|
body_format: json
|
|
body:
|
|
name: "{{ item.name }}"
|
|
slug: "{{ item.slug }}"
|
|
color: "{{ item.color }}"
|
|
description: "{{ item.description }}"
|
|
status_code: 201
|
|
loop: "{{ tags }}"
|
|
when: item.slug not in existing_tag_slugs
|
|
register: tag_creation
|
|
changed_when: tag_creation.status == 201
|
|
|
|
# ── Custom Fields ─────────────────────────────────────────────────────
|
|
|
|
- name: Fetch existing custom fields
|
|
ansible.builtin.uri:
|
|
url: "{{ netbox_api }}/extras/custom-fields/?limit=200"
|
|
method: GET
|
|
headers: "{{ headers }}"
|
|
return_content: true
|
|
status_code: 200
|
|
register: existing_cf_response
|
|
|
|
- name: Set existing custom field names fact
|
|
ansible.builtin.set_fact:
|
|
existing_cf_names: "{{ existing_cf_response.json.results | map(attribute='name') | list }}"
|
|
|
|
- name: Create custom field — hostnames
|
|
ansible.builtin.uri:
|
|
url: "{{ netbox_api }}/extras/custom-fields/"
|
|
method: POST
|
|
headers: "{{ headers }}"
|
|
body_format: json
|
|
body:
|
|
name: hostnames
|
|
label: Hostnames
|
|
type: longtext
|
|
object_types: "{{ custom_fields[0].object_types }}"
|
|
description: "{{ custom_fields[0].description }}"
|
|
ui_visible: always
|
|
ui_editable: yes
|
|
status_code: 201
|
|
when: "'hostnames' not in existing_cf_names"
|
|
register: cf_hostnames
|
|
changed_when: cf_hostnames.status == 201
|
|
|
|
- name: Create custom field — namespace
|
|
ansible.builtin.uri:
|
|
url: "{{ netbox_api }}/extras/custom-fields/"
|
|
method: POST
|
|
headers: "{{ headers }}"
|
|
body_format: json
|
|
body:
|
|
name: namespace
|
|
label: Namespace
|
|
type: text
|
|
object_types: "{{ custom_fields[1].object_types }}"
|
|
description: "{{ custom_fields[1].description }}"
|
|
ui_visible: always
|
|
ui_editable: yes
|
|
status_code: 201
|
|
when: "'namespace' not in existing_cf_names"
|
|
register: cf_namespace
|
|
changed_when: cf_namespace.status == 201
|
|
|
|
- name: Create choice set for managed_by field
|
|
ansible.builtin.uri:
|
|
url: "{{ netbox_api }}/extras/custom-field-choice-sets/"
|
|
method: POST
|
|
headers: "{{ headers }}"
|
|
body_format: json
|
|
body:
|
|
name: managed-by-choices
|
|
extra_choices:
|
|
- - ArgoCD
|
|
- ArgoCD
|
|
- - Ansible
|
|
- Ansible
|
|
- - Manual
|
|
- Manual
|
|
status_code: [201, 400]
|
|
register: choice_set
|
|
changed_when: choice_set.status == 201
|
|
|
|
- name: Fetch choice set ID
|
|
ansible.builtin.uri:
|
|
url: "{{ netbox_api }}/extras/custom-field-choice-sets/?name=managed-by-choices"
|
|
method: GET
|
|
headers: "{{ headers }}"
|
|
return_content: true
|
|
status_code: 200
|
|
register: choice_set_response
|
|
|
|
- name: Create custom field — managed_by
|
|
ansible.builtin.uri:
|
|
url: "{{ netbox_api }}/extras/custom-fields/"
|
|
method: POST
|
|
headers: "{{ headers }}"
|
|
body_format: json
|
|
body:
|
|
name: managed_by
|
|
label: Managed By
|
|
type: select
|
|
object_types: "{{ custom_fields[2].object_types }}"
|
|
description: "{{ custom_fields[2].description }}"
|
|
choice_set: "{{ choice_set_response.json.results[0].id }}"
|
|
ui_visible: always
|
|
ui_editable: yes
|
|
status_code: 201
|
|
when: "'managed_by' not in existing_cf_names"
|
|
register: cf_managed_by
|
|
changed_when: cf_managed_by.status == 201
|
|
|
|
- name: Create custom field — thematic_name
|
|
ansible.builtin.uri:
|
|
url: "{{ netbox_api }}/extras/custom-fields/"
|
|
method: POST
|
|
headers: "{{ headers }}"
|
|
body_format: json
|
|
body:
|
|
name: thematic_name
|
|
label: Thematic Name
|
|
type: text
|
|
object_types: "{{ custom_fields[3].object_types }}"
|
|
description: "{{ custom_fields[3].description }}"
|
|
ui_visible: always
|
|
ui_editable: yes
|
|
status_code: 201
|
|
when: "'thematic_name' not in existing_cf_names"
|
|
register: cf_thematic_name
|
|
changed_when: cf_thematic_name.status == 201
|
|
|
|
# ── Provisioning Pipeline Fields ──────────────────────────────────────
|
|
# These fields drive the NetBox → n8n → Terraform pipeline.
|
|
# proxmox_datastore already exists — PATCH it to add utilidor choice.
|
|
# data_disk_enabled and data_disk_size_gb are new POSTs.
|
|
|
|
- name: Fetch proxmox_datastore field ID
|
|
ansible.builtin.uri:
|
|
url: "{{ netbox_api }}/extras/custom-fields/?name=proxmox_datastore"
|
|
method: GET
|
|
headers: "{{ headers }}"
|
|
return_content: true
|
|
status_code: 200
|
|
register: proxmox_datastore_cf_response
|
|
|
|
- name: Fetch proxmox-datastore-choices choice set ID
|
|
ansible.builtin.uri:
|
|
url: "{{ netbox_api }}/extras/custom-field-choice-sets/?name=proxmox-datastore-choices"
|
|
method: GET
|
|
headers: "{{ headers }}"
|
|
return_content: true
|
|
status_code: 200
|
|
register: datastore_choice_set_response
|
|
|
|
- name: Create proxmox-datastore-choices choice set if missing
|
|
ansible.builtin.uri:
|
|
url: "{{ netbox_api }}/extras/custom-field-choice-sets/"
|
|
method: POST
|
|
headers: "{{ headers }}"
|
|
body_format: json
|
|
body:
|
|
name: proxmox-datastore-choices
|
|
extra_choices:
|
|
- - liberty-tree
|
|
- liberty-tree
|
|
- - utilidor
|
|
- utilidor
|
|
status_code: [201, 400]
|
|
register: datastore_choice_set_create
|
|
changed_when: datastore_choice_set_create.status == 201
|
|
when: datastore_choice_set_response.json.count == 0
|
|
|
|
- name: Re-fetch proxmox-datastore-choices choice set ID after possible creation
|
|
ansible.builtin.uri:
|
|
url: "{{ netbox_api }}/extras/custom-field-choice-sets/?name=proxmox-datastore-choices"
|
|
method: GET
|
|
headers: "{{ headers }}"
|
|
return_content: true
|
|
status_code: 200
|
|
register: datastore_choice_set_response
|
|
|
|
- name: Patch proxmox_datastore field to use choice set with utilidor
|
|
ansible.builtin.uri:
|
|
url: "{{ netbox_api }}/extras/custom-fields/{{ proxmox_datastore_cf_response.json.results[0].id }}/"
|
|
method: PATCH
|
|
headers: "{{ headers }}"
|
|
body_format: json
|
|
body:
|
|
choice_set: "{{ datastore_choice_set_response.json.results[0].id }}"
|
|
status_code: 200
|
|
when: proxmox_datastore_cf_response.json.count > 0
|
|
register: cf_datastore_patch
|
|
changed_when: cf_datastore_patch.status == 200
|
|
|
|
- name: Create custom field — data_disk_enabled
|
|
ansible.builtin.uri:
|
|
url: "{{ netbox_api }}/extras/custom-fields/"
|
|
method: POST
|
|
headers: "{{ headers }}"
|
|
body_format: json
|
|
body:
|
|
name: data_disk_enabled
|
|
label: Data Disk Enabled
|
|
type: boolean
|
|
object_types:
|
|
- virtualization.virtualmachine
|
|
description: "When true, Terraform provisions a second disk for application data storage."
|
|
default: false
|
|
ui_visible: always
|
|
ui_editable: yes
|
|
status_code: 201
|
|
when: "'data_disk_enabled' not in existing_cf_names"
|
|
register: cf_data_disk_enabled
|
|
changed_when: cf_data_disk_enabled.status == 201
|
|
|
|
- name: Create custom field — data_disk_size_gb
|
|
ansible.builtin.uri:
|
|
url: "{{ netbox_api }}/extras/custom-fields/"
|
|
method: POST
|
|
headers: "{{ headers }}"
|
|
body_format: json
|
|
body:
|
|
name: data_disk_size_gb
|
|
label: Data Disk Size (GB)
|
|
type: integer
|
|
object_types:
|
|
- virtualization.virtualmachine
|
|
description: "Size in GB for the optional second data disk. Only used when data_disk_enabled is true."
|
|
ui_visible: always
|
|
ui_editable: yes
|
|
status_code: 201
|
|
when: "'data_disk_size_gb' not in existing_cf_names"
|
|
register: cf_data_disk_size
|
|
changed_when: cf_data_disk_size.status == 201
|
|
|
|
# ── Summary ───────────────────────────────────────────────────────────
|
|
|
|
- name: Summary
|
|
ansible.builtin.debug:
|
|
msg:
|
|
- "Tags created: {{ tag_creation.results | selectattr('status', 'equalto', 201) | list | length }}"
|
|
- "Tags skipped (already exist): {{ tag_creation.results | selectattr('skipped', 'defined') | list | length }}"
|
|
- "Custom fields configured: hostnames, namespace, managed_by, thematic_name, proxmox_datastore (patched), data_disk_enabled, data_disk_size_gb" |