-
Notifications
You must be signed in to change notification settings - Fork 0
BestPractices
This guide provides best practices and recommendations for using AzureDevOpsDscNative effectively in production environments.
- Configuration Management
- Security Best Practices
- Performance Optimization
- Error Handling & Troubleshooting
- Large-Scale Deployments
- Testing & Validation
- Maintenance & Updates
Separate configurations for different environments:
# Structure example
.
├── Configurations
│ ├── Dev
│ │ ├── BaseConfig.ps1
│ │ └── Services.ps1
│ ├── Staging
│ │ ├── BaseConfig.ps1
│ │ └── Services.ps1
│ └── Production
│ ├── BaseConfig.ps1
│ └── Services.ps1
├── Common
│ └── SharedFunctions.ps1
└── Deploy.ps1Separate data from configuration logic:
# ConfigurationData.psd1
@{
AllNodes = @(
@{
NodeName = 'localhost'
Environment = 'Production'
OrganizationName = 'ProdOrg'
Projects = @(
@{ Name = 'Project1'; Template = 'Agile' }
@{ Name = 'Project2'; Template = 'Scrum' }
)
}
)
}
# Configuration.ps1
Configuration DeployAzureDevOps {
Param([hashtable]$ConfigurationData)
Import-DscResource -ModuleName 'AzureDevOpsDscNative'
Node $AllNodes.NodeName {
foreach ($project in $Node.Projects) {
AzDoProject "Project_$($project.Name)" {
Ensure = 'Present'
ProjectName = $project.Name
ProcessTemplate = $project.Template
SourceControlType = 'Git'
Visibility = 'Private'
}
}
}
}
# Usage
$data = Import-PowerShellDataFile -Path ConfigurationData.psd1
DeployAzureDevOps -ConfigurationData $dataEnsure configurations are idempotent (safe to run multiple times):
# Good: Idempotent
Configuration IdempotentConfig {
Import-DscResource -ModuleName 'AzureDevOpsDscNative'
Node localhost {
AzDoProject 'MyProject' {
Ensure = 'Present'
ProjectName = 'MyProject'
SourceControlType = 'Git'
ProcessTemplate = 'Agile'
Visibility = 'Private'
}
# This will only be applied if project is present
AzDoProjectGroup 'ProjectAdmins' {
Ensure = 'Present'
ProjectName = 'MyProject'
GroupName = 'Project Admins'
DependsOn = '[AzDoProject]MyProject'
}
}
}
# Bad: Not idempotent - will fail if run twice
Configuration NonIdempotent {
Node localhost {
Script CreateProject {
SetScript = {
# Direct API call without idempotence checking
}
}
}
}Explicitly define resource dependencies:
Configuration WithDependencies {
Import-DscResource -ModuleName 'AzureDevOpsDscNative'
Node localhost {
# Create project first
AzDoProject 'MyProject' {
Ensure = 'Present'
ProjectName = 'MyProject'
SourceControlType = 'Git'
ProcessTemplate = 'Agile'
Visibility = 'Private'
}
# Create repo only after project exists
AzDoGitRepository 'MainRepo' {
Ensure = 'Present'
ProjectName = 'MyProject'
RepositoryName = 'MainRepository'
DependsOn = '[AzDoProject]MyProject'
}
# Configure permissions only after repo exists
AzDoGitPermission 'RepoAccess' {
RepositoryName = 'MainRepository'
ProjectName = 'MyProject'
IdentityName = 'Project Admins'
PermissionName = 'Contribute'
Allow = $true
DependsOn = '[AzDoGitRepository]MainRepo'
}
}
}Use version control for all configurations:
# Version configuration files
git tag -a v1.0.0 -m "Initial DSC configuration"
# Document changes
<#
v1.0.0 - Initial Release
- Basic project setup
- Team management
- Repository configuration
v1.1.0 - Added Pipeline Support
- Pipeline creation
- Environment management
- Check configuration
#># Bad: Credentials in configuration
Configuration BadCredentials {
Node localhost {
# NEVER DO THIS
$token = 'your-pat-token-hardcoded'
}
}
# Good: Use secure storage
Configuration GoodCredentials {
Node localhost {
# Retrieve from secure storage
$token = Get-Secret -Name 'AzureDevOpsPAT' -AsPlainText
}
}Configuration SecureExecution {
Import-DscResource -ModuleName 'AzureDevOpsDscNative'
Node localhost {
# Run with specific credential
AzDoProject 'MyProject' {
Ensure = 'Present'
ProjectName = 'MyProject'
SourceControlType = 'Git'
ProcessTemplate = 'Agile'
Visibility = 'Private'
PsDscRunAsCredential = $credential
}
}
}Configuration RBACSetup {
Import-DscResource -ModuleName 'AzureDevOpsDscNative'
Node localhost {
# Create role-specific groups
AzDoOrganizationGroup 'Developers' {
Ensure = 'Present'
GroupName = 'Developers'
GroupDescription = 'Development team members'
}
AzDoOrganizationGroup 'Admins' {
Ensure = 'Present'
GroupName = 'Admins'
GroupDescription = 'Azure DevOps administrators'
}
# Assign minimal required permissions
AzDoGroupPermission 'DeveloperPermissions' {
GroupName = 'Developers'
PermissionName = 'Create Repository'
Allow = $true
DependsOn = '[AzDoOrganizationGroup]Developers'
}
AzDoGroupPermission 'AdminPermissions' {
GroupName = 'Admins'
PermissionName = 'Administer'
Allow = $true
DependsOn = '[AzDoOrganizationGroup]Admins'
}
}
}# Enable DSC logging
Enable-DscDebug -Force
# Check configuration status
Get-DscConfigurationStatus -All
# Review compliance
Get-DscConfigurationStatus | Where-Object Type -eq 'Consistency'Configuration LeastPrivilege {
Import-DscResource -ModuleName 'AzureDevOpsDscNative'
Node localhost {
# Grant only necessary permissions
AzDoGitPermission 'ReadOnlyAccess' {
RepositoryName = 'MainRepository'
ProjectName = 'MyProject'
IdentityName = 'Readers'
PermissionName = 'GenericRead'
Allow = $true
}
AzDoGitPermission 'ContributorAccess' {
RepositoryName = 'MainRepository'
ProjectName = 'MyProject'
IdentityName = 'Contributors'
PermissionName = 'Contribute'
Allow = $true
}
}
}Group related resource configurations:
Configuration OptimizedBatching {
Import-DscResource -ModuleName 'AzureDevOpsDscNative'
Node localhost {
# Create all projects together
$projects = @('Project1', 'Project2', 'Project3')
foreach ($projectName in $projects) {
AzDoProject "Project_$projectName" {
Ensure = 'Present'
ProjectName = $projectName
SourceControlType = 'Git'
ProcessTemplate = 'Agile'
Visibility = 'Private'
}
}
}
}# Configure independent resources in parallel
Configuration ParallelConfiguration {
Import-DscResource -ModuleName 'AzureDevOpsDscNative'
Node localhost {
# These can run in parallel (no dependencies)
AzDoProject 'Project1' {
Ensure = 'Present'
ProjectName = 'Project1'
SourceControlType = 'Git'
ProcessTemplate = 'Agile'
Visibility = 'Private'
}
AzDoProject 'Project2' {
Ensure = 'Present'
ProjectName = 'Project2'
SourceControlType = 'Git'
ProcessTemplate = 'Scrum'
Visibility = 'Private'
}
# These must wait for projects above (have dependencies)
AzDoGitRepository 'Repo1' {
Ensure = 'Present'
ProjectName = 'Project1'
RepositoryName = 'Repository1'
DependsOn = '[AzDoProject]Project1'
}
}
}# Measure configuration execution time
$startTime = Get-Date
$config | Start-DscConfiguration -Wait -Verbose
$endTime = Get-Date
Write-Host "Configuration took $($endTime - $startTime) to complete"Configuration WithErrorHandling {
Import-DscResource -ModuleName 'AzureDevOpsDscNative'
Node localhost {
AzDoProject 'MyProject' {
Ensure = 'Present'
ProjectName = 'MyProject'
SourceControlType = 'Git'
ProcessTemplate = 'Agile'
Visibility = 'Private'
ErrorAction = 'Stop'
}
}
}
# Execute with error handling
try {
$config | Start-DscConfiguration -Wait -Verbose -ErrorAction Stop
}
catch {
Write-Error "DSC configuration failed: $_"
# Implement recovery logic
}# Enable verbose output for troubleshooting
$config | Start-DscConfiguration -Wait -Verbose
# Check DSC event log
Get-WinEvent -LogName 'DSC/Operational' | Select-Object TimeCreated, Message | Out-GridViewConfiguration TestConfiguration {
Import-DscResource -ModuleName 'AzureDevOpsDscNative'
Node localhost {
# Your configuration here
}
}
# Test without applying changes
Test-DscConfiguration -Path ./TestConfiguration -Verbose
# Check what would change
Compare-DscConfiguration -ReferenceConfiguration ./TestConfigurationFor large-scale deployments across many projects and teams, the recommended approach is to use the companion Dsc.PipelineRunner — a custom pipeline runner designed to execute DSC configurations within CI/CD pipelines. It is built on Datum and enables layered YAML configuration merging, dependency ordering, conditional logic, and organisation-wide policy enforcement without writing individual DSC configuration scripts per project.
See the Pipeline Runner Configuration wiki page for a full walkthrough and example directory layout. Example configurations are available in the Dsc.PipelineRunner repository.
$params = @{
AzureDevopsOrganizationName = 'my-organization'
ConfigurationDirectory = 'C:\Datum\DSCOutput\'
ConfigurationUrl = 'https://my-config-repo/path'
AuthenticationType = 'ManagedIdentity'
Mode = 'Set'
ReportPath = 'C:\Datum\DSCOutput\Reports'
}
Invoke-DscPipelineRunner @paramsResource configuration stubs are written in YAML and merged by Datum. Organisation-wide policy sits at the top of the merge precedence; per-project files override it.
resources:
- name: Project
type: AzureDevOpsDscNative/AzDoProject
properties:
projectName: $ProjectName
projectDescription: $ProjectDescription
visibility: private
SourceControlType: Git
ProcessTemplate: Agile| Scenario | Use |
|---|---|
| Single project, small team | Direct Invoke-DscResource or DSC configuration script |
| Multiple projects, shared policy | Dsc.PipelineRunner (recommended) |
| CI/CD pipeline at org scale | Dsc.PipelineRunner with Managed Identity or Workload Identity |
| One-off or exploratory changes | Direct Invoke-DscResource
|
# Create Pester tests for configurations
Describe 'Azure DevOps DSC Configuration' {
It 'Should create project' {
# Mock the Azure DevOps API
Mock Get-DscResource { return $true }
# Test your configuration
{ MyConfiguration | Start-DscConfiguration -Wait } | Should -Not -Throw
}
}
# Run tests
Invoke-Pester -Path .\ConfigurationTests.psd1 -Verbose# Test against actual Azure DevOps instance
Describe 'Azure DevOps Integration' {
It 'Should create and verify project' {
$config | Start-DscConfiguration -Wait
# Verify the resource was created
Get-AzDoProject -ProjectName 'TestProject' | Should -Not -BeNullOrEmpty
}
AfterAll {
# Clean up test resources
Remove-AzDoProject -ProjectName 'TestProject'
}
}# Check for module updates
Find-Module AzureDevOpsDscNative | Select-Object Name, Version
# Update to latest version
Update-Module -Name AzureDevOpsDscNative
# Verify update
Get-Module AzureDevOpsDscNative -ListAvailable | Sort-Object Version# Keep detailed changelog
<#
CHANGELOG.md
## [2.0.0] - 2025-01-15
### Added
- Support for new pipeline features
- Environment permissions management
### Changed
- Updated authentication module
- Improved error messages
### Deprecated
- Legacy authentication method
### Fixed
- Bug in permission assignment
- Resource naming issue
#># Check for configuration drift
Get-DscConfigurationStatus
# Monitor compliance over time
$status = Get-DscConfigurationStatus
if ($status.ResourcesNotInDesiredState.Count -gt 0) {
Write-Warning "Configuration drift detected"
# Take corrective action
Start-DscConfiguration -Path ./Configuration -Wait
}# Regular backups of configurations
$backupPath = "C:\Backups\DSC\$(Get-Date -Format 'yyyyMMdd')"
Copy-Item -Path 'C:\DSC\Configurations' -Destination $backupPath -Recurse
# Version control
git commit -m "Configuration backup $(Get-Date -Format 'yyyy-MM-dd')"Configuration Management
- Organize configurations by environment
- Use configuration data files
- Implement idempotency
- Define dependencies explicitly
- Version configurations
Security
- Never hardcode credentials
- Use secure credential storage
- Implement RBAC
- Audit changes
- Apply least privilege
Performance
- Batch related resources
- Use parallel processing
- Monitor performance
Reliability
- Handle errors properly
- Enable logging
- Test before deployment
- Progressive rollout
- Monitor drift
Maintenance
- Keep module updated
- Document changes
- Monitor compliance
- Regular backups
- Assert-BoundParameter
- Assert-ElevatedUser
- Assert-IPAddress
- Assert-Module
- AzDoAPI_0_ProjectCache
- AzDoAPI_1_GroupCache
- AzDoAPI_2_UserCache
- AzDoAPI_3_GroupMemberCache
- AzDoAPI_4_GitRepositoryCache
- AzDoAPI_5_PermissionsCache
- AzDoAPI_6_ServicePrinciple
- AzDoAPI_7_IdentitySubjectDescriptors
- AzDoAPI_8_ProjectProcessTemplates
- AzDoAPI_9_DevOpsClassificationNodes
- Compare-DscParameterState
- Compare-ResourcePropertyState
- ConvertFrom-DscResourceInstance
- ConvertTo-Base64String
- ConvertTo-CimInstance
- ConvertTo-HashTable
- Find-Certificate
- Format-Path
- Get-AzDevOpsOperation
- Get-AzDevOpsServicesApiUri
- Get-AzDevOpsServicesUri
- AzDoAgentPool
- AzDoAgentPoolPermission
- AzDoAgentQueue
- AzDoAreaNodes
- AzDoAreaPermission
- AzDoArtifactFeed
- AzDoArtifactFeedPermission
- AzDoArtifactFeedSettings
- AzDoArtifactFeedView
- AzDoAuditStream
- AzDoBranchPolicy
- AzDoCheckConfiguration
- AzDoDeploymentGroup
- AzDoEnvironmentApproval
- AzDoEnvironmentPermission
- AzDoExtension
- AzDoGitPermission
- AzDoGitRepository
- AzDoGroupMember
- AzDoGroupPermission
- AzDoIterationNodes
- AzDoIterationPermission
- AzDoNotificationSubscription
- AzDoOrganizationGroup
- AzDoOrganizationSettings
- AzDoPipeline
- AzDoPipelineEnvironment
- AzDoPipelinePermission
- AzDoPipelineSettings
- AzDoProcess
- AzDoProcessPermission
- AzDoProject
- AzDoProjectGroup
- AzDoProjectPermission
- AzDoProjectServices
- AzDoRepositorySettings
- AzDoSecurityNamespacePermission
- AzDoServiceConnection
- AzDoServiceConnectionPermission
- AzDoServiceHook
- AzDoTaskGroup
- AzDoTeam
- AzDoTeamMember
- AzDoTeamSettings
- AzDoUserEntitlement
- AzDoVariableGroup
- AzDoVariableGroupPermission
- AzDoWiki
- AzDoWIPTags