Skip to content

refactor(bdr-veeam): location-scoped buckets, device-OUID folders, active/inactive inventory#85

Open
Gumbees wants to merge 5 commits into
mainfrom
refactor/veeam-location-scoped-bucket-naming
Open

refactor(bdr-veeam): location-scoped buckets, device-OUID folders, active/inactive inventory#85
Gumbees wants to merge 5 commits into
mainfrom
refactor/veeam-location-scoped-bucket-naming

Conversation

@Gumbees

@Gumbees Gumbees commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

Summary

Brings Veeam B2 provisioning in line with the DTC Cloud Backup Architecture Standards and OUID Standard, pushes storage isolation down to the location layer, and adds the missing device-identity primitive.

Storage scheme

bucket:  veeam-{location-ouid-flat}     (one bucket per location)
folder:  {device-ouid-dashed}/          (one repo root per BDR; Veeam owns below)

Locations get bought and sold, so the bucket tracks the location OUID (looked up at runtime from NinjaOne) rather than the org. The per-device folder uses the BDR device OUID, which is recomputable from a stable source (minted once, persisted on the device) so re-runs and rebuilds resolve to the same folder and reclaim existing data instead of orphaning it. This is why neither Veeam's internal instance GUID nor a creation timestamp was used (both regenerate on rebuild).

Changes

bdr-veeam/veeam-configure-backblaze-repo.ps1

  • Bucket veeam-{location-ouid-flat} from the NinjaOne location field (replaces org UUID + time-based short id).
  • Folder = device OUID, read from registry HKLM\SOFTWARE\DTC\DeviceGuid then the NinjaOne Device GUID field. Fails fast pointing at the minting script if absent.

rmm-ninja/ninja-ensure-device-guid.ps1 (new)

  • Boot-time, idempotent, self-healing. Mints a canonical UUIDv7 device OUID once and persists it to registry + NinjaOne. Exits early if already consistent; backfills a half-provisioned state; refuses to overwrite a conflicting assigned OUID. Native [Guid]::CreateVersion7() with a dependency-free UUIDv7 fallback for older runtimes.

bdr-veeam/veeam-inventory.ps1

  • Splits the S3 inventory into Active vs Inactive repositories (active = a backup copy job targets it), each with a Last Backup column.
  • New CUSTOM_FIELD_S3_TOTAL_SIZE totals used space across all S3 repos so leftover (pre-move) buckets surface in the cost picture.

New NinjaOne fields to create

  • CUSTOM_FIELD_LOCATION_UUID - Location text field (location OUID)
  • CUSTOM_FIELD_DEVICE_GUID - Device text field (device OUID; written by the minting script)
  • CUSTOM_FIELD_S3_TOTAL_SIZE - Device text field (total S3 size)

Testing

  • PowerShell parser clean on all three scripts.
  • UUIDv7 generator verified RFC 9562-correct: version nibble 7, variant in {8,9,a,b}, embedded timestamp decodes to creation time, time-ordered.
  • Inventory total + active/inactive split exercised with synthetic repo data.
  • Not yet run on a live BDR - Veeam cmdlet paths and Ninja-Property-Get/Set need a real-device smoke test. Bucket/folder naming is deterministic so a dry run is safe.

RMM compatibility

Dual-mode (interactive + RMM) preserved. Minting script requires SYSTEM context (HKLM write + Ninja cmdlets); intended as a boot trigger in NinjaOne.

🤖 Generated with Claude Code

Gumbees and others added 5 commits May 11, 2026 13:14
…67)

* improvement(docs): sync CLAUDE.md with DTC canonical change-taxonomy

Replace the deprecated enhancement/ + problem/ two-prefix model with the
canonical four-prefix taxonomy (bug/, refactor/, improvement/, feature/)
and two-tier GitHub labels (type:* + category:*). Mirrors
https://kb.dtctoday.com/books/developer-operations-devops/page/change-taxonomy.

- Rewrite Git Workflow section to reflect main-as-release (this repo has
  no development branch; the prior section described a model that did
  not match the actual repo).
- Add Halo Type / Category / GitHub labels / Branch prefix / Default
  semver mapping table.
- Note BREAKING: PR title override as future-state (no semver yet).
- Add dependabot categorization rule and 'name the branch after the
  change' guidance.
- Mark legacy enhancement/ and problem/ prefixes accepted on existing
  branches; new work uses the four-prefix model.
- Add db-* to the Category Organization list (db-mysql/ exists on disk).

* docs(taxonomy): add Enhancement/Refactor row + clarify Refactor spans both types
* Add NinjaRMM GUID to Halo PSA GUID Sync Script

Syncs DTC Org GUID from NinjaRMM custom fields to DTC Client GUID field in Halo PSA for all matching clients by name.

* Update ninja-halo-guid-sync.ps1

Fix Halo custom field name to CFDtcClientGuid

* Fix NinjaClientSecret placeholder comment typo

* Remove hardcoded credentials — replace with placeholders

Secrets were exposed in script body. Rotated all credentials. Real values stored in NinjaRMM script body (never leaves Ninja) and 1Password. GitHub copy uses placeholders only.

* Update script — use Script Variable injection for secrets

Refactored to inject client secrets via NinjaRMM Script Variables at runtime. Client IDs hardcoded (not secrets). Secrets never stored in repo. Validated working in production.

* Fix: Bring script into compliance with DTC PowerShell engineering standard

Addresses CodeRabbit review concerns on PR #49:
- Add RMM variable declaration block per script-template-powershell.ps1
- Add Start-Transcript/Stop-Transcript with C:\ProgramData\DTC\Logs\ output
- Add $ScriptLogName and $Description audit trail variables
- Replace Write-Host with Write-Information per output stream standard
- Add $ErrorActionPreference = 'Stop' and $ProgressPreference = 'SilentlyContinue'
- Add explicit exit codes (0/1/2 per DTC convention)
- Wrap all work in try/catch/finally per page 3299
* feat(rmm-ninja): add Lockhart remediation script

Automated remediation for NinjaOne Backup Lockhart service failures. Responds to the '(SEC) Last Successful Backup' condition on policy 58 (servers). Diagnose -> Repair -> Confirm flow with business-hours gating for disruptive operations. HV0 hypervisor host exclusion. DeviceOffline short-circuit. Forensic capture on remediation failure.

Pilot tested on internal DTC server 2026-04-23. Full cycle validated: idle classification, service restart, PID rotation, state file written, counter incremented correctly.

Includes Pester test coverage for structural validation, HV0 hypervisor host exclusion, and NotApplicable short-circuit path. Tests file force-added: .gitignore line 25 (*test*) excludes Pester source files - flagging as separate concern for repo cleanup.

* fix(rmm-ninja): RMM env-var binding + template compliance per review

- ForceDisruptiveRepairs/ClearStateAndExit now resolve from env vars (NinjaOne preset variable path) with CLI switch fallback for interactive runs; all numeric tunables gain env-var overrides (review comment 1)

- Three-section template structure per CLAUDE.md: RMM variable block, input handling with env:RMM detection and env:Description audit capture, Start/Stop-Transcript to RMMScriptPath/WINDIR logs with 10MB rotation. Documented deviation: no Read-Host (condition-triggered automation must never block on input)

- Collapsed dead Running-case conditional to single restart with explanatory comment (review comment 2)

- Counter semantics: docs and MaxAttemptsReached log now say 'interventions within window' matching the intentional increment-on-success behavior (review comment 3)

- .NOTES Path corrected to rmm-ninja/ (review comment 4)

- HV0 exclusion moved to entry point before transcript so hypervisors take zero file writes; Pester test added for env:ClearStateAndExit checkbox binding
…rt remediation (#83)

backup.ninjarmm.com is no longer used by the NinjaOne backup service, so it
fails DNS resolution and TCP connect on every run. This (1) fired unnecessary
disruptive network repairs (DNS cache flush, Dnscache restart, ARP clear) and
(2) poisoned the post-repair verdict via DNS_Still_Failing / Cloud_Still_Unreachable,
marking the remediation FAILED even when Lockhart was successfully repaired.

Remaining endpoints (app.ninjarmm.com, s3.amazonaws.com, s3.us-east-1.amazonaws.com)
still cover the agent control channel and backup storage targets.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…tive/inactive inventory

Bring Veeam B2 provisioning in line with the DTC Cloud Backup Architecture
Standards and OUID Standard, and add the missing device-identity primitive.

veeam-configure-backblaze-repo.ps1
- Bucket is now veeam-{location-ouid-flat} (per-location isolation; locations
  get bought/sold, so the boundary tracks the location OUID, not the org).
- Repository-root folder is the device OUID (dashed), read from on-device
  registry HKLM\SOFTWARE\DTC\DeviceGuid then the NinjaOne field. Chosen over
  Veeam's internal instance GUID / a timestamp because it is recomputable from
  a stable source, so re-runs and rebuilds resolve to the SAME folder and
  reclaim existing data instead of orphaning it.
- Replaces the org UUID + time-based short-id scheme.

rmm-ninja/ninja-ensure-device-guid.ps1 (new)
- Boot-time, idempotent, self-healing: mints a canonical UUIDv7 device OUID
  once and persists it to registry + the NinjaOne Device GUID field. Exits
  early if already consistent; backfills a half-provisioned state; refuses to
  overwrite a conflicting assigned OUID. Native [Guid]::CreateVersion7() with a
  dependency-free hand-rolled UUIDv7 fallback for older runtimes.

veeam-inventory.ps1
- Split the S3 inventory into Active vs Inactive repositories (active = a
  backup copy job targets the repo), each with a Last Backup column.
- New CUSTOM_FIELD_S3_TOTAL_SIZE totals used space across ALL S3 repos so
  leftover (pre-move) buckets show up in the cost picture.

Testing: PowerShell parser clean on all scripts; UUIDv7 generator verified
RFC 9562-correct (version/variant nibbles, embedded timestamp, ordering);
inventory total + active/inactive split exercised with synthetic data. Veeam
cmdlet paths and Ninja-Property-Get/Set require a real-BDR smoke test.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Code review skipped — your organization's overage spend limit has been reached.

Code review is billed via overage credits. To resume reviews, an organization admin can raise the monthly limit at claude.ai/admin-settings/claude-code.

Once credits are available, push a new commit or reopen this pull request to trigger a review.

@Gumbees Gumbees added type:enhancement New work that adds or improves capability (Halo: Enhancement) category:refactor Working-but-clunky design redo; code-structure change labels Jun 15, 2026
@Gumbees Gumbees changed the base branch from development to main June 25, 2026 11:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

category:refactor Working-but-clunky design redo; code-structure change type:enhancement New work that adds or improves capability (Halo: Enhancement)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants