AADSyncRuleCount: Add report-only rule-count DSC resource#32
Conversation
New class-based DSC resource that compares the expected number of AAD Connect sync rules against the current state and reports drift. Scoped per connector via ConnectorName, or across all connectors when ConnectorName is empty or '*'. Set() throws on drift; the resource never attempts to remediate count differences because that is not meaningful. Also registers the resource in the manifest, adds a basic example, and updates CHANGELOG and the memory bank.
WalkthroughThis PR ships a new DSC resource ChangesAADSyncRuleCount DSC Resource
🎯 3 (Moderate) | ⏱️ ~20 minutes 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
🧹 Nitpick comments (1)
source/Examples/Resources/AADSyncRuleCount/1-AADSyncRuleCount_Basic.ps1 (1)
1-8: ⚡ Quick winAdd required script help sections before the example.
The header currently includes only
.EXAMPLE. Please add at least.SYNOPSISand.DESCRIPTIONso the script-level help meets repository standards.♻️ Suggested update
<# + .SYNOPSIS + Demonstrates report-only validation of sync rule counts. + + .DESCRIPTION + Shows how to validate connector-specific and global Azure AD Connect + synchronization rule counts using the AADSyncRuleCount DSC resource. + .EXAMPLE 1 Verifies that the 'contoso.com' connector has exactly 42 sync rules. If the actual count differs, the resource throws an error during Set and the LCM marks the configuration as failed. The resource never attempts to add or remove rules to reach the expected count. #>As per coding guidelines, "Always add comment-based help to all functions and scripts" and include "SYNOPSIS, DESCRIPTION (40+ chars), PARAMETER, EXAMPLE sections".
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@source/Examples/Resources/AADSyncRuleCount/1-AADSyncRuleCount_Basic.ps1` around lines 1 - 8, The script example file 1-AADSyncRuleCount_Basic.ps1 only contains a .EXAMPLE help block; add full comment-based help by inserting at the top a .SYNOPSIS (one-line summary) and a .DESCRIPTION (40+ characters) above or adjacent to the existing .EXAMPLE, and also include PARAMETER stubs if the script accepts parameters so the help meets repository standards; ensure the new help uses the PowerShell comment-based help keywords (.SYNOPSIS, .DESCRIPTION, .PARAMETER, .EXAMPLE) and describes the purpose (e.g., verifying contoso.com has 42 sync rules) without changing the example logic.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.memory-bank/progress.md:
- Around line 25-27: Change the three numbered list items so they all use the
1/1/1 ordered-list numbering style: replace the leading "1.", "2.", "3." before
"Unit tests for AADSyncRuleCount (mock Get-ADSyncRule)", "Markdown lint sweep
across docs/ — minor formatting drift", and "Link validation for
internal/external links in README.md and" so each line begins with "1." to
satisfy MD029 and the project's Markdown guideline.
In `@source/Classes/AADSyncRuleCount.ps1`:
- Around line 38-44: Replace hardcoded user-facing and diagnostic strings in the
AADSyncRuleCount class (the Write-Verbose calls and TryWriteEventLog invocations
that produce the information/warning messages) with localized string keys and
lookup calls; e.g., introduce/consume a localization helper (Get-LocalizedString
or $this.Localize) and pass the RuleCount, scope, and CurrentRuleCount as format
arguments to that helper instead of embedding values in literal strings, and
update the other occurrences noted (the messages at the other
Write-Verbose/TryWriteEventLog sites referenced in the comment) to use the same
localization keys and helper so all
Write-Debug/Write-Verbose/Write-Error/Write-Warning and event log messages are
sourced from localized resources.
- Around line 1-105: Add unit and integration tests for the new DSC resource
AADSyncRuleCount: write unit tests that exercise AADSyncRuleCount::Test, ::Get,
::Set, ::GetScopeDescription and ::TryWriteEventLog using mocked Get-ADSyncRule
and Write-AADConnectEventLog to cover scenarios (all connectors vs specific
ConnectorName, zero rules, non-zero rules, and exception path when writing event
log), and add integration tests that deploy the resource to a test LCM/AD
Connect environment to validate behavior end-to-end (expected count, drift error
thrown by Set, and event log entries with EventId 1100–1102).
- Around line 1-3: Update the AADSyncRuleCount DSC resource to follow the DSC
class pattern: change the class attribute to [DscResource(RunAsCredential =
'Optional')], inherit from ResourceBase (class AADSyncRuleCount : ResourceBase)
and implement the required constructor that calls the base ctor; refactor the
Test(), Get(), and Set() methods to call the corresponding base class
implementations (e.g., base.Test(), base.Get(), base.Set()) as part of their
workflows; replace hardcoded Write-Verbose and error strings in methods (lines
referenced in the review) with localized messages (use the existing localization
mechanism used in other resources); and replace the raw throw $message on
failure in Set() with the appropriate New-*Exception cmdlet (e.g.,
New-ItemStateChangeException or the project-standard New-*-Exception helper)
while including the localized message.
- Around line 80-82: Replace the raw "throw $message" with a typed exception
created by New-InvalidOperationException to comply with class DSC resource
rules: after calling $this.TryWriteEventLog('Error', 1102, $message) change the
termination to "throw (New-InvalidOperationException -Message $message)" (or
equivalent New-*Exception helper) so the resource (AADSyncRuleCount class/method
using $this.TryWriteEventLog and throw $message) raises a proper
InvalidOperationException rather than a raw throw.
---
Nitpick comments:
In `@source/Examples/Resources/AADSyncRuleCount/1-AADSyncRuleCount_Basic.ps1`:
- Around line 1-8: The script example file 1-AADSyncRuleCount_Basic.ps1 only
contains a .EXAMPLE help block; add full comment-based help by inserting at the
top a .SYNOPSIS (one-line summary) and a .DESCRIPTION (40+ characters) above or
adjacent to the existing .EXAMPLE, and also include PARAMETER stubs if the
script accepts parameters so the help meets repository standards; ensure the new
help uses the PowerShell comment-based help keywords (.SYNOPSIS, .DESCRIPTION,
.PARAMETER, .EXAMPLE) and describes the purpose (e.g., verifying contoso.com has
42 sync rules) without changing the example logic.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 69d767b1-23c9-4f7b-9201-64d772b5738c
📒 Files selected for processing (6)
.memory-bank/activeContext.md.memory-bank/progress.mdCHANGELOG.mdsource/AADConnectDsc.psd1source/Classes/AADSyncRuleCount.ps1source/Examples/Resources/AADSyncRuleCount/1-AADSyncRuleCount_Basic.ps1
| 1. **Unit tests** for ``AADSyncRuleCount`` (mock ``Get-ADSyncRule``). | ||
| 2. **Markdown lint sweep** across ``docs/`` — minor formatting drift. | ||
| 3. **Link validation** for internal/external links in ``README.md`` and |
There was a problem hiding this comment.
Use 1/1/1 ordered-list numbering to satisfy markdown rules.
This section currently uses 1., 2., 3. and triggers MD029 warnings.
✅ Minimal fix
-1. **Unit tests** for ``AADSyncRuleCount`` (mock ``Get-ADSyncRule``).
-2. **Markdown lint sweep** across ``docs/`` — minor formatting drift.
-3. **Link validation** for internal/external links in ``README.md`` and
+1. **Unit tests** for ``AADSyncRuleCount`` (mock ``Get-ADSyncRule``).
+1. **Markdown lint sweep** across ``docs/`` — minor formatting drift.
+1. **Link validation** for internal/external links in ``README.md`` andAs per coding guidelines, Markdown requires "Use '1.' for all items in ordered lists (1/1/1 numbering style)".
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| 1. **Unit tests** for ``AADSyncRuleCount`` (mock ``Get-ADSyncRule``). | |
| 2. **Markdown lint sweep** across ``docs/`` — minor formatting drift. | |
| 3. **Link validation** for internal/external links in ``README.md`` and | |
| 1. **Unit tests** for ``AADSyncRuleCount`` (mock ``Get-ADSyncRule``). | |
| 1. **Markdown lint sweep** across ``docs/`` — minor formatting drift. | |
| 1. **Link validation** for internal/external links in ``README.md`` and |
🧰 Tools
🪛 markdownlint-cli2 (0.22.1)
[warning] 26-26: Ordered list item prefix
Expected: 1; Actual: 2; Style: 1/1/1
(MD029, ol-prefix)
[warning] 27-27: Ordered list item prefix
Expected: 1; Actual: 3; Style: 1/1/1
(MD029, ol-prefix)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.memory-bank/progress.md around lines 25 - 27, Change the three numbered
list items so they all use the 1/1/1 ordered-list numbering style: replace the
leading "1.", "2.", "3." before "Unit tests for AADSyncRuleCount (mock
Get-ADSyncRule)", "Markdown lint sweep across docs/ — minor formatting drift",
and "Link validation for internal/external links in README.md and" so each line
begins with "1." to satisfy MD029 and the project's Markdown guideline.
| [DscResource()] | ||
| class AADSyncRuleCount | ||
| { | ||
| <# | ||
| .SYNOPSIS | ||
| Verifies the number of Azure AD Connect synchronization rules. | ||
|
|
||
| .DESCRIPTION | ||
| Read-only / report-only DSC resource that compares the expected | ||
| number of AAD Connect sync rules against the current state and | ||
| reports a drift if they differ. The resource never attempts to | ||
| remediate drift because creating or removing rules to match a | ||
| count is not a meaningful operation; the user must investigate | ||
| and reconcile manually. | ||
|
|
||
| Use `ConnectorName` to scope the count to a specific connector. | ||
| Leave `ConnectorName` empty (or set it to `*`) to count every | ||
| sync rule across all connectors. | ||
| #> | ||
|
|
||
| # `ConnectorName` is the key. Empty string or '*' means "all connectors". | ||
| [DscProperty(Key = $true)] | ||
| [string]$ConnectorName | ||
|
|
||
| [DscProperty(Mandatory = $true)] | ||
| [uint32]$RuleCount | ||
|
|
||
| [DscProperty(NotConfigurable)] | ||
| [uint32]$CurrentRuleCount | ||
|
|
||
| [bool]Test() | ||
| { | ||
| $currentState = $this.Get() | ||
| $scope = $this.GetScopeDescription() | ||
|
|
||
| if ($currentState.CurrentRuleCount -eq $this.RuleCount) | ||
| { | ||
| Write-Verbose -Message "AADSyncRuleCount: $scope has the expected $($this.RuleCount) sync rule(s)." | ||
| $this.TryWriteEventLog('Information', 1100, "AADSyncRuleCount in desired state for $scope (count=$($this.RuleCount)).") | ||
| return $true | ||
| } | ||
|
|
||
| Write-Verbose -Message "AADSyncRuleCount: $scope has $($currentState.CurrentRuleCount) sync rule(s) but expected $($this.RuleCount)." | ||
| $this.TryWriteEventLog('Warning', 1101, "AADSyncRuleCount drift for $scope. Expected=$($this.RuleCount); Current=$($currentState.CurrentRuleCount).") | ||
| return $false | ||
| } | ||
|
|
||
| [AADSyncRuleCount]Get() | ||
| { | ||
| $currentState = [AADSyncRuleCount]::new() | ||
| $currentState.ConnectorName = $this.ConnectorName | ||
| $currentState.RuleCount = $this.RuleCount | ||
|
|
||
| $rules = if ([string]::IsNullOrEmpty($this.ConnectorName) -or $this.ConnectorName -eq '*') | ||
| { | ||
| Get-ADSyncRule | ||
| } | ||
| else | ||
| { | ||
| Get-ADSyncRule -ConnectorName $this.ConnectorName | ||
| } | ||
|
|
||
| # Get-ADSyncRule may return $null when no rules match; coerce to 0. | ||
| $currentState.CurrentRuleCount = if ($null -eq $rules) { 0 } else { @($rules).Count } | ||
|
|
||
| return $currentState | ||
| } | ||
|
|
||
| [void]Set() | ||
| { | ||
| # This resource is report-only. Adjusting the rule count automatically | ||
| # is not safe and not meaningful — the operator must investigate which | ||
| # rules are missing or extra. Throw a clear error so the LCM marks the | ||
| # configuration as failed. | ||
|
|
||
| $currentState = $this.Get() | ||
| $scope = $this.GetScopeDescription() | ||
| $message = "AADSyncRuleCount drift detected for $scope. Expected $($this.RuleCount) sync rule(s) but found $($currentState.CurrentRuleCount). This resource does not remediate count drift; investigate the AAD Connect configuration manually." | ||
|
|
||
| $this.TryWriteEventLog('Error', 1102, $message) | ||
| throw $message | ||
| } | ||
|
|
||
| hidden [string]GetScopeDescription() | ||
| { | ||
| if ([string]::IsNullOrEmpty($this.ConnectorName) -or $this.ConnectorName -eq '*') | ||
| { | ||
| return "all connectors" | ||
| } | ||
|
|
||
| return "connector '$($this.ConnectorName)'" | ||
| } | ||
|
|
||
| hidden [void]TryWriteEventLog([string]$EventType, [int]$EventId, [string]$Message) | ||
| { | ||
| try | ||
| { | ||
| Write-AADConnectEventLog -EventType $EventType -EventId $EventId -Message $Message -ConnectorName $this.ConnectorName | ||
| } | ||
| catch | ||
| { | ||
| Write-Verbose -Message "Failed to write event log entry: $($_.Exception.Message)" | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major | 🏗️ Heavy lift
Add unit and integration tests for the new resource before merge.
This PR adds a new DSC resource, but no corresponding resource tests are included in the reviewed changes.
As per coding guidelines, Add unit tests for all commands/functions/resources and Add integration tests for all public commands and resources.
🧰 Tools
🪛 PSScriptAnalyzer (1.25.0)
[warning] Missing BOM encoding for non-ASCII encoded file 'AADSyncRuleCount.ps1'
(PSUseBOMForUnicodeEncodedFile)
[info] 1-105: No examples found for resource 'AADSyncRuleCount'
(PSDSCDscExamplesPresent)
[info] 1-105: No tests found for resource 'AADSyncRuleCount'
(PSDSCDscTestsPresent)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@source/Classes/AADSyncRuleCount.ps1` around lines 1 - 105, Add unit and
integration tests for the new DSC resource AADSyncRuleCount: write unit tests
that exercise AADSyncRuleCount::Test, ::Get, ::Set, ::GetScopeDescription and
::TryWriteEventLog using mocked Get-ADSyncRule and Write-AADConnectEventLog to
cover scenarios (all connectors vs specific ConnectorName, zero rules, non-zero
rules, and exception path when writing event log), and add integration tests
that deploy the resource to a test LCM/AD Connect environment to validate
behavior end-to-end (expected count, drift error thrown by Set, and event log
entries with EventId 1100–1102).
| [DscResource()] | ||
| class AADSyncRuleCount | ||
| { |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major | 🏗️ Heavy lift
🧩 Analysis chain
🏁 Script executed:
# First, let's examine the file being reviewed
cat -n source/Classes/AADSyncRuleCount.ps1Repository: dsccommunity/AADConnectDsc
Length of output: 4633
🏁 Script executed:
# Check if ResourceBase exists in the module
fd -type f "ResourceBase" source/Repository: dsccommunity/AADConnectDsc
Length of output: 241
🏁 Script executed:
# Check for existing DSC resources in the module to understand the pattern
find source/Classes -name "*.ps1" -type f | head -5 | xargs -I {} sh -c 'echo "=== {} ===" && head -15 {}'Repository: dsccommunity/AADConnectDsc
Length of output: 1810
Restructure resource to follow the required DSC class pattern.
The resource deviates from mandatory guidelines: missing RunAsCredential decoration, ResourceBase inheritance, required constructor, and base method calls. Additionally, hardcoded strings violate localization requirements, and line 81 uses throw instead of New-*Exception for error handling.
Per coding guidelines:
- Line 1: Change
[DscResource()]to[DscResource(RunAsCredential = 'Optional')] - Line 2: Add
: ResourceBaseinheritance and implement required constructor - Lines 31-67, 69-82: Restructure Test(), Get(), Set() to call base class implementations
- Lines 38, 43, 78, 81, 102: Localize hardcoded Write-Verbose and error messages
- Line 81: Replace
throw $messagewith appropriateNew-*Exceptioncommand
🧰 Tools
🪛 PSScriptAnalyzer (1.25.0)
[warning] Missing BOM encoding for non-ASCII encoded file 'AADSyncRuleCount.ps1'
(PSUseBOMForUnicodeEncodedFile)
[info] 1-105: No examples found for resource 'AADSyncRuleCount'
(PSDSCDscExamplesPresent)
[info] 1-105: No tests found for resource 'AADSyncRuleCount'
(PSDSCDscTestsPresent)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@source/Classes/AADSyncRuleCount.ps1` around lines 1 - 3, Update the
AADSyncRuleCount DSC resource to follow the DSC class pattern: change the class
attribute to [DscResource(RunAsCredential = 'Optional')], inherit from
ResourceBase (class AADSyncRuleCount : ResourceBase) and implement the required
constructor that calls the base ctor; refactor the Test(), Get(), and Set()
methods to call the corresponding base class implementations (e.g., base.Test(),
base.Get(), base.Set()) as part of their workflows; replace hardcoded
Write-Verbose and error strings in methods (lines referenced in the review) with
localized messages (use the existing localization mechanism used in other
resources); and replace the raw throw $message on failure in Set() with the
appropriate New-*Exception cmdlet (e.g., New-ItemStateChangeException or the
project-standard New-*-Exception helper) while including the localized message.
| Write-Verbose -Message "AADSyncRuleCount: $scope has the expected $($this.RuleCount) sync rule(s)." | ||
| $this.TryWriteEventLog('Information', 1100, "AADSyncRuleCount in desired state for $scope (count=$($this.RuleCount)).") | ||
| return $true | ||
| } | ||
|
|
||
| Write-Verbose -Message "AADSyncRuleCount: $scope has $($currentState.CurrentRuleCount) sync rule(s) but expected $($this.RuleCount)." | ||
| $this.TryWriteEventLog('Warning', 1101, "AADSyncRuleCount drift for $scope. Expected=$($this.RuleCount); Current=$($currentState.CurrentRuleCount).") |
There was a problem hiding this comment.
Localize all user-facing and diagnostic messages.
Hardcoded strings are used in Write-Verbose and drift/event messages (Line 38, Line 43, Line 78, Line 102). These should come from localized string keys.
As per coding guidelines, Localize all Write-Debug, Write-Verbose, Write-Error, Write-Warning and $PSCmdlet.ThrowTerminatingError() messages and Use localized string keys, not hardcoded strings.
Also applies to: 78-78, 102-102
🧰 Tools
🪛 PSScriptAnalyzer (1.25.0)
[warning] Missing BOM encoding for non-ASCII encoded file 'AADSyncRuleCount.ps1'
(PSUseBOMForUnicodeEncodedFile)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@source/Classes/AADSyncRuleCount.ps1` around lines 38 - 44, Replace hardcoded
user-facing and diagnostic strings in the AADSyncRuleCount class (the
Write-Verbose calls and TryWriteEventLog invocations that produce the
information/warning messages) with localized string keys and lookup calls; e.g.,
introduce/consume a localization helper (Get-LocalizedString or $this.Localize)
and pass the RuleCount, scope, and CurrentRuleCount as format arguments to that
helper instead of embedding values in literal strings, and update the other
occurrences noted (the messages at the other Write-Verbose/TryWriteEventLog
sites referenced in the comment) to use the same localization keys and helper so
all Write-Debug/Write-Verbose/Write-Error/Write-Warning and event log messages
are sourced from localized resources.
| $this.TryWriteEventLog('Error', 1102, $message) | ||
| throw $message | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
head -100 source/Classes/AADSyncRuleCount.ps1 | cat -nRepository: dsccommunity/AADConnectDsc
Length of output: 4476
🏁 Script executed:
wc -l source/Classes/AADSyncRuleCount.ps1Repository: dsccommunity/AADConnectDsc
Length of output: 110
🏁 Script executed:
sed -n '70,90p' source/Classes/AADSyncRuleCount.ps1 | cat -nRepository: dsccommunity/AADConnectDsc
Length of output: 1107
🏁 Script executed:
grep -n "throw " source/Classes/AADSyncRuleCount.ps1Repository: dsccommunity/AADConnectDsc
Length of output: 96
🏁 Script executed:
# Check if New-InvalidOperationException is available/used in this codebase
find source -name "*.ps1" -type f -exec grep -l "New-InvalidOperationException" {} \;Repository: dsccommunity/AADConnectDsc
Length of output: 52
🏁 Script executed:
# Check what DscResource.Common functions are imported or referenced
grep -r "New-.*Exception" source --include="*.ps1" | head -20Repository: dsccommunity/AADConnectDsc
Length of output: 52
🏁 Script executed:
# Check the module manifest for DscResource.Common dependencies
cat source/*.psd1 | grep -A 20 "RequiredModules"Repository: dsccommunity/AADConnectDsc
Length of output: 947
🏁 Script executed:
# Check if there are any imports at the top of the file
head -20 source/Classes/AADSyncRuleCount.ps1Repository: dsccommunity/AADConnectDsc
Length of output: 848
🏁 Script executed:
# Check if this module has any base classes or parent references
grep -n "inherit" source/Classes/AADSyncRuleCount.ps1Repository: dsccommunity/AADConnectDsc
Length of output: 52
🏁 Script executed:
# Check the psm1 file to see what's imported
head -50 source/AADConnectDsc.psm1 | grep -i "function\|require\|import\|module"Repository: dsccommunity/AADConnectDsc
Length of output: 152
🏁 Script executed:
# Check if there are other class-based resources in the source/Classes directory
find source/Classes -name "*.ps1" -type fRepository: dsccommunity/AADConnectDsc
Length of output: 378
🏁 Script executed:
# Check if the module uses DscResource.Base or DscResource.Common in any way
grep -r "DscResource\|ResourceBase" source --include="*.ps1"Repository: dsccommunity/AADConnectDsc
Length of output: 1172
🏁 Script executed:
# Check if this is actually an issue by looking at similar projects
# First, let's see what other classes exist
ls -la source/Classes/Repository: dsccommunity/AADConnectDsc
Length of output: 772
🏁 Script executed:
# Check the Prefix.ps1 file to see DscResource.Common loading
cat source/Prefix.ps1 | head -30Repository: dsccommunity/AADConnectDsc
Length of output: 234
🏁 Script executed:
# Check if the DscResource.Common module exists in the Modules directory
ls -la source/Modules/Repository: dsccommunity/AADConnectDsc
Length of output: 133
🏁 Script executed:
# Check what exception helpers are available in DscResource.Common
find source/Modules/DscResource.Common -name "*.ps1" -type f | grep -i exceptionRepository: dsccommunity/AADConnectDsc
Length of output: 139
🏁 Script executed:
# Let's verify the coding guideline applies to this class specifically
# Check if the class has [DscResource(...)] decoration
grep -A 2 "^class AADSyncRuleCount" source/Classes/AADSyncRuleCount.ps1Repository: dsccommunity/AADConnectDsc
Length of output: 102
🏁 Script executed:
# Check the context around line 81 to understand the error scenario
sed -n '69,82p' source/Classes/AADSyncRuleCount.ps1 | cat -nRepository: dsccommunity/AADConnectDsc
Length of output: 879
🏁 Script executed:
# Search for how other DSC resources in this repo handle errors
grep -r "New-InvalidOperationException\|New-InvalidDataException\|throw" source/Classes/*.ps1 | head -20Repository: dsccommunity/AADConnectDsc
Length of output: 129
Replace raw throw with New-InvalidOperationException.
Line 81 uses throw $message in a class resource with [DscResource()] decoration. Per the coding guidelines, class resources must use New-InvalidOperationException (or another appropriate New-*Exception helper) instead of raw throw for terminating errors.
Since this resource is report-only and cannot remediate drift, New-InvalidOperationException is the appropriate exception type for this scenario.
🧰 Tools
🪛 PSScriptAnalyzer (1.25.0)
[warning] Missing BOM encoding for non-ASCII encoded file 'AADSyncRuleCount.ps1'
(PSUseBOMForUnicodeEncodedFile)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@source/Classes/AADSyncRuleCount.ps1` around lines 80 - 82, Replace the raw
"throw $message" with a typed exception created by New-InvalidOperationException
to comply with class DSC resource rules: after calling
$this.TryWriteEventLog('Error', 1102, $message) change the termination to "throw
(New-InvalidOperationException -Message $message)" (or equivalent New-*Exception
helper) so the resource (AADSyncRuleCount class/method using
$this.TryWriteEventLog and throw $message) raises a proper
InvalidOperationException rather than a raw throw.
Pull Request (PR) description
Adds a new class-based DSC resource,
AADSyncRuleCount, that verifies the number of Azure AD Connect synchronization rules against an expected value and reports drift.Key behavior:
ConnectorName, or across all connectors whenConnectorNameis empty or*.Set()deliberately throws a descriptive error on drift. The resource never attempts to remediate a count difference, because automatically adding or deleting sync rules to match a number is not meaningful — drift must be investigated and resolved by an operator.Set()causes the LCM to mark the configuration as failed, surfacing the drift in standard DSC reporting.Changes in this PR:
[Unreleased].This Pull Request (PR) fixes the following issues
None.
Task list
This change is