Skip to main content
This guide covers building Intune Commander, running tests, and the development workflow.

Prerequisites

Required Software

  • .NET 10 SDK - Download
  • Git - For cloning the repository
  • IDE (choose one):
    • Visual Studio 2022 17.8+ (Windows)
    • JetBrains Rider 2024.3+ (Cross-platform)
    • VS Code with C# Dev Kit extension

Optional Tools

  • GitHub CLI (gh) - For creating PRs
  • PowerShell 7+ - For running setup scripts

System Requirements

Windows:
  • Windows 10 1809+ (build 17763) or Windows 11
  • 512MB RAM minimum, 1GB recommended
  • ~500MB disk space for dependencies
Linux/macOS:
  • Supported by Avalonia but not actively tested
  • See Technology Stack for platform details

Getting the Code

Clone Repository

git clone https://github.com/your-org/intune-commander.git
cd intune-commander

Branch for Development

Never commit directly to main. Always create a feature branch:
git checkout main
git pull
git checkout -b feature/my-feature
See Contributing Guide for branch naming conventions.

Building the Application

Build All Projects

# Build in Debug configuration
dotnet build

# Build in Release configuration
dotnet build --configuration Release
Output:
src/Intune.Commander.Core/bin/Debug/net10.0/
src/Intune.Commander.Desktop/bin/Debug/net10.0/
tests/Intune.Commander.Core.Tests/bin/Debug/net10.0/

Build Specific Project

# Core library only
dotnet build src/Intune.Commander.Core

# Desktop app only
dotnet build src/Intune.Commander.Desktop

# Tests only
dotnet build tests/Intune.Commander.Core.Tests

Clean Build

# Clean all build outputs
dotnet clean

# Clean and rebuild
dotnet clean && dotnet build

Restore Dependencies

# Restore NuGet packages
dotnet restore

# Restore and build
dotnet restore && dotnet build

Running the Application

Run from Source

# Run desktop application
dotnet run --project src/Intune.Commander.Desktop

# Run in Release mode
dotnet run --project src/Intune.Commander.Desktop --configuration Release

Run with Debugger

Visual Studio:
  1. Open Intune.Commander.sln
  2. Set Intune.Commander.Desktop as startup project
  3. Press F5 or click “Start Debugging”
VS Code:
  1. Open folder in VS Code
  2. Install C# Dev Kit extension
  3. Press F5 or use Run and Debug panel
Rider:
  1. Open Intune.Commander.sln
  2. Right-click Intune.Commander.Desktop → “Run”

Application Data Locations

During development, the app stores data in: Windows:
  • Profiles: %LOCALAPPDATA%\Intune.Commander\profiles.json
  • Cache: %LOCALAPPDATA%\Intune.Commander\cache.db
  • Encryption keys: %LOCALAPPDATA%\Intune.Commander\keys\
Linux:
  • Profiles: ~/.config/Intune.Commander/profiles.json
  • Cache: ~/.config/Intune.Commander/cache.db
  • Encryption keys: ~/.config/Intune.Commander/keys/
macOS:
  • Profiles: ~/Library/Application Support/Intune.Commander/profiles.json
  • Cache: ~/Library/Application Support/Intune.Commander/cache.db
  • Encryption keys: ~/Library/Application Support/Intune.Commander/keys/

Running Tests

Unit Tests Only (Fast)

# Run all unit tests (excludes integration tests)
dotnet test --filter "Category!=Integration"

# Run with detailed output
dotnet test --filter "Category!=Integration" --verbosity detailed
Typical run time: 5-15 seconds

Unit Tests with Coverage

# Run with coverage (40% threshold enforced)
dotnet test /p:CollectCoverage=true /p:Threshold=40 /p:ThresholdType=line /p:ThresholdStat=total

# Generate coverage report
dotnet test /p:CollectCoverage=true /p:CoverletOutputFormat=cobertura /p:CoverletOutput=./coverage/
Coverage report location:
tests/Intune.Commander.Core.Tests/coverage/coverage.cobertura.xml

Integration Tests (Slow)

Integration tests require Azure credentials: Windows (PowerShell):
$env:AZURE_TENANT_ID = "your-tenant-id"
$env:AZURE_CLIENT_ID = "your-client-id"
$env:AZURE_CLIENT_SECRET = "your-client-secret"

dotnet test --filter "Category=Integration"
Linux/macOS (Bash):
export AZURE_TENANT_ID="your-tenant-id"
export AZURE_CLIENT_ID="your-client-id"
export AZURE_CLIENT_SECRET="your-client-secret"

dotnet test --filter "Category=Integration"
Typical run time: 1-5 minutes (depends on tenant size) Setup integration test app:
# Run from repository root
.\scripts\Setup-IntegrationTestApp.ps1
See Testing Guide for detailed integration test setup.

Run Specific Test

# Run specific test class
dotnet test --filter "FullyQualifiedName~ProfileServiceTests"

# Run specific test method
dotnet test --filter "FullyQualifiedName~ProfileServiceTests.LoadAsync_ReturnsProfileStore_WhenFileExists"

Run All Tests

# Unit + Integration (requires Azure credentials)
dotnet test

# With coverage
dotnet test /p:CollectCoverage=true

Publishing Release Builds

Self-Contained Windows Executable

# Publish for Windows x64
dotnet publish src/Intune.Commander.Desktop \
  --configuration Release \
  --runtime win-x64 \
  --self-contained \
  --output ./publish/win-x64
Output:
publish/win-x64/Intune.Commander.Desktop.exe
Self-contained: Includes .NET runtime, no separate .NET installation required.

Framework-Dependent Build

# Requires .NET 10 runtime on target machine
dotnet publish src/Intune.Commander.Desktop \
  --configuration Release \
  --runtime win-x64 \
  --output ./publish/win-x64-framework
Smaller output size, but requires .NET 10 runtime installed.

Publish with Syncfusion License

# Pass license key at publish time
dotnet publish src/Intune.Commander.Desktop \
  --configuration Release \
  --runtime win-x64 \
  --self-contained \
  -p:SyncfusionLicenseKey="YOUR-LICENSE-KEY" \
  --output ./publish/win-x64
Never commit the license key to source control. Use environment variables or CI secrets.

Other Platforms

# Linux x64
dotnet publish src/Intune.Commander.Desktop \
  --configuration Release \
  --runtime linux-x64 \
  --self-contained \
  --output ./publish/linux-x64

# macOS x64
dotnet publish src/Intune.Commander.Desktop \
  --configuration Release \
  --runtime osx-x64 \
  --self-contained \
  --output ./publish/osx-x64

# macOS ARM64 (Apple Silicon)
dotnet publish src/Intune.Commander.Desktop \
  --configuration Release \
  --runtime osx-arm64 \
  --self-contained \
  --output ./publish/osx-arm64
Note: Linux and macOS builds are supported by Avalonia but not actively tested.

Development Workflow

Typical Development Cycle

  1. Create feature branch:
    git checkout -b feature/my-feature
    
  2. Make changes in your IDE
  3. Build and test frequently:
    dotnet build
    dotnet test --filter "Category!=Integration"
    
  4. Run the app to verify:
    dotnet run --project src/Intune.Commander.Desktop
    
  5. Check coverage before committing:
    dotnet test /p:CollectCoverage=true /p:Threshold=40
    
  6. Commit changes:
    git add .
    git commit -m "feat(services): add new Graph service"
    
  7. Push and create PR:
    git push -u origin feature/my-feature
    gh pr create
    
See Contributing Guide for PR guidelines.

Debugging Tips

Enable Avalonia DevTools (Debug builds only): Press F12 while app is running to open Avalonia DevTools:
  • Visual tree inspector
  • Property editor
  • Event viewer
  • Style debugger
DebugLogWindow: View in-memory debug logs:
  • Menu: View → Debug Log
  • Shows last 2000 log entries
  • All Graph API calls logged here
See src/Intune.Commander.Desktop/Services/DebugLogService.cs. Verbose logging:
DebugLog.Log("CategoryName", "Message");
DebugLog.LogError("CategoryName", $"Error: {ex.Message}");

Hot Reload

.NET Hot Reload is supported for code changes: Visual Studio:
  • Automatic hot reload while debugging
  • Look for “Hot Reload” icon in toolbar
VS Code / Rider:
  • May require manual restart for some changes
XAML Hot Reload:
  • Avalonia XAML hot reload is supported in Visual Studio
  • Install Avalonia for Visual Studio extension

CI/CD Workflows

CI — Test & Coverage

Trigger: All pushes + PRs to main File: .github/workflows/ci-test.yml What it does:
  1. Setup .NET 10 SDK
  2. Restore dependencies
  3. Build in Release mode
  4. Run unit tests (excludes integration tests)
  5. Enforce 40% line coverage
  6. Upload coverage report as artifact
Locally replicate CI:
dotnet restore
dotnet build --configuration Release
dotnet test --configuration Release --no-build --filter "Category!=Integration" \
  /p:CollectCoverage=true /p:Threshold=40 /p:ThresholdType=line /p:ThresholdStat=total

CI — Integration Tests

Trigger: Push/PR to main + manual dispatch File: .github/workflows/ci-integration.yml What it does:
  1. Setup .NET 10 SDK
  2. Restore dependencies
  3. Build solution
  4. Run integration tests with credentials from secrets
Required repository secrets:
  • AZURE_TENANT_ID
  • AZURE_CLIENT_ID
  • AZURE_CLIENT_SECRET

Build Release

Trigger: All pushes + PRs File: .github/workflows/build-release.yml What it does:
  1. Build self-contained Windows x64 executable
  2. Upload artifact
Download artifact:
  • Go to Actions tab in GitHub
  • Click on workflow run
  • Download “Intune.Commander-win-x64” artifact

Troubleshooting

Build Errors

“SDK version not found”:
# Verify .NET 10 is installed
dotnet --list-sdks

# Should show: 10.0.xxx
“Package restore failed”:
# Clear NuGet cache
dotnet nuget locals all --clear

# Restore again
dotnet restore
“Could not find a part of the path”:
  • Windows has path length limits (~260 chars)
  • Clone repo to shorter path (e.g., C:\src\intune-commander)
  • Or enable long paths: git config --system core.longpaths true

Test Errors

“Coverage threshold not met”:
# Check current coverage
dotnet test /p:CollectCoverage=true /p:CoverletOutputFormat=cobertura

# Coverage report shows which files need more tests
Integration tests fail “ShouldSkip()”:
  • Environment variables not set
  • Set AZURE_TENANT_ID, AZURE_CLIENT_ID, AZURE_CLIENT_SECRET
  • Run Setup-IntegrationTestApp.ps1 first

Runtime Errors

“profiles.json access denied”:
  • Close any other instances of the app
  • Check folder permissions: %LOCALAPPDATA%\Intune.Commander\
“Failed to create GraphServiceClient”:
  • Check tenant ID and client ID are correct
  • Verify app registration has required permissions
  • Check cloud environment matches (Commercial vs GCC-High vs DoD)
“MSAL token cache error”:
  • Delete token cache: %LOCALAPPDATA%\.IdentityService\
  • Sign in again

IDE Configuration

Visual Studio 2022

Recommended extensions:
  • Avalonia for Visual Studio (XAML preview and hot reload)
  • GitHub Copilot (optional)
Solution file: Intune.Commander.sln Startup project: Intune.Commander.Desktop

JetBrains Rider

Recommended plugins:
  • AvaloniaRider (XAML support)
  • .NET Core User Secrets (manage secrets)
Solution file: Intune.Commander.sln

VS Code

Required extensions:
  • C# Dev Kit (Microsoft)
  • C# (Microsoft)
Recommended extensions:
  • Avalonia for VSCode (XAML support)
  • GitLens (Git integration)
Open folder: Repository root Tasks: Defined in .vscode/tasks.json (if present)

Project Structure Reference

intune-commander/
├── .github/
│   └── workflows/           # CI/CD workflows
│       ├── ci-test.yml
│       ├── ci-integration.yml
│       └── build-release.yml
├── docs/                    # Architecture and design docs
│   ├── ARCHITECTURE.md
│   ├── GRAPH-PERMISSIONS.md
│   └── issues/              # Wave tracking
├── scripts/                 # PowerShell setup scripts
│   └── Setup-IntegrationTestApp.ps1
├── src/
│   ├── Intune.Commander.Core/          # Business logic
│   │   ├── Auth/
│   │   ├── Models/
│   │   ├── Services/
│   │   └── Extensions/
│   └── Intune.Commander.Desktop/       # Avalonia UI
│       ├── Views/
│       ├── ViewModels/
│       ├── Services/
│       └── Converters/
├── tests/
│   └── Intune.Commander.Core.Tests/    # xUnit tests
│       ├── Auth/
│       ├── Models/
│       ├── Services/
│       └── Integration/
├── CHANGELOG.md             # Release notes
├── CLAUDE.md                # AI coding assistant guide
├── CONTRIBUTING.md          # Contribution guidelines
├── README.md                # Getting started
└── Intune.Commander.sln     # Solution file

Build docs developers (and LLMs) love