Skip to content

Instantly share code, notes, and snippets.

@trossr32
Created December 18, 2025 15:54
Show Gist options
  • Select an option

  • Save trossr32/a21536449c4941392d972e189172b5d4 to your computer and use it in GitHub Desktop.

Select an option

Save trossr32/a21536449c4941392d972e189172b5d4 to your computer and use it in GitHub Desktop.
Scans a .NET solution for NuGet package versions used across projects.
function Nuget-SolutionPackageVersions {
<#
.SYNOPSIS
Scans a .NET solution for NuGet package versions used across projects.
.DESCRIPTION
Scans a .NET solution for NuGet package versions used across projects.
.PARAMETER SolutionPath
The path to the .NET solution (.sln file). If not provided, the current directory is used.
.EXAMPLE
Nuget-SolutionPackageVersions -SolutionPath "C:\Projects\MySolution"
Scans the specified solution for NuGet package versions.
.EXAMPLE
Nuget-SolutionPackageVersions
Scans the current directory for NuGet package versions.
#>
[CmdletBinding()]
param(
[Parameter(Mandatory=$false, ValueFromPipeline=$true)]
[string]$SolutionPath
)
begin {
# if solution path not provided, use current directory
if (-not $SolutionPath) {
$SolutionPath = Get-Location
}
# Check there's a .sln file in the provided path
$slnFiles = Get-ChildItem -Path $SolutionPath -Filter *.sln
if ($slnFiles.Count -eq 0) {
throw "No .sln file found in path: $SolutionPath"
}
# check there's at least one .csproj file in the provided path or subdirectories
$csprojFiles = Get-ChildItem -Path $SolutionPath -Filter *.csproj -Recurse
if ($csprojFiles.Count -eq 0) {
throw "No .csproj file(s) found in path: $SolutionPath"
}
}
process {
# Scan all .csproj files and aggregate unique package versions
$packages = Get-ChildItem -Filter *.csproj -Recurse |
Get-Content |
Select-String -Pattern '<PackageReference Include="([^"]+)" Version="([^"]+)"' -AllMatches |
ForEach-Object { $_.Matches } |
Group-Object { $_.Groups[1].Value } |
ForEach-Object { @{
Name = $_.Name
Versions = $_.Group.ForEach({ $_.Groups[2].Value }) | Select-Object -Unique
}}
# Build table rows: Package | Versions (comma-delimited) | Status (emoji)
$rows = $packages |
Sort-Object { $_.Name } |
ForEach-Object {
$versionList = ($_.Versions -join ', ')
$statusEmoji = if ($_.Versions.Count -le 1) { "✅" } else { "❌" }
[pscustomobject]@{
Package = $_.Name
Versions = $versionList
"No consolidation required" = $statusEmoji
}
}
# Display as a formatted table
$rows | Format-Table -AutoSize
}
end {
Write-Host ""
Write-Host "Create a Directory.Packages.props file using: " -NoNewline
Write-Host "dotnet new packagesprops" -ForegroundColor Magenta
Write-Host ""
Write-Host "Automate migration to Central Package Management (CPM) using: "
Write-Host "Install if not present => " -NoNewline
Write-Host "dotnet tool install CentralisedPackageConverter --global" -ForegroundColor Magenta
Write-Host "Convert solution to CPM => " -NoNewline
Write-Host "central-pkg-converter '$SolutionPath'" -ForegroundColor Magenta
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment