Add session context: initial AD Compare plugin build and iteration
This commit is contained in:
1 parent
01ba443ac5
commit
dbfb6458a4
1 file changed
+81
@@ -0,0 +1,81 @@
|
||||
# Disco AD Compare Plugin - Session: Initial Build & Iteration
|
||||
|
||||
**Date:** 2026-04-28
|
||||
**Project:** disco-ad-compare-plugin
|
||||
**Status:** ongoing
|
||||
**Tags:** disco-ict, plugin, active-directory, c-sharp, managed-by, uiextension
|
||||
|
||||
## Context
|
||||
Built a Disco ICT plugin from scratch that compares the AD computer `managedBy` attribute against the Disco `AssignedUserId` for devices, and reports mismatches. The plugin was developed iteratively through many build-fix cycles due to constraints of the target environment (no Visual Studio, only .NET Framework 4.0 MSBuild, Disco installed at `C:\Program Files\Disco\WebApp\bin`).
|
||||
|
||||
## Key Decisions
|
||||
- **C# 5 only** — The server has only .NET Framework MSBuild v4.0.30319 (no VS installed). No string interpolation (`$""`), no null-conditional (`?.`), no auto-property initializers, no expression-bodied members. All code uses `string.Format()`, explicit null checks, constructors for list init.
|
||||
- **ToolsVersion 4.0 / TargetFramework v4.5** — Downgraded from 15.0/4.7.2 to avoid MSB3644 errors. The warnings about architecture mismatch (MSIL vs AMD64) are harmless.
|
||||
- **Manual manifest.json** — No ManifestGenerator.exe available in the install. Build script creates manifest manually with correct field names matching `PluginFeatureManifest.cs`: `Id`, `Name`, `TypeName`, `PrimaryFeature`, `CategoryTypeName`.
|
||||
- **CategoryTypeName for UIExtension** — Must be the generic type definition: `Disco.Services.Plugins.Features.UIExtension.UIExtensionFeature``1` (backtick-1 for one generic param). Confirmed by comparing with working plugins (CasperSuite, PasswordManager, DocFill).
|
||||
- **DN-to-DN comparison** — AD `managedBy` returns a Distinguished Name, not `DOMAIN\username`. Must look up the Disco assigned user in AD to get their DN, then compare DN-to-DN (case-insensitive). Initial version compared `DOMAIN\username` against DN which always mismatched.
|
||||
- **Filter invalid devices** — Skip devices without `DOMAIN\ComputerName` format (no backslash) to avoid "The Id must include the Domain" errors. Also exclude devices not found in AD from the results table (counted separately).
|
||||
- **Server-side rendering for web handler** — jQuery/AJAX approach failed because the standalone plugin page doesn't load Disco's layout/scripts. Rewrote to render full HTML server-side with inline CSS. The `/Run` action generates the complete page.
|
||||
- **Plugin URL routing** — Disco route is `Plugin/{PluginId}/{PluginAction}` where PluginAction is required. URL must be `/Plugin/Disco.Plugins.ADCompare/Run` not `/Plugin/Disco.Plugins.ADCompare`.
|
||||
- **Build as .zip then rename** — `Compress-Archive` only supports `.zip` extension. Build script creates `.zip` then renames to `.discoPlugin`.
|
||||
- **HintPath regex replacement** — Single regex `<HintPath>[^<]+\\([^\\<]+\.dll)</HintPath>` replaces ALL assembly references to point at `DiscoBinPath`, handling Disco, Newtonsoft, MVC, WebPages, Razor, and EntityFramework in one pass.
|
||||
|
||||
## Technical Details
|
||||
|
||||
### Build Command
|
||||
```powershell
|
||||
.\Build-Plugin.ps1 -DiscoBinPath "C:\Program Files\Disco\WebApp\bin"
|
||||
```
|
||||
|
||||
### Disco Plugin Paths
|
||||
- Installed plugins: `C:\Disco\Plugins\<PluginId>\`
|
||||
- Plugin packages: `C:\Disco\PluginPackages\`
|
||||
- Plugin storage: `C:\Disco\PluginStorage\`
|
||||
- Disco binaries: `C:\Program Files\Disco\WebApp\bin\`
|
||||
- Disco logs: CSV format, viewable in Disco web UI under Configuration
|
||||
|
||||
### Key Disco API Patterns
|
||||
- `ActiveDirectory.RetrieveADMachineAccount(domainId, additionalProperties)` — returns AD computer, null if not found
|
||||
- `ActiveDirectory.RetrieveADUserAccount(userId)` — looks up by `DOMAIN\username`
|
||||
- `adAccount.GetPropertyValue<T>("attributeName")` — reads AD attributes like `managedBy`, `lastLogonTimestamp`
|
||||
- `UIExtensionFeature<DeviceShowModel>` — inject HTML on device pages
|
||||
- `UIExtensionFeature<UserShowModel>` — inject HTML on user pages
|
||||
- `PluginWebHandler.ExecuteAction(ActionName)` — web handler routing
|
||||
- `PluginConfigurationHandler` with `WebViewPage` — required for plugin system
|
||||
|
||||
### Assembly References Required
|
||||
System, System.Core, System.Data, System.Data.Entity, System.Web, System.ComponentModel.DataAnnotations, EntityFramework, System.Web.Mvc, System.Web.WebPages, System.Web.Razor, Newtonsoft.Json, Disco.Models, Disco.Data, Disco.Services
|
||||
|
||||
### Manifest Feature Format (exact JSON shape)
|
||||
```json
|
||||
{
|
||||
"Id": "ADCompareDeviceUI",
|
||||
"Name": "Device Page - AD Compare",
|
||||
"TypeName": "Disco.Plugins.ADCompare.Features.DeviceUIExtension",
|
||||
"PrimaryFeature": false,
|
||||
"CategoryTypeName": "Disco.Services.Plugins.Features.UIExtension.UIExtensionFeature`1"
|
||||
}
|
||||
```
|
||||
|
||||
## Code Changes
|
||||
- `ADComparePlugin.cs` — Main plugin class, empty Initialize (features self-register via manifest)
|
||||
- `Features/DeviceCompareService.cs` — Core comparison: DN-to-DN matching, filters invalid/missing devices
|
||||
- `Features/DeviceUIExtension.cs` — Device page panel: managedBy comparison, last login, AD status
|
||||
- `Features/UserUIExtension.cs` — User page panel: last login, AD status, OU location, 30-day inactivity warning
|
||||
- `Models/DeviceComparisonResult.cs` — Result + summary models with SkippedDevices counter
|
||||
- `WebHandler/ADCompareWebHandler.cs` — Server-side rendered dashboard at `/Run`, CSV export at `/Export`
|
||||
- `ConfigurationHandler/ADCompareConfigurationHandler.cs` — Required config handler with WebViewPage
|
||||
- `Properties/AssemblyInfo.cs` — Version 1.3.1.0, author Jess Rogerson
|
||||
- `Build-Plugin.ps1` — Build + package script, regex HintPath resolution, manual manifest generation
|
||||
- `Disco.Plugins.ADCompare.csproj` — ToolsVersion 4.0, .NET 4.5, all references
|
||||
- `.gitignore` — Standard .NET ignores
|
||||
- `README.md` — Full documentation
|
||||
|
||||
## Open Questions / Next Steps
|
||||
- **UIExtension panels not rendering** — Features initialize without errors in Disco logs (`Initialized Plugin Feature: 'DeviceUIExtension'`), manifest format matches working plugins exactly, but HTML panels don't appear on device/user pages. The `Register()` call in `Initialize()` override runs. Need to investigate: does `Literal()` work? Does `Nothing()` suppress? Should we check how CasperSuite's `CasperSuiteUIExt_DeviceShow.ExecuteAction` returns its result vs our approach? May need to examine the Disco Views (`.cshtml`) that render UIExtension slots to understand where the HTML is injected.
|
||||
- **Comparison dashboard works perfectly** — `/Plugin/Disco.Plugins.ADCompare/Run` renders correctly with summary stats, mismatch table, and CSV export.
|
||||
- **Credit line** — Plugin should show "Created by: Jess Rogerson (yelling commands at Claude.AI)" in a footer or about section.
|
||||
|
||||
## Artifacts
|
||||
- Gitea repo: https://gitea.hideawaygaming.com.au/jessikitty/disco-ad-compare-plugin
|
||||
- Source Disco repo (forked): https://gitea.hideawaygaming.com.au/jessikitty/Disco (branch: master)
|
||||
Reference in new issue
Block a user