Initial commit
This commit is contained in:
@@ -0,0 +1,570 @@
|
||||
# Ansible Dynamic Inventory from NetBox
|
||||
|
||||
## Overview
|
||||
|
||||
Use NetBox as a dynamic inventory source for Ansible, eliminating the need for static inventory
|
||||
files and ensuring your automation always has up-to-date infrastructure data.
|
||||
|
||||
## Architecture
|
||||
|
||||
```text
|
||||
┌──────────┐
|
||||
│ NetBox │ (Source of Truth)
|
||||
│ IPAM │
|
||||
└────┬─────┘
|
||||
│
|
||||
│ API Query
|
||||
│
|
||||
▼
|
||||
┌────────────────┐
|
||||
│ nb_inventory │ (Ansible Plugin)
|
||||
│ Plugin │
|
||||
└────┬───────────┘
|
||||
│
|
||||
│ Generates Dynamic Inventory
|
||||
│
|
||||
▼
|
||||
┌────────────────┐
|
||||
│ Ansible │ (Uses inventory for playbooks)
|
||||
│ Playbooks │
|
||||
└────────────────┘
|
||||
```
|
||||
|
||||
## Prerequisites
|
||||
|
||||
### Install NetBox Ansible Collection
|
||||
|
||||
```bash
|
||||
cd ansible
|
||||
uv run ansible-galaxy collection install netbox.netbox
|
||||
```
|
||||
|
||||
**Or add to requirements.yml:**
|
||||
|
||||
```yaml
|
||||
---
|
||||
collections:
|
||||
- name: netbox.netbox
|
||||
version: ">=3.0.0"
|
||||
```
|
||||
|
||||
```bash
|
||||
uv run ansible-galaxy collection install -r requirements.yml
|
||||
```
|
||||
|
||||
### NetBox API Token
|
||||
|
||||
Create read-only API token in NetBox:
|
||||
|
||||
**NetBox UI:** Admin → API Tokens → Add
|
||||
|
||||
- User: ansible (create service user)
|
||||
- Key: Generated automatically
|
||||
- Write enabled: No (read-only)
|
||||
|
||||
**Save token securely:**
|
||||
|
||||
```bash
|
||||
# Option 1: Environment variable
|
||||
export NETBOX_API_TOKEN="your-token-here"
|
||||
|
||||
# Option 2: Ansible Vault
|
||||
ansible-vault create group_vars/all/vault.yml
|
||||
# Add: netbox_token: "your-token-here"
|
||||
```
|
||||
|
||||
## Basic Configuration
|
||||
|
||||
### Create Inventory File
|
||||
|
||||
**File:** `ansible/inventory/netbox.yml`
|
||||
|
||||
```yaml
|
||||
---
|
||||
plugin: netbox.netbox.nb_inventory
|
||||
|
||||
# NetBox API connection
|
||||
api_endpoint: https://netbox.spaceships.work
|
||||
token: !vault |
|
||||
$ANSIBLE_VAULT;1.1;AES256
|
||||
...
|
||||
|
||||
# Validate SSL (set to false for self-signed certs)
|
||||
validate_certs: true
|
||||
|
||||
# Group hosts by these NetBox attributes
|
||||
group_by:
|
||||
- device_roles
|
||||
- tags
|
||||
- sites
|
||||
- platforms
|
||||
|
||||
# Set ansible_host variable from primary_ip4
|
||||
compose:
|
||||
ansible_host: primary_ip4
|
||||
|
||||
# Only include active devices/VMs
|
||||
query_filters:
|
||||
- status: active
|
||||
```
|
||||
|
||||
### Test Inventory
|
||||
|
||||
```bash
|
||||
# List all hosts
|
||||
ansible-inventory -i ansible/inventory/netbox.yml --list
|
||||
|
||||
# View in YAML format
|
||||
ansible-inventory -i ansible/inventory/netbox.yml --list --yaml
|
||||
|
||||
# View specific host
|
||||
ansible-inventory -i ansible/inventory/netbox.yml --host docker-01-nexus
|
||||
|
||||
# Graph inventory
|
||||
ansible-inventory -i ansible/inventory/netbox.yml --graph
|
||||
```
|
||||
|
||||
## Advanced Configuration
|
||||
|
||||
### Filter by Tags
|
||||
|
||||
**Only include hosts with specific tag:**
|
||||
|
||||
```yaml
|
||||
---
|
||||
plugin: netbox.netbox.nb_inventory
|
||||
api_endpoint: https://netbox.spaceships.work
|
||||
token: !vault |
|
||||
$ANSIBLE_VAULT;...
|
||||
|
||||
# Only hosts tagged with "ansible-managed"
|
||||
query_filters:
|
||||
- tag: ansible-managed
|
||||
- status: active
|
||||
|
||||
group_by:
|
||||
- tags
|
||||
```
|
||||
|
||||
### Filter by Device Role
|
||||
|
||||
**Only include specific device roles:**
|
||||
|
||||
```yaml
|
||||
query_filters:
|
||||
- role: docker-host
|
||||
- role: k8s-node
|
||||
- status: active
|
||||
```
|
||||
|
||||
### Custom Groups
|
||||
|
||||
**Create custom groups based on NetBox data:**
|
||||
|
||||
```yaml
|
||||
---
|
||||
plugin: netbox.netbox.nb_inventory
|
||||
api_endpoint: https://netbox.spaceships.work
|
||||
token: !vault |
|
||||
$ANSIBLE_VAULT;...
|
||||
|
||||
group_by:
|
||||
- device_roles
|
||||
- tags
|
||||
- sites
|
||||
|
||||
# Custom group mappings
|
||||
keyed_groups:
|
||||
- key: tags
|
||||
prefix: tag
|
||||
- key: device_role.name
|
||||
prefix: role
|
||||
- key: platform.name
|
||||
prefix: platform
|
||||
|
||||
compose:
|
||||
ansible_host: primary_ip4
|
||||
ansible_user: ansible
|
||||
ansible_become: true
|
||||
```
|
||||
|
||||
### Include Custom Fields
|
||||
|
||||
**Use NetBox custom fields in inventory:**
|
||||
|
||||
```yaml
|
||||
---
|
||||
plugin: netbox.netbox.nb_inventory
|
||||
api_endpoint: https://netbox.spaceships.work
|
||||
token: !vault |
|
||||
$ANSIBLE_VAULT;...
|
||||
|
||||
compose:
|
||||
ansible_host: primary_ip4
|
||||
|
||||
# Use custom fields from NetBox
|
||||
backup_schedule: custom_fields.backup_schedule
|
||||
monitoring_enabled: custom_fields.monitoring_enabled
|
||||
application_owner: custom_fields.owner
|
||||
|
||||
group_by:
|
||||
- tags
|
||||
- custom_fields.environment
|
||||
```
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### Example 1: Configure All Docker Hosts
|
||||
|
||||
**Inventory:** `ansible/inventory/netbox.yml`
|
||||
|
||||
```yaml
|
||||
---
|
||||
plugin: netbox.netbox.nb_inventory
|
||||
api_endpoint: https://netbox.spaceships.work
|
||||
token: !vault |
|
||||
$ANSIBLE_VAULT;...
|
||||
|
||||
query_filters:
|
||||
- tag: docker-host
|
||||
- status: active
|
||||
|
||||
group_by:
|
||||
- tags
|
||||
|
||||
compose:
|
||||
ansible_host: primary_ip4
|
||||
ansible_user: ansible
|
||||
ansible_become: true
|
||||
```
|
||||
|
||||
**Playbook:** `ansible/playbooks/configure-docker-hosts.yml`
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Configure Docker hosts from NetBox inventory
|
||||
hosts: tag_docker_host
|
||||
become: true
|
||||
|
||||
tasks:
|
||||
- name: Ensure Docker is running
|
||||
ansible.builtin.systemd:
|
||||
name: docker
|
||||
state: started
|
||||
enabled: true
|
||||
|
||||
- name: Update Docker daemon config
|
||||
ansible.builtin.copy:
|
||||
dest: /etc/docker/daemon.json
|
||||
content: |
|
||||
{
|
||||
"log-driver": "json-file",
|
||||
"log-opts": {
|
||||
"max-size": "10m",
|
||||
"max-file": "3"
|
||||
}
|
||||
}
|
||||
notify: Restart Docker
|
||||
|
||||
handlers:
|
||||
- name: Restart Docker
|
||||
ansible.builtin.systemd:
|
||||
name: docker
|
||||
state: restarted
|
||||
```
|
||||
|
||||
**Run playbook:**
|
||||
|
||||
```bash
|
||||
cd ansible
|
||||
uv run ansible-playbook -i inventory/netbox.yml playbooks/configure-docker-hosts.yml
|
||||
```
|
||||
|
||||
### Example 2: Site-Specific Deployments
|
||||
|
||||
**Inventory with site grouping:**
|
||||
|
||||
```yaml
|
||||
---
|
||||
plugin: netbox.netbox.nb_inventory
|
||||
api_endpoint: https://netbox.spaceships.work
|
||||
token: !vault |
|
||||
$ANSIBLE_VAULT;...
|
||||
|
||||
group_by:
|
||||
- sites
|
||||
- tags
|
||||
|
||||
compose:
|
||||
ansible_host: primary_ip4
|
||||
|
||||
query_filters:
|
||||
- status: active
|
||||
```
|
||||
|
||||
**Playbook targeting specific site:**
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Update hosts at primary site
|
||||
hosts: site_homelab # Automatically grouped by site name
|
||||
become: true
|
||||
|
||||
tasks:
|
||||
- name: Update all packages
|
||||
ansible.builtin.apt:
|
||||
upgrade: dist
|
||||
update_cache: true
|
||||
when: ansible_os_family == "Debian"
|
||||
```
|
||||
|
||||
### Example 3: Platform-Specific Configuration
|
||||
|
||||
**Inventory:**
|
||||
|
||||
```yaml
|
||||
---
|
||||
plugin: netbox.netbox.nb_inventory
|
||||
api_endpoint: https://netbox.spaceships.work
|
||||
token: !vault |
|
||||
$ANSIBLE_VAULT;...
|
||||
|
||||
group_by:
|
||||
- platforms
|
||||
|
||||
compose:
|
||||
ansible_host: primary_ip4
|
||||
|
||||
keyed_groups:
|
||||
- key: platform.name
|
||||
prefix: platform
|
||||
```
|
||||
|
||||
**Playbook with platform-specific tasks:**
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Platform-specific configuration
|
||||
hosts: all
|
||||
become: true
|
||||
|
||||
tasks:
|
||||
- name: Configure Ubuntu hosts
|
||||
ansible.builtin.apt:
|
||||
name: netbox-agent
|
||||
state: present
|
||||
when: "'ubuntu' in group_names"
|
||||
|
||||
- name: Configure Rocky hosts
|
||||
ansible.builtin.dnf:
|
||||
name: netbox-agent
|
||||
state: present
|
||||
when: "'rocky' in group_names"
|
||||
```
|
||||
|
||||
## Integration with Secrets Management
|
||||
|
||||
### Use with Infisical
|
||||
|
||||
**Combine dynamic inventory with Infisical secrets:**
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Deploy app with NetBox inventory and Infisical secrets
|
||||
hosts: tag_app_server
|
||||
become: true
|
||||
|
||||
vars:
|
||||
infisical_project_id: "7b832220-24c0-45bc-a5f1-ce9794a31259"
|
||||
infisical_env: "prod"
|
||||
infisical_path: "/app-config"
|
||||
|
||||
tasks:
|
||||
- name: Retrieve database password
|
||||
ansible.builtin.include_tasks: "{{ playbook_dir }}/../tasks/infisical-secret-lookup.yml"
|
||||
vars:
|
||||
secret_name: 'DB_PASSWORD'
|
||||
secret_var_name: 'db_password'
|
||||
|
||||
- name: Deploy application config
|
||||
ansible.builtin.template:
|
||||
src: app-config.j2
|
||||
dest: /etc/app/config.yml
|
||||
owner: app
|
||||
group: app
|
||||
mode: '0600'
|
||||
vars:
|
||||
db_host: "{{ hostvars[groups['tag_database'][0]]['ansible_host'] }}"
|
||||
db_password: "{{ db_password }}"
|
||||
```
|
||||
|
||||
## Caching for Performance
|
||||
|
||||
### Enable Inventory Caching
|
||||
|
||||
**File:** `ansible/ansible.cfg`
|
||||
|
||||
```ini
|
||||
[defaults]
|
||||
inventory_plugins = /usr/share/ansible/plugins/inventory
|
||||
|
||||
[inventory]
|
||||
enable_plugins = netbox.netbox.nb_inventory
|
||||
|
||||
# Enable caching
|
||||
cache = true
|
||||
cache_plugin = jsonfile
|
||||
cache_timeout = 3600 # 1 hour
|
||||
cache_connection = /tmp/ansible-netbox-cache
|
||||
```
|
||||
|
||||
**Benefits:**
|
||||
|
||||
- Faster playbook runs
|
||||
- Reduced API calls to NetBox
|
||||
- Works offline (for cache duration)
|
||||
|
||||
**Clear cache:**
|
||||
|
||||
```bash
|
||||
rm -rf /tmp/ansible-netbox-cache
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Authentication Errors
|
||||
|
||||
**Error:** `Failed to query NetBox API`
|
||||
|
||||
**Check:**
|
||||
|
||||
```bash
|
||||
# Test API token
|
||||
curl -H "Authorization: Token $NETBOX_API_TOKEN" \
|
||||
https://netbox.spaceships.work/api/dcim/devices/ | jq
|
||||
|
||||
# Verify token permissions
|
||||
# Token must have read access to: DCIM, IPAM, Virtualization
|
||||
```
|
||||
|
||||
### SSL Certificate Errors
|
||||
|
||||
**Error:** `SSL: CERTIFICATE_VERIFY_FAILED`
|
||||
|
||||
**Solutions:**
|
||||
|
||||
```yaml
|
||||
# Option 1: Add CA certificate
|
||||
validate_certs: true
|
||||
ssl_ca_cert: /path/to/ca-bundle.crt
|
||||
|
||||
# Option 2: Disable for self-signed (dev only!)
|
||||
validate_certs: false
|
||||
```
|
||||
|
||||
### No Hosts Found
|
||||
|
||||
**Error:** Inventory is empty
|
||||
|
||||
**Check:**
|
||||
|
||||
```bash
|
||||
# List all devices in NetBox
|
||||
curl -H "Authorization: Token $NETBOX_API_TOKEN" \
|
||||
https://netbox.spaceships.work/api/dcim/devices/ | jq '.count'
|
||||
|
||||
# Check query filters
|
||||
# Ensure devices match your filters (status, tags, etc.)
|
||||
```
|
||||
|
||||
**Debug inventory plugin:**
|
||||
|
||||
```bash
|
||||
ansible-inventory -i ansible/inventory/netbox.yml --list -vvv
|
||||
```
|
||||
|
||||
### Primary IP Not Set
|
||||
|
||||
**Error:** `ansible_host` is undefined
|
||||
|
||||
**Cause:** Devices/VMs in NetBox don't have primary_ip4 set
|
||||
|
||||
**Solution:**
|
||||
|
||||
```yaml
|
||||
# Fallback to custom field or use DNS name
|
||||
compose:
|
||||
ansible_host: primary_ip4 | default(custom_fields.management_ip) | default(name + '.spaceships.work')
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
### 1. Use Service Account
|
||||
|
||||
Create dedicated NetBox user for Ansible:
|
||||
|
||||
```text
|
||||
Username: ansible-automation
|
||||
Permissions: Read-only (DCIM, IPAM, Virtualization)
|
||||
Token: Never expires (or set appropriate expiration)
|
||||
```
|
||||
|
||||
### 2. Tag for Inventory
|
||||
|
||||
Tag devices/VMs intended for Ansible management:
|
||||
|
||||
```text
|
||||
Tag: ansible-managed
|
||||
```
|
||||
|
||||
**Filter in inventory:**
|
||||
|
||||
```yaml
|
||||
query_filters:
|
||||
- tag: ansible-managed
|
||||
```
|
||||
|
||||
### 3. Set Primary IPs
|
||||
|
||||
Always set primary_ip4 in NetBox for devices/VMs:
|
||||
|
||||
```text
|
||||
Device → Edit → Primary IPv4
|
||||
```
|
||||
|
||||
### 4. Use Custom Fields
|
||||
|
||||
Add custom fields to NetBox for Ansible-specific data:
|
||||
|
||||
```text
|
||||
ansible_user (Text)
|
||||
ansible_port (Integer)
|
||||
ansible_python_interpreter (Text)
|
||||
backup_enabled (Boolean)
|
||||
```
|
||||
|
||||
### 5. Test Before Running
|
||||
|
||||
Always test inventory before running playbooks:
|
||||
|
||||
```bash
|
||||
# Verify hosts
|
||||
ansible-inventory -i inventory/netbox.yml --graph
|
||||
|
||||
# Test connectivity
|
||||
ansible all -i inventory/netbox.yml -m ping
|
||||
```
|
||||
|
||||
### 6. Document in NetBox
|
||||
|
||||
Use NetBox description fields to document:
|
||||
|
||||
- Ansible playbooks that manage this host
|
||||
- Special configuration requirements
|
||||
- Dependencies on other hosts
|
||||
|
||||
## Further Reading
|
||||
|
||||
- [NetBox Ansible Collection Documentation](https://docs.ansible.com/ansible/latest/collections/netbox/netbox/)
|
||||
- [Dynamic Inventory Plugin Guide](https://docs.ansible.com/ansible/latest/plugins/inventory.html)
|
||||
- [NetBox API Documentation](https://demo.netbox.dev/api/docs/)
|
||||
592
skills/netbox-powerdns-integration/workflows/dns-automation.md
Normal file
592
skills/netbox-powerdns-integration/workflows/dns-automation.md
Normal file
@@ -0,0 +1,592 @@
|
||||
# DNS Automation Workflows
|
||||
|
||||
## Overview
|
||||
|
||||
This guide covers end-to-end workflows for automating DNS record management using NetBox as the
|
||||
source of truth and PowerDNS as the authoritative DNS server.
|
||||
|
||||
## Architecture
|
||||
|
||||
```text
|
||||
┌─────────────┐
|
||||
│ Terraform │───┐
|
||||
│ Ansible │ │
|
||||
│ Manual │ │
|
||||
└─────────────┘ │
|
||||
▼
|
||||
┌──────────┐
|
||||
│ NetBox │ (Source of Truth)
|
||||
│ IPAM │
|
||||
└────┬─────┘
|
||||
│
|
||||
│ netbox-powerdns-sync plugin
|
||||
│
|
||||
▼
|
||||
┌──────────┐
|
||||
│ PowerDNS │ (Authoritative DNS)
|
||||
│ API │
|
||||
└────┬─────┘
|
||||
│
|
||||
│ Zone files / API
|
||||
│
|
||||
▼
|
||||
┌──────────┐
|
||||
│ DNS │ (Resolvers query here)
|
||||
│ Clients │
|
||||
└──────────┘
|
||||
```
|
||||
|
||||
## Workflow 1: Create VM with Automatic DNS
|
||||
|
||||
### Using Terraform
|
||||
|
||||
**End-to-end automation:**
|
||||
|
||||
```hcl
|
||||
# 1. Create VM in Proxmox
|
||||
resource "proxmox_vm_qemu" "docker_host" {
|
||||
name = "docker-01-nexus"
|
||||
target_node = "foxtrot"
|
||||
vmid = 101
|
||||
|
||||
clone = "ubuntu-template"
|
||||
full_clone = true
|
||||
|
||||
cores = 4
|
||||
memory = 8192
|
||||
|
||||
network {
|
||||
bridge = "vmbr0"
|
||||
model = "virtio"
|
||||
tag = 30
|
||||
}
|
||||
|
||||
disk {
|
||||
storage = "local-lvm"
|
||||
type = "scsi"
|
||||
size = "50G"
|
||||
}
|
||||
|
||||
# Cloud-init IP configuration
|
||||
ipconfig0 = "ip=192.168.1.100/24,gw=192.168.1.1"
|
||||
|
||||
sshkeys = file("~/.ssh/id_rsa.pub")
|
||||
}
|
||||
|
||||
# 2. Register in NetBox (triggers DNS sync)
|
||||
resource "netbox_ip_address" "docker_host" {
|
||||
ip_address = "192.168.1.100/24"
|
||||
dns_name = "docker-01-nexus.spaceships.work"
|
||||
status = "active"
|
||||
description = "Docker host for Nexus container registry"
|
||||
|
||||
tags = [
|
||||
"terraform",
|
||||
"production-dns", # Triggers auto DNS sync
|
||||
"docker-host"
|
||||
]
|
||||
|
||||
# Ensure VM is created first
|
||||
depends_on = [proxmox_vm_qemu.docker_host]
|
||||
}
|
||||
|
||||
# 3. DNS records automatically created by netbox-powerdns-sync plugin:
|
||||
# A: docker-01-nexus.spaceships.work → 192.168.1.100
|
||||
# PTR: 100.1.168.192.in-addr.arpa → docker-01-nexus.spaceships.work
|
||||
|
||||
# 4. Output for verification
|
||||
output "vm_fqdn" {
|
||||
value = netbox_ip_address.docker_host.dns_name
|
||||
}
|
||||
|
||||
output "vm_ip" {
|
||||
value = split("/", netbox_ip_address.docker_host.ip_address)[0]
|
||||
}
|
||||
```
|
||||
|
||||
**Apply workflow:**
|
||||
|
||||
```bash
|
||||
cd terraform/netbox-vm/
|
||||
tofu init
|
||||
tofu plan
|
||||
tofu apply
|
||||
|
||||
# Verify DNS
|
||||
dig @192.168.3.1 docker-01-nexus.spaceships.work +short
|
||||
# Returns: 192.168.1.100
|
||||
|
||||
# Verify PTR
|
||||
dig @192.168.3.1 -x 192.168.1.100 +short
|
||||
# Returns: docker-01-nexus.spaceships.work
|
||||
```
|
||||
|
||||
### Using Ansible
|
||||
|
||||
**Playbook for VM with DNS:**
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Provision VM with automatic DNS
|
||||
hosts: localhost
|
||||
gather_facts: false
|
||||
|
||||
vars:
|
||||
vm_name: docker-01-nexus
|
||||
vm_ip: 192.168.1.100
|
||||
vm_fqdn: "{{ vm_name }}.spaceships.work"
|
||||
proxmox_node: foxtrot
|
||||
|
||||
tasks:
|
||||
# 1. Create VM in Proxmox
|
||||
- name: Clone template to create VM
|
||||
community.proxmox.proxmox_kvm:
|
||||
api_host: "{{ proxmox_api_host }}"
|
||||
api_user: "{{ proxmox_api_user }}"
|
||||
api_token_id: "{{ proxmox_token_id }}"
|
||||
api_token_secret: "{{ proxmox_token_secret }}"
|
||||
node: "{{ proxmox_node }}"
|
||||
vmid: 101
|
||||
name: "{{ vm_name }}"
|
||||
clone: ubuntu-template
|
||||
full: true
|
||||
storage: local-lvm
|
||||
net:
|
||||
net0: 'virtio,bridge=vmbr0,tag=30'
|
||||
ipconfig:
|
||||
ipconfig0: 'ip={{ vm_ip }}/24,gw=192.168.1.1'
|
||||
cores: 4
|
||||
memory: 8192
|
||||
agent: 1
|
||||
state: present
|
||||
register: vm_result
|
||||
|
||||
- name: Start VM
|
||||
community.proxmox.proxmox_kvm:
|
||||
api_host: "{{ proxmox_api_host }}"
|
||||
api_user: "{{ proxmox_api_user }}"
|
||||
api_token_id: "{{ proxmox_token_id }}"
|
||||
api_token_secret: "{{ proxmox_token_secret }}"
|
||||
node: "{{ proxmox_node }}"
|
||||
vmid: 101
|
||||
state: started
|
||||
|
||||
# 2. Register in NetBox
|
||||
- name: Create IP address in NetBox
|
||||
netbox.netbox.netbox_ip_address:
|
||||
netbox_url: "{{ netbox_url }}"
|
||||
netbox_token: "{{ netbox_token }}"
|
||||
data:
|
||||
address: "{{ vm_ip }}/24"
|
||||
dns_name: "{{ vm_fqdn }}"
|
||||
status: active
|
||||
description: "Docker host for Nexus container registry"
|
||||
tags:
|
||||
- name: production-dns
|
||||
- name: ansible
|
||||
- name: docker-host
|
||||
|
||||
# 3. Wait for DNS propagation
|
||||
- name: Wait for DNS record
|
||||
ansible.builtin.command: dig @192.168.3.1 {{ vm_fqdn }} +short
|
||||
register: dns_check
|
||||
until: dns_check.stdout == vm_ip
|
||||
retries: 10
|
||||
delay: 5
|
||||
changed_when: false
|
||||
|
||||
# 4. Verify DNS resolution
|
||||
- name: Verify DNS forward resolution
|
||||
ansible.builtin.command: dig @192.168.3.1 {{ vm_fqdn }} +short
|
||||
register: forward_dns
|
||||
changed_when: false
|
||||
|
||||
- name: Verify DNS reverse resolution
|
||||
ansible.builtin.command: dig @192.168.3.1 -x {{ vm_ip }} +short
|
||||
register: reverse_dns
|
||||
changed_when: false
|
||||
|
||||
- name: Report DNS status
|
||||
ansible.builtin.debug:
|
||||
msg:
|
||||
- "VM created: {{ vm_name }}"
|
||||
- "IP: {{ vm_ip }}"
|
||||
- "FQDN: {{ vm_fqdn }}"
|
||||
- "Forward DNS: {{ forward_dns.stdout }}"
|
||||
- "Reverse DNS: {{ reverse_dns.stdout }}"
|
||||
```
|
||||
|
||||
**Run playbook:**
|
||||
|
||||
```bash
|
||||
cd ansible
|
||||
uv run ansible-playbook playbooks/provision-vm-with-dns.yml
|
||||
```
|
||||
|
||||
## Workflow 2: Bulk IP Address Management
|
||||
|
||||
### Reserve IP Range in NetBox
|
||||
|
||||
```python
|
||||
#!/usr/bin/env python3
|
||||
# /// script
|
||||
# dependencies = ["pynetbox"]
|
||||
# ///
|
||||
|
||||
import pynetbox
|
||||
import os
|
||||
|
||||
netbox = pynetbox.api(
|
||||
os.getenv("NETBOX_URL"),
|
||||
token=os.getenv("NETBOX_TOKEN")
|
||||
)
|
||||
|
||||
# Define IP range for Docker hosts
|
||||
docker_ips = [
|
||||
{"ip": "192.168.1.100/24", "dns": "docker-01-nexus.spaceships.work", "desc": "Nexus registry"},
|
||||
{"ip": "192.168.1.101/24", "dns": "docker-02-gitlab.spaceships.work", "desc": "GitLab CI/CD"},
|
||||
{"ip": "192.168.1.102/24", "dns": "docker-03-monitoring.spaceships.work", "desc": "Monitoring stack"},
|
||||
]
|
||||
|
||||
for entry in docker_ips:
|
||||
ip = netbox.ipam.ip_addresses.create(
|
||||
address=entry["ip"],
|
||||
dns_name=entry["dns"],
|
||||
description=entry["desc"],
|
||||
status="reserved",
|
||||
tags=[{"name": "production-dns"}, {"name": "docker-host"}]
|
||||
)
|
||||
print(f"Created: {ip.dns_name} → {entry['ip']}")
|
||||
```
|
||||
|
||||
**Run script:**
|
||||
|
||||
```bash
|
||||
export NETBOX_URL="https://netbox.spaceships.work"
|
||||
export NETBOX_TOKEN="your-api-token"
|
||||
|
||||
uv run reserve-docker-ips.py
|
||||
```
|
||||
|
||||
### Update Status to Active
|
||||
|
||||
When VMs are deployed, update IPs from "reserved" to "active":
|
||||
|
||||
```python
|
||||
#!/usr/bin/env python3
|
||||
# /// script
|
||||
# dependencies = ["pynetbox"]
|
||||
# ///
|
||||
|
||||
import pynetbox
|
||||
import os
|
||||
import sys
|
||||
|
||||
netbox = pynetbox.api(
|
||||
os.getenv("NETBOX_URL"),
|
||||
token=os.getenv("NETBOX_TOKEN")
|
||||
)
|
||||
|
||||
fqdn = sys.argv[1] if len(sys.argv) > 1 else "docker-01-nexus.spaceships.work"
|
||||
|
||||
# Find IP by DNS name
|
||||
ips = netbox.ipam.ip_addresses.filter(dns_name=fqdn)
|
||||
if not ips:
|
||||
print(f"No IP found for {fqdn}")
|
||||
sys.exit(1)
|
||||
|
||||
ip = ips[0]
|
||||
ip.status = "active"
|
||||
ip.save()
|
||||
|
||||
print(f"Updated {ip.dns_name}: {ip.address} → active")
|
||||
```
|
||||
|
||||
## Workflow 3: DNS Record Auditing
|
||||
|
||||
### Verify NetBox and PowerDNS are in Sync
|
||||
|
||||
```python
|
||||
#!/usr/bin/env python3
|
||||
# /// script
|
||||
# dependencies = ["pynetbox", "requests"]
|
||||
# ///
|
||||
|
||||
import pynetbox
|
||||
import requests
|
||||
import os
|
||||
import sys
|
||||
|
||||
netbox = pynetbox.api(
|
||||
os.getenv("NETBOX_URL"),
|
||||
token=os.getenv("NETBOX_TOKEN")
|
||||
)
|
||||
|
||||
powerdns_url = os.getenv("POWERDNS_URL", "http://192.168.3.1:8081/api/v1")
|
||||
powerdns_key = os.getenv("POWERDNS_API_KEY")
|
||||
zone = sys.argv[1] if len(sys.argv) > 1 else "spaceships.work"
|
||||
|
||||
# Get NetBox IPs tagged for DNS
|
||||
netbox_ips = netbox.ipam.ip_addresses.filter(tag="production-dns")
|
||||
|
||||
# Get PowerDNS records
|
||||
headers = {"X-API-Key": powerdns_key}
|
||||
pdns_resp = requests.get(f"{powerdns_url}/servers/localhost/zones/{zone}", headers=headers)
|
||||
pdns_zone = pdns_resp.json()
|
||||
|
||||
# Extract A records from PowerDNS
|
||||
pdns_records = {}
|
||||
for rrset in pdns_zone.get("rrsets", []):
|
||||
if rrset["type"] == "A":
|
||||
name = rrset["name"].rstrip(".")
|
||||
for record in rrset["records"]:
|
||||
pdns_records[name] = record["content"]
|
||||
|
||||
# Compare
|
||||
print("NetBox → PowerDNS Sync Status\n")
|
||||
print(f"{'DNS Name':<45} {'NetBox IP':<15} {'PowerDNS IP':<15} {'Status'}")
|
||||
print("-" * 90)
|
||||
|
||||
for nb_ip in netbox_ips:
|
||||
if not nb_ip.dns_name:
|
||||
continue
|
||||
|
||||
dns_name = nb_ip.dns_name.rstrip(".")
|
||||
nb_addr = str(nb_ip.address).split("/")[0]
|
||||
pdns_addr = pdns_records.get(dns_name, "MISSING")
|
||||
|
||||
if pdns_addr == nb_addr:
|
||||
status = "✓ SYNCED"
|
||||
elif pdns_addr == "MISSING":
|
||||
status = "✗ NOT IN POWERDNS"
|
||||
else:
|
||||
status = f"✗ MISMATCH"
|
||||
|
||||
print(f"{dns_name:<45} {nb_addr:<15} {pdns_addr:<15} {status}")
|
||||
```
|
||||
|
||||
**Run audit:**
|
||||
|
||||
```bash
|
||||
export NETBOX_URL="https://netbox.spaceships.work"
|
||||
export NETBOX_TOKEN="your-netbox-token"
|
||||
export POWERDNS_URL="http://192.168.3.1:8081/api/v1"
|
||||
export POWERDNS_API_KEY="your-powerdns-key"
|
||||
|
||||
uv run dns-audit.py spaceships.work
|
||||
```
|
||||
|
||||
## Workflow 4: Dynamic Inventory for Ansible
|
||||
|
||||
### Use NetBox as Inventory Source
|
||||
|
||||
**Create dynamic inventory file:**
|
||||
|
||||
```yaml
|
||||
# ansible/inventory/netbox.yml
|
||||
plugin: netbox.netbox.nb_inventory
|
||||
api_endpoint: https://netbox.spaceships.work
|
||||
token: !vault |
|
||||
$ANSIBLE_VAULT;1.1;AES256
|
||||
...
|
||||
|
||||
# Group hosts by tags
|
||||
group_by:
|
||||
- tags
|
||||
|
||||
# Group hosts by device role
|
||||
compose:
|
||||
ansible_host: primary_ip4
|
||||
|
||||
# Filter to only include VMs with production-dns tag
|
||||
query_filters:
|
||||
- tag: production-dns
|
||||
```
|
||||
|
||||
**Test inventory:**
|
||||
|
||||
```bash
|
||||
ansible-inventory -i ansible/inventory/netbox.yml --list --yaml
|
||||
```
|
||||
|
||||
**Use in playbook:**
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Configure all Docker hosts
|
||||
hosts: tag_docker_host # Automatically grouped by tag
|
||||
become: true
|
||||
|
||||
tasks:
|
||||
- name: Ensure Docker is running
|
||||
ansible.builtin.systemd:
|
||||
name: docker
|
||||
state: started
|
||||
enabled: true
|
||||
|
||||
- name: Report host info
|
||||
ansible.builtin.debug:
|
||||
msg: "Configuring {{ inventory_hostname }} ({{ ansible_host }})"
|
||||
```
|
||||
|
||||
**Run with dynamic inventory:**
|
||||
|
||||
```bash
|
||||
cd ansible
|
||||
uv run ansible-playbook -i inventory/netbox.yml playbooks/configure-docker-hosts.yml
|
||||
```
|
||||
|
||||
## Workflow 5: Cleanup and Decommission
|
||||
|
||||
### Remove VM and DNS Records
|
||||
|
||||
**Terraform destroy workflow:**
|
||||
|
||||
```bash
|
||||
cd terraform/netbox-vm/
|
||||
tofu destroy
|
||||
|
||||
# This will:
|
||||
# 1. Remove NetBox IP address record
|
||||
# 2. netbox-powerdns-sync plugin removes DNS records
|
||||
# 3. Proxmox VM is deleted
|
||||
```
|
||||
|
||||
**Ansible decommission playbook:**
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Decommission VM and remove DNS
|
||||
hosts: localhost
|
||||
gather_facts: false
|
||||
|
||||
vars:
|
||||
vm_fqdn: docker-01-nexus.spaceships.work
|
||||
vm_ip: 192.168.1.100
|
||||
|
||||
tasks:
|
||||
- name: Remove IP from NetBox
|
||||
netbox.netbox.netbox_ip_address:
|
||||
netbox_url: "{{ netbox_url }}"
|
||||
netbox_token: "{{ netbox_token }}"
|
||||
data:
|
||||
address: "{{ vm_ip }}/24"
|
||||
state: absent
|
||||
|
||||
# DNS records automatically removed by plugin
|
||||
|
||||
- name: Verify DNS record removed
|
||||
ansible.builtin.command: dig @192.168.3.1 {{ vm_fqdn }} +short
|
||||
register: dns_check
|
||||
failed_when: dns_check.stdout != ""
|
||||
changed_when: false
|
||||
|
||||
- name: Delete VM from Proxmox
|
||||
community.proxmox.proxmox_kvm:
|
||||
api_host: "{{ proxmox_api_host }}"
|
||||
api_user: "{{ proxmox_api_user }}"
|
||||
api_token_id: "{{ proxmox_token_id }}"
|
||||
api_token_secret: "{{ proxmox_token_secret }}"
|
||||
node: foxtrot
|
||||
vmid: 101
|
||||
state: absent
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
### 1. Always Use Tags
|
||||
|
||||
Tag IP addresses for automatic DNS sync:
|
||||
|
||||
```hcl
|
||||
tags = ["terraform", "production-dns", "service-type"]
|
||||
```
|
||||
|
||||
### 2. Reserve Before Deploy
|
||||
|
||||
Reserve IPs in NetBox before deploying VMs:
|
||||
|
||||
```text
|
||||
Status: reserved → active (after deployment)
|
||||
```
|
||||
|
||||
### 3. Validate Names
|
||||
|
||||
Use naming convention validation before creating:
|
||||
|
||||
```bash
|
||||
./tools/validate_dns_naming.py docker-01-nexus.spaceships.work
|
||||
```
|
||||
|
||||
### 4. Monitor Sync Status
|
||||
|
||||
Regular audits to ensure NetBox and PowerDNS are in sync:
|
||||
|
||||
```bash
|
||||
./tools/dns-audit.py spaceships.work
|
||||
```
|
||||
|
||||
### 5. Use Descriptions
|
||||
|
||||
Document in NetBox description field:
|
||||
|
||||
```text
|
||||
Description: Docker host for Nexus container registry
|
||||
Owner: Platform Team
|
||||
Related: docker-02-gitlab.spaceships.work
|
||||
```
|
||||
|
||||
### 6. Test DNS Resolution
|
||||
|
||||
Always verify DNS after creation:
|
||||
|
||||
```bash
|
||||
dig @192.168.3.1 <fqdn> +short
|
||||
dig @192.168.3.1 -x <ip> +short
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### DNS Records Not Created
|
||||
|
||||
#### Check 1: Tag matching
|
||||
|
||||
```bash
|
||||
# Verify IP has production-dns tag
|
||||
curl -H "Authorization: Token $NETBOX_TOKEN" \
|
||||
"$NETBOX_URL/api/ipam/ip-addresses/?address=192.168.1.100" | jq '.results[0].tags'
|
||||
```
|
||||
|
||||
#### Check 2: Plugin configuration
|
||||
|
||||
```python
|
||||
# In NetBox: Plugins → NetBox PowerDNS Sync → Zones
|
||||
# Verify zone exists and tag rules match
|
||||
```
|
||||
|
||||
#### Check 3: Manual sync
|
||||
|
||||
```bash
|
||||
# In NetBox UI: Plugins → NetBox PowerDNS Sync → Zones → <zone> → Sync Now
|
||||
```
|
||||
|
||||
### DNS Resolution Failures
|
||||
|
||||
**Check PowerDNS API:**
|
||||
|
||||
```bash
|
||||
curl -H "X-API-Key: $POWERDNS_API_KEY" \
|
||||
http://192.168.3.1:8081/api/v1/servers/localhost/zones/spaceships.work
|
||||
```
|
||||
|
||||
**Check DNS server:**
|
||||
|
||||
```bash
|
||||
dig @192.168.3.1 spaceships.work SOA
|
||||
```
|
||||
|
||||
## Further Reading
|
||||
|
||||
- [NetBox PowerDNS Sync Plugin](../reference/sync-plugin-reference.md)
|
||||
- [Terraform NetBox Provider](../reference/terraform-provider-guide.md)
|
||||
- [DNS Naming Conventions](naming-conventions.md)
|
||||
@@ -0,0 +1,418 @@
|
||||
# DNS Naming Conventions
|
||||
|
||||
## Overview
|
||||
|
||||
Consistent DNS naming is critical for automation and infrastructure documentation. This document defines the naming
|
||||
conventions used in the Virgo-Core infrastructure.
|
||||
|
||||
## Standard Pattern
|
||||
|
||||
**Format:** `<service>-<number>-<purpose>.<domain>`
|
||||
|
||||
**Components:**
|
||||
|
||||
- `<service>` - Service type (docker, k8s, proxmox, storage, db, etc.)
|
||||
- `<number>` - Instance number (01, 02, 03, etc.) - always 2 digits
|
||||
- `<purpose>` - Specific purpose or application name
|
||||
- `<domain>` - DNS domain (e.g., spaceships.work)
|
||||
|
||||
**Regex Pattern:**
|
||||
|
||||
```regex
|
||||
^[a-z0-9-]+-\d{2}-[a-z0-9-]+\.[a-z0-9.-]+$
|
||||
```
|
||||
|
||||
## Service Types
|
||||
|
||||
### Container Platforms
|
||||
|
||||
**Docker hosts:**
|
||||
|
||||
```text
|
||||
docker-01-nexus.spaceships.work # Nexus container registry
|
||||
docker-02-gitlab.spaceships.work # GitLab CI/CD
|
||||
docker-03-monitoring.spaceships.work # Monitoring stack (Prometheus, Grafana)
|
||||
```
|
||||
|
||||
**Kubernetes nodes:**
|
||||
|
||||
```text
|
||||
k8s-01-master.spaceships.work # Control plane node 1
|
||||
k8s-02-master.spaceships.work # Control plane node 2
|
||||
k8s-03-master.spaceships.work # Control plane node 3
|
||||
k8s-04-worker.spaceships.work # Worker node 1
|
||||
k8s-05-worker.spaceships.work # Worker node 2
|
||||
```
|
||||
|
||||
### Infrastructure
|
||||
|
||||
**Proxmox nodes:**
|
||||
|
||||
```text
|
||||
proxmox-foxtrot-mgmt.spaceships.work # Foxtrot management interface
|
||||
proxmox-foxtrot-ceph.spaceships.work # Foxtrot CEPH public interface
|
||||
proxmox-golf-mgmt.spaceships.work # Golf management interface
|
||||
proxmox-hotel-mgmt.spaceships.work # Hotel management interface
|
||||
```
|
||||
|
||||
**Storage systems:**
|
||||
|
||||
```text
|
||||
storage-01-nas.spaceships.work # NAS storage (TrueNAS/FreeNAS)
|
||||
storage-02-backup.spaceships.work # Backup storage
|
||||
storage-03-archive.spaceships.work # Long-term archive storage
|
||||
```
|
||||
|
||||
### Databases
|
||||
|
||||
```text
|
||||
db-01-postgres.spaceships.work # PostgreSQL primary
|
||||
db-02-postgres.spaceships.work # PostgreSQL replica
|
||||
db-03-mysql.spaceships.work # MySQL/MariaDB
|
||||
db-04-redis.spaceships.work # Redis cache
|
||||
```
|
||||
|
||||
### Network Services
|
||||
|
||||
```text
|
||||
network-01-pfsense.spaceships.work # pfSense router
|
||||
network-02-unifi.spaceships.work # UniFi controller
|
||||
network-03-dns.spaceships.work # DNS server (PowerDNS)
|
||||
network-04-dhcp.spaceships.work # DHCP server
|
||||
```
|
||||
|
||||
### Application Services
|
||||
|
||||
```text
|
||||
app-01-netbox.spaceships.work # NetBox IPAM
|
||||
app-02-vault.spaceships.work # HashiCorp Vault
|
||||
app-03-consul.spaceships.work # HashiCorp Consul
|
||||
app-04-nomad.spaceships.work # HashiCorp Nomad
|
||||
```
|
||||
|
||||
## Special Cases
|
||||
|
||||
### Management Interfaces
|
||||
|
||||
For infrastructure with multiple interfaces, include interface purpose:
|
||||
|
||||
```text
|
||||
proxmox-<node>-mgmt.spaceships.work # Management network
|
||||
proxmox-<node>-ceph.spaceships.work # CEPH public network
|
||||
proxmox-<node>-backup.spaceships.work # Backup network
|
||||
```
|
||||
|
||||
### Virtual IPs (FHRP/VIPs)
|
||||
|
||||
```text
|
||||
vip-01-k8s-api.spaceships.work # Kubernetes API VIP
|
||||
vip-02-haproxy.spaceships.work # HAProxy VIP
|
||||
vip-03-postgres.spaceships.work # PostgreSQL VIP
|
||||
```
|
||||
|
||||
### Service Endpoints
|
||||
|
||||
```text
|
||||
service-01-api.spaceships.work # API endpoint
|
||||
service-02-web.spaceships.work # Web frontend
|
||||
service-03-cdn.spaceships.work # CDN endpoint
|
||||
```
|
||||
|
||||
## Rules and Best Practices
|
||||
|
||||
### Mandatory Rules
|
||||
|
||||
1. **Always lowercase** - No uppercase letters
|
||||
2. **Hyphens only** - No underscores or other special characters
|
||||
3. **Two-digit numbers** - Use 01, 02, not 1, 2
|
||||
4. **Descriptive purpose** - Purpose should clearly indicate function
|
||||
5. **Valid DNS characters** - Only `a-z`, `0-9`, `-`, `.`
|
||||
|
||||
### Recommended Practices
|
||||
|
||||
1. **Consistent service names** - Stick to established service types
|
||||
2. **Logical numbering** - Start at 01, increment sequentially
|
||||
3. **Purpose specificity** - Be specific but concise (nexus, not nexus-container-registry)
|
||||
4. **Avoid ambiguity** - Don't use `test-01-prod` or similar confusing names
|
||||
5. **Document exceptions** - If you must break a rule, document why
|
||||
|
||||
## Validation
|
||||
|
||||
### Python Validation Script
|
||||
|
||||
```python
|
||||
#!/usr/bin/env python3
|
||||
# /// script
|
||||
# dependencies = []
|
||||
# ///
|
||||
|
||||
import re
|
||||
import sys
|
||||
|
||||
PATTERN = r'^[a-z0-9-]+-\d{2}-[a-z0-9-]+\.[a-z0-9.-]+$'
|
||||
|
||||
def validate_dns_name(name: str) -> tuple[bool, str]:
|
||||
"""Validate DNS name against convention."""
|
||||
if not re.match(PATTERN, name):
|
||||
return False, "Name doesn't match pattern: <service>-<NN>-<purpose>.<domain>"
|
||||
|
||||
parts = name.split('.')
|
||||
if len(parts) < 2:
|
||||
return False, "Must include domain"
|
||||
|
||||
hostname = parts[0]
|
||||
components = hostname.split('-')
|
||||
|
||||
if len(components) < 3:
|
||||
return False, "Hostname must have at least 3 components: <service>-<NN>-<purpose>"
|
||||
|
||||
# Check number component (should be 2 digits)
|
||||
number_component = components[1]
|
||||
if not number_component.isdigit() or len(number_component) != 2:
|
||||
return False, f"Number component '{number_component}' must be exactly 2 digits (01-99)"
|
||||
|
||||
return True, "Valid"
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) != 2:
|
||||
print("Usage: validate_dns_naming.py <dns-name>")
|
||||
sys.exit(1)
|
||||
|
||||
name = sys.argv[1]
|
||||
valid, message = validate_dns_name(name)
|
||||
|
||||
if valid:
|
||||
print(f"✓ {name}: {message}")
|
||||
sys.exit(0)
|
||||
else:
|
||||
print(f"✗ {name}: {message}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
```
|
||||
|
||||
**Usage:**
|
||||
|
||||
```bash
|
||||
./tools/validate_dns_naming.py docker-01-nexus.spaceships.work
|
||||
# ✓ docker-01-nexus.spaceships.work: Valid
|
||||
|
||||
./tools/validate_dns_naming.py Docker-1-Nexus.spaceships.work
|
||||
# ✗ Docker-1-Nexus.spaceships.work: Name doesn't match pattern
|
||||
```
|
||||
|
||||
## NetBox Integration
|
||||
|
||||
### Setting DNS Names in NetBox
|
||||
|
||||
**Via Web UI:**
|
||||
|
||||
IP Addresses → Add → DNS Name field: `docker-01-nexus.spaceships.work`
|
||||
|
||||
**Via Terraform:**
|
||||
|
||||
```hcl
|
||||
resource "netbox_ip_address" "docker_nexus" {
|
||||
ip_address = "192.168.1.100/24"
|
||||
dns_name = "docker-01-nexus.spaceships.work"
|
||||
description = "Docker host for Nexus container registry"
|
||||
|
||||
tags = ["terraform", "production-dns"]
|
||||
}
|
||||
```
|
||||
|
||||
**Via Ansible:**
|
||||
|
||||
```yaml
|
||||
- name: Create IP address in NetBox
|
||||
netbox.netbox.netbox_ip_address:
|
||||
netbox_url: "{{ netbox_url }}"
|
||||
netbox_token: "{{ netbox_token }}"
|
||||
data:
|
||||
address: "192.168.1.100/24"
|
||||
dns_name: "docker-01-nexus.spaceships.work"
|
||||
tags:
|
||||
- name: production-dns
|
||||
```
|
||||
|
||||
### Tagging for Auto-DNS
|
||||
|
||||
Tag IP addresses to trigger automatic DNS record creation:
|
||||
|
||||
**Production DNS:**
|
||||
|
||||
```text
|
||||
Tag: production-dns
|
||||
Zone: spaceships.work
|
||||
```
|
||||
|
||||
**Development DNS:**
|
||||
|
||||
```text
|
||||
Tag: dev-dns
|
||||
Zone: dev.spaceships.work
|
||||
```
|
||||
|
||||
**Lab/Testing DNS:**
|
||||
|
||||
```text
|
||||
Tag: lab-dns
|
||||
Zone: lab.spaceships.work
|
||||
```
|
||||
|
||||
## PowerDNS Record Generation
|
||||
|
||||
### Automatic Record Creation
|
||||
|
||||
When an IP address with correct tags is created in NetBox:
|
||||
|
||||
```text
|
||||
IP: 192.168.1.100/24
|
||||
DNS Name: docker-01-nexus.spaceships.work
|
||||
Tag: production-dns
|
||||
|
||||
→ A record created: docker-01-nexus.spaceships.work → 192.168.1.100
|
||||
→ PTR record created: 100.1.168.192.in-addr.arpa → docker-01-nexus.spaceships.work
|
||||
```
|
||||
|
||||
### Sync Verification
|
||||
|
||||
```bash
|
||||
# Verify DNS record exists
|
||||
dig @192.168.3.1 docker-01-nexus.spaceships.work +short
|
||||
# Should return: 192.168.1.100
|
||||
|
||||
# Verify PTR record
|
||||
dig @192.168.3.1 -x 192.168.1.100 +short
|
||||
# Should return: docker-01-nexus.spaceships.work
|
||||
```
|
||||
|
||||
## Common Mistakes
|
||||
|
||||
### Wrong Number Format
|
||||
|
||||
```text
|
||||
❌ docker-1-nexus.spaceships.work # Single digit
|
||||
✓ docker-01-nexus.spaceships.work # Two digits
|
||||
|
||||
❌ docker-001-nexus.spaceships.work # Three digits
|
||||
✓ docker-01-nexus.spaceships.work # Two digits
|
||||
```
|
||||
|
||||
### Wrong Separators
|
||||
|
||||
```text
|
||||
❌ docker_01_nexus.spaceships.work # Underscores
|
||||
✓ docker-01-nexus.spaceships.work # Hyphens
|
||||
|
||||
❌ docker.01.nexus.spaceships.work # Dots in hostname
|
||||
✓ docker-01-nexus.spaceships.work # Hyphens only
|
||||
```
|
||||
|
||||
### Wrong Case
|
||||
|
||||
```text
|
||||
❌ Docker-01-Nexus.spaceships.work # Mixed case
|
||||
✓ docker-01-nexus.spaceships.work # Lowercase only
|
||||
|
||||
❌ DOCKER-01-NEXUS.SPACESHIPS.WORK # Uppercase
|
||||
✓ docker-01-nexus.spaceships.work # Lowercase only
|
||||
```
|
||||
|
||||
### Missing Components
|
||||
|
||||
```text
|
||||
❌ docker-nexus.spaceships.work # Missing number
|
||||
✓ docker-01-nexus.spaceships.work # Complete
|
||||
|
||||
❌ 01-nexus.spaceships.work # Missing service
|
||||
✓ docker-01-nexus.spaceships.work # Complete
|
||||
|
||||
❌ docker-01.spaceships.work # Missing purpose
|
||||
✓ docker-01-nexus.spaceships.work # Complete
|
||||
```
|
||||
|
||||
## Migration Strategy
|
||||
|
||||
### From Legacy Names
|
||||
|
||||
If you have existing DNS names that don't follow the convention:
|
||||
|
||||
1. **Document current names** - Create inventory of legacy names
|
||||
2. **Create new names** - Following convention
|
||||
3. **Create CNAME records** - Point legacy names to new names
|
||||
4. **Update configs gradually** - Migrate services to use new names
|
||||
5. **Monitor usage** - Track legacy CNAME usage
|
||||
6. **Deprecate legacy names** - Remove after migration complete
|
||||
|
||||
**Example migration:**
|
||||
|
||||
```text
|
||||
Legacy: nexus.spaceships.work
|
||||
New: docker-01-nexus.spaceships.work
|
||||
CNAME: nexus.spaceships.work → docker-01-nexus.spaceships.work
|
||||
|
||||
After 6 months: Remove CNAME, update all references to use new name
|
||||
```
|
||||
|
||||
## Environment-Specific Domains
|
||||
|
||||
### Production
|
||||
|
||||
```text
|
||||
Domain: spaceships.work
|
||||
Example: docker-01-nexus.spaceships.work
|
||||
```
|
||||
|
||||
### Development
|
||||
|
||||
```text
|
||||
Domain: dev.spaceships.work
|
||||
Example: docker-01-nexus.dev.spaceships.work
|
||||
```
|
||||
|
||||
### Lab/Testing
|
||||
|
||||
```text
|
||||
Domain: lab.spaceships.work
|
||||
Example: docker-01-nexus.lab.spaceships.work
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
### In NetBox
|
||||
|
||||
Use the **Description** field to document:
|
||||
|
||||
- Primary purpose
|
||||
- Hosted applications
|
||||
- Related services
|
||||
- Contact owner
|
||||
|
||||
**Example:**
|
||||
|
||||
```text
|
||||
IP: 192.168.1.100/24
|
||||
DNS Name: docker-01-nexus.spaceships.work
|
||||
Description: Docker host for Nexus container registry.
|
||||
Serves Docker and Maven artifacts.
|
||||
Owner: Platform Team
|
||||
Related: docker-02-gitlab.spaceships.work
|
||||
```
|
||||
|
||||
### In Infrastructure Code
|
||||
|
||||
**Terraform example:**
|
||||
|
||||
```hcl
|
||||
resource "netbox_ip_address" "docker_nexus" {
|
||||
ip_address = "192.168.1.100/24"
|
||||
dns_name = "docker-01-nexus.spaceships.work"
|
||||
description = "Docker host for Nexus container registry"
|
||||
|
||||
tags = ["terraform", "production-dns", "docker-host", "nexus"]
|
||||
}
|
||||
```
|
||||
|
||||
## Further Reading
|
||||
|
||||
- [RFC 1035 - Domain Names](https://www.rfc-editor.org/rfc/rfc1035)
|
||||
- [DNS Best Practices](https://www.ietf.org/rfc/rfc1912.txt)
|
||||
Reference in New Issue
Block a user