-
Notifications
You must be signed in to change notification settings - Fork 8.1k
[release/v7.6] DSC v3 resource for Powershell Profile #26447
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[release/v7.6] DSC v3 resource for Powershell Profile #26447
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This pull request backports DSC v3 resource support for managing PowerShell profiles from the master branch to the release/v7.6 branch. It enables administrators to use DSC v3 to consistently manage PowerShell profile content across all profile types (CurrentUserCurrentHost, CurrentUserAllHosts, AllUsersCurrentHost, AllUsersAllHosts) on Windows, Linux, and macOS.
Key Changes:
- Adds DSC v3 resource implementation for PowerShell profile management
- Updates CI workflows to download and use DSC v3.2+ packages for testing
- Registers PSProfileDSCResource as an experimental feature
Reviewed Changes
Copilot reviewed 19 out of 19 changed files in this pull request and generated 14 comments.
Show a summary per file
| File | Description |
|---|---|
| dsc/pwsh.profile.resource.ps1 | Implements the DSC resource logic for get/set/export operations on PowerShell profiles |
| dsc/pwsh.profile.dsc.resource.json | Provides the DSC v3 resource manifest with schema definitions and operation specifications |
| src/powershell-win-core/powershell-win-core.csproj | Includes DSC resource files in Windows builds using wildcard pattern |
| src/powershell-unix/powershell-unix.csproj | Includes DSC resource files in Unix/Linux/macOS builds with explicit file listing |
| src/System.Management.Automation/engine/ExperimentalFeature/ExperimentalFeature.cs | Registers PSProfileDSCResource as an experimental feature |
| experimental-feature-windows.json | Enables the PSProfileDSCResource experimental feature on Windows |
| experimental-feature-linux.json | Enables the PSProfileDSCResource experimental feature on Linux |
| tools/packaging/boms/windows.json | Adds DSC resource files to Windows packaging BOM |
| tools/packaging/boms/linux.json | Adds DSC resource files to Linux packaging BOM |
| tools/packaging/boms/mac.json | Adds DSC resource files to macOS packaging BOM |
| test/powershell/dsc/dsc.profileresource.Tests.ps1 | Comprehensive Pester test suite for the DSC profile resource covering all profile types |
| test/powershell/dsc/psprofile_*.dsc.yaml | Test configuration files demonstrating various profile management scenarios |
| .github/actions/test/windows/action.yml | Downloads DSC v3.2+ package for Windows CI testing |
| .github/actions/test/nix/action.yml | Downloads platform-specific DSC v3.2+ packages for Linux and macOS CI testing |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
src/System.Management.Automation/engine/ExperimentalFeature/ExperimentalFeature.cs
Show resolved
Hide resolved
| run: |- | ||
| Import-Module .\.github\workflows\GHWorkflowHelper\GHWorkflowHelper.psm1 | ||
| $releases = Invoke-RestMethod -Uri "https://api.github.com/repos/PowerShell/Dsc/releases" | ||
| $latestRelease = $releases | Where-Object { $v = $_.name.trim("v"); $semVer = [System.Management.Automation.SemanticVersion]::new($v); if ($semVer.Major -eq 3 -and $semVer.Minor -ge 2) { $_ } } | Select-Object -First 1 |
Copilot
AI
Nov 13, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If no releases match the filter criteria (Major = 3, Minor >= 2), $latestRelease will be $null, causing subsequent property accesses to fail. Add validation to handle this case gracefully.
Add error handling:
$latestRelease = $releases | Where-Object { $v = $_.name.trim("v"); $semVer = [System.Management.Automation.SemanticVersion]::new($v); if ($semVer.Major -eq 3 -and $semVer.Minor -ge 2) { $_ } } | Select-Object -First 1
if (-not $latestRelease) {
throw "No DSC v3.2+ release found"
}
$latestVersion = $latestRelease.tag_name.TrimStart("v")This prevents cryptic errors when the expected DSC version isn't available.
| $latestRelease = $releases | Where-Object { $v = $_.name.trim("v"); $semVer = [System.Management.Automation.SemanticVersion]::new($v); if ($semVer.Major -eq 3 -and $semVer.Minor -ge 2) { $_ } } | Select-Object -First 1 | |
| $latestRelease = $releases | Where-Object { $v = $_.name.trim("v"); $semVer = [System.Management.Automation.SemanticVersion]::new($v); if ($semVer.Major -eq 3 -and $semVer.Minor -ge 2) { $_ } } | Select-Object -First 1 | |
| if (-not $latestRelease) { | |
| throw "No DSC v3.2+ release found" | |
| } |
| # Default constructor logic - read from file system | ||
| $fileExists = Test-Path $resource.profilePath | ||
| if ($fileExists) { | ||
| $resource.content = Get-Content -Path $resource.profilePath |
Copilot
AI
Nov 13, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The Get-Content call returns an array of strings (one per line) by default, but this should return the entire content as a single string to match the expected behavior based on the test expectations and the Set-Content usage in line 147.
Add the -Raw parameter to return the content as a single string:
$resource.content = Get-Content -Path $resource.profilePath -RawThis ensures consistency with how profile content is expected to be handled throughout the resource (as a single string, not an array).
| $resource.content = Get-Content -Path $resource.profilePath | |
| $resource.content = Get-Content -Path $resource.profilePath -Raw |
| <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> | ||
| <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory> | ||
| </Content> | ||
| <Content Include="..\..\dsc\pwsh.profile.dsc.resource.json;..\..\dsc\pwsh.profile.resource.ps1"> |
Copilot
AI
Nov 13, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The Unix project file explicitly lists the two DSC files individually, while the Windows project file uses a wildcard pattern ..\..\dsc\* (line 32). This creates an inconsistency where future DSC files added to the /dsc folder would be automatically included in Windows builds but would need manual addition to the Unix project file.
For consistency and maintainability, use the same wildcard pattern as Windows:
<Content Include="..\..\dsc\*">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>This ensures both platforms handle DSC resource files consistently and reduces maintenance burden when adding new DSC resources.
| <Content Include="..\..\dsc\pwsh.profile.dsc.resource.json;..\..\dsc\pwsh.profile.resource.ps1"> | |
| <Content Include="..\..\dsc\*"> |
| New-Item -Path $testProfilePathAllUsersAllHosts -Value $testProfileContent -Force -ItemType File | ||
|
|
||
| $originalPath = $env:PATH | ||
| $env:PATH += "$pathSeparator$PSHome" |
Copilot
AI
Nov 13, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Line 156 duplicates the PATH modification already performed in line 131. This creates redundant PATH entries.
Remove the duplicate line 156:
$testProfilePathAllUsersAllHosts = $PROFILE.AllUsersAllHosts
Copy-Item -Path $testProfilePathAllUsersAllHosts -Destination "$TestDrive/allusers-allhosts-profile.bak" -Force -ErrorAction SilentlyContinue
New-Item -Path $testProfilePathAllUsersAllHosts -Value $testProfileContent -Force -ItemType File
$originalPath = $env:PATH
# Line 156 removed - $PSHome already added to PATH in line 131| $env:PATH += "$pathSeparator$PSHome" |
| run: |- | ||
| Import-Module ./.github/workflows/GHWorkflowHelper/GHWorkflowHelper.psm1 | ||
| $releases = Invoke-RestMethod -Uri "https://api.github.com/repos/PowerShell/Dsc/releases" | ||
| $latestRelease = $releases | Where-Object { $v = $_.name.trim("v"); $semVer = [System.Management.Automation.SemanticVersion]::new($v); if ($semVer.Major -eq 3 -and $semVer.Minor -ge 2) { $_ } } | Select-Object -First 1 |
Copilot
AI
Nov 13, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If no releases match the filter criteria (Major = 3, Minor >= 2), $latestRelease will be $null, causing subsequent property accesses to fail. Add validation to handle this case gracefully.
Add error handling:
$latestRelease = $releases | Where-Object { $v = $_.name.trim("v"); $semVer = [System.Management.Automation.SemanticVersion]::new($v); if ($semVer.Major -eq 3 -and $semVer.Minor -ge 2) { $_ } } | Select-Object -First 1
if (-not $latestRelease) {
throw "No DSC v3.2+ release found"
}
$latestVersion = $latestRelease.tag_name.TrimStart("v")This prevents cryptic errors when the expected DSC version isn't available.
| $latestRelease = $releases | Where-Object { $v = $_.name.trim("v"); $semVer = [System.Management.Automation.SemanticVersion]::new($v); if ($semVer.Major -eq 3 -and $semVer.Minor -ge 2) { $_ } } | Select-Object -First 1 | |
| $latestRelease = $releases | Where-Object { $v = $_.name.trim("v"); $semVer = [System.Management.Automation.SemanticVersion]::new($v); if ($semVer.Major -eq 3 -and $semVer.Minor -ge 2) { $_ } } | Select-Object -First 1 | |
| if (-not $latestRelease) { | |
| throw "No DSC v3.2+ release found" | |
| } |
| Remove-Item $testProfilePathAllUsersAllHosts -Force -ErrorAction SilentlyContinue | ||
| } | ||
|
|
||
| Remove-Item -Path "$TestDrive/currentuser-allhosts-profile.bak" -Force -ErrorAction SilentlyContinue |
Copilot
AI
Nov 13, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This line attempts to remove a file that doesn't exist. Based on the BeforeAll block (line 147), the backup file is named allusers-currenthost-profile.bak, not currentuser-allhosts-profile.bak.
Update to match the correct backup filename:
Remove-Item -Path "$TestDrive/allusers-currenthost-profile.bak" -Force -ErrorAction SilentlyContinue
Remove-Item -Path "$TestDrive/allusers-allhosts-profile.bak" -Force -ErrorAction SilentlyContinue| Remove-Item -Path "$TestDrive/currentuser-allhosts-profile.bak" -Force -ErrorAction SilentlyContinue | |
| Remove-Item -Path "$TestDrive/allusers-currenthost-profile.bak" -Force -ErrorAction SilentlyContinue |
| $latestVersion = $latestRelease.tag_name.TrimStart("v") | ||
| Write-Host "Latest DSC Version: $latestVersion" | ||
| $downloadUrl = $latestRelease.assets | Where-Object { $_.name -like "DSC-*-x86_64-pc-windows-msvc.zip" } | Select-Object -First 1 | Select-Object -ExpandProperty browser_download_url |
Copilot
AI
Nov 13, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If no asset matches the filter pattern, $downloadUrl will be $null, causing Invoke-RestMethod to fail with an unclear error. Add validation to handle this case.
Add error handling:
$downloadUrl = $latestRelease.assets | Where-Object { $_.name -like "DSC-*-x86_64-pc-windows-msvc.zip" } | Select-Object -First 1 | Select-Object -ExpandProperty browser_download_url
if (-not $downloadUrl) {
throw "No matching DSC package found for Windows x86_64"
}
Write-Host "Download URL: $downloadUrl"This provides a clear error message if the expected package isn't found in the release.
| $downloadUrl = $latestRelease.assets | Where-Object { $_.name -like "DSC-*-x86_64-pc-windows-msvc.zip" } | Select-Object -First 1 | Select-Object -ExpandProperty browser_download_url | |
| $downloadUrl = $latestRelease.assets | Where-Object { $_.name -like "DSC-*-x86_64-pc-windows-msvc.zip" } | Select-Object -First 1 | Select-Object -ExpandProperty browser_download_url | |
| if (-not $downloadUrl) { | |
| throw "No matching DSC package found for Windows x86_64" | |
| } |
Backport of #26157 to release/v7.6
Triggered by @TravisEz13 on behalf of @adityapatwardhan
Original CL Label: CL-General
/cc @PowerShell/powershell-maintainers
Impact
REQUIRED: Choose either Tooling Impact or Customer Impact (or both). At least one checkbox must be selected.
Tooling Impact
Updates the Linux and Windows GitHub workflows to download the latest DSC package so CI and packaging continue to use supported binaries.
Customer Impact
Enables administrators to manage all PowerShell profile types via DSC v3 so profile content stays consistent across machines; without it, profiles must be maintained manually.
Regression
REQUIRED: Check exactly one box.
This is not a regression.
Testing
Validated the PowerShell Profile DSC resource using the new Pester suite added in the original change, confirmed the DSC package download logic via CI runs on master, and manually exercised DSC export/get scenarios to ensure consistent behavior.
Risk
REQUIRED: Check exactly one box.
Adds a new DSC resource and packaging entries but is isolated to the DSC subsystem and experimental feature registration, minimizing blast radius.