Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions PSModuleDevelopment/PSModuleDevelopment.psd1
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

# Version number of this module.

ModuleVersion = '2.2.13.176'
ModuleVersion = '2.2.13.216'

# ID used to uniquely identify this module
GUID = '37dd5fce-e7b5-4d57-ac37-832055ce49d6'
Expand All @@ -28,8 +28,8 @@
# Modules that must be imported into the global environment prior to importing
# this module
RequiredModules = @(
@{ ModuleName = 'PSFramework'; ModuleVersion = '1.12.346' }
@{ ModuleName = 'string'; ModuleVersion = '1.1.5' }
@{ ModuleName = 'PSFramework'; ModuleVersion = '1.13.426' }
@{ ModuleName = 'string'; ModuleVersion = '1.2.13' }
)

# Assemblies that must be loaded prior to importing this module
Expand Down
43 changes: 43 additions & 0 deletions PSModuleDevelopment/changelog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,48 @@
# Changelog

## 2.2.13.216 (2026-01-16)

+ Upd: Template AzureFunction - Update host.json extension bundle version to `[4.*, 5.0.0)`
+ Upd: Template AzureFunction - PSModulePath ensured to exist correctly
+ Upd: Template AzureFunction - Configuration for Flex Consumption added, changing build behavior
+ Upd: Template AzureFunction - Added Eventgrid Trigger folder to project module
+ Upd: Template AzureFunction - Added readme with setup instructions
+ Upd: Template AzureFunction - Added `-Restart` parameter to the build script, restarting the Function App after deployment
+ Upd: Template AzureFunction - Added psf-build.ps1 for faster build through PSFramework.Nuget
+ Upd: Template AzureFunction - Added option to deploy to Flex Consumption plans, disabling Managed Dependencies
+ Upd: Template DscModule - Reordered import sequence to prioritize resources over other components
+ Upd: Template DscModule - Added support to exclude files from the syntax verification, to address issues with PowerShell Classes
+ Upd: Template MiniModule - Now asks for the Github Account name during invocation (GithubAccount)
+ Upd: Template MiniModule - Improved Readme.md
+ Upd: Template MiniModule - Added Project Uri, License Uri & Readme Uri to the manifest
+ Upd: Template MiniModule - Added `build/psf-prerequisites.ps1` as a faster dependencies alternative
+ Upd: Template MiniModule - Updated `build/vsts-build.ps1`, removing publishing steps and integrating config file
+ Upd: Template MiniModule - Added `build/psf-build.ps1`, an alternative build option using PSFramework.NuGet
+ Upd: Template MiniModule - added ability to auto-populate FunctionsToExport
+ Upd: Template MiniModule - added example project snippets for vscode
+ Upd: Template MiniModule - added example `initialize.ps1` script
+ Upd: Template MiniModule - added example `variables.ps1` script
+ Upd: Template MiniModule - added example project settings for vscode
+ Upd: Template MiniModule - added config file for simple settings that apply to the project
+ Upd: Template MiniModule - added the module variable "$script:ModuleRoo", pointing at the folder the module is in.
+ Upd: Template MiniModule - Added `build/vsts-release.ps1`, allowing to publish a release to Github
+ Upd: Template MiniModule - Added `build/vsts-publish.ps1` as a dedicated publishing step
+ Upd: Template MiniModule - Added `build/psf-publish.ps1` as a dedicated publishing step using `PSFramework.NuGet`. Can skip dependencies check.
+ Upd: Template MiniModule - Added the modules `Microsoft.PowerShell.PlatyPS` and `PSModuleDevelopment` to `build/vsts-prerequisites.ps1` for new build & publish features
+ Upd: Template MiniModule - Added explicit `Export-ModuleMember` call as part of the build step
+ Upd: Template MiniModule - Removed the type test part of the help test, as not relevant in a project unlikely to have Cmdlet exports
+ Upd: Template MiniModule - Updated PR Validation flow to run on PowerShell 7
+ Upd: Template MiniModule - Updated PR Validation flow to use `psf-prerequisites.ps1` by default, improving performance substantially
+ Upd: Template MiniModule - Updated PR Validation flow to use `actions/checkout@v6`
+ Upd: Template MiniModule - Updated Build & Publish flow to use `psf-prerequisites.ps1` by default, improving performance substantially
+ Upd: Template MiniModule - Updated Build & Publish flow to use `actions/checkout@v6`
+ Upd: Template MiniModule - Updated Build & Publish flow, splitting up building the module & publishing it to a repository
+ Upd: Template MiniModule - Updated Build & Publish flow, adding Github Release creation as the final step
+ Upd: Template MiniModule - Added changelog.md
+ Fix: Template AzureFunction - the folder "modules" should be "Modules"
+ Fix: Template DscClassFile - Removed a trailing whitespace that would trigger module tests checking for that.
+ Fix: Template MiniModule - help tests fail on parameters that are part of multiple parametersets but only mandatory in some of them

## 2.2.13.176 (2025-05-04)

+ New: Template AzureFunctionEventGrid - dedicated endpoint template for an EventGrid function
Expand Down
7 changes: 7 additions & 0 deletions templates/AzureFunction/build/build.config.psd1
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
@{
General = @{
# Is this Function App deployed to a Flex COnsumption plan?
# If so, Managed Dependencies cannot be used and must be* disabled!
# *The build script will handle that for you, if setting this to $true
FlexConsumption = $false
}

TimerTrigger = @{
# Default Schedule for timed executions
Schedule = '0 5 * * * *'
Expand Down
80 changes: 73 additions & 7 deletions templates/AzureFunction/build/build.ps1
Original file line number Diff line number Diff line change
@@ -1,13 +1,24 @@
param (
[CmdletBinding()]
param (
[string]
$Repository = 'PSGallery',

[string]
$AppRg,

[string]
$AppName
$AppName,

[switch]
$Restart
)

$ErrorActionPreference = 'Stop'
trap {
Write-Warning "Script failed: $_"
throw $_
}

$workingDirectory = Split-Path $PSScriptRoot
$config = Import-PowerShellDataFile -Path "$PSScriptRoot\build.config.psd1"

Expand All @@ -16,17 +27,63 @@ Remove-Item -Path "$workingDirectory/publish" -Recurse -Force -ErrorAction Ignor
$buildFolder = New-Item -Path $workingDirectory -Name 'publish' -ItemType Directory -Force -ErrorAction Stop
Copy-Item -Path "$workingDirectory/function/*" -Destination $buildFolder.FullName -Recurse -Force

#region Handle Modules
# Process Dependencies
$requiredModules = (Import-PowerShellDataFile -Path "$workingDirectory/þnameþ/þnameþ.psd1").RequiredModules
foreach ($module in $requiredModules) {
Save-Module -Name $module -Path "$($buildFolder.FullName)/modules" -Force -Repository $Repository
if ($module -is [string]) {
Save-Module -Name $module -Path "$($buildFolder.FullName)/modules" -Force -Repository $Repository
continue
}

$saveParam = @{ Name = $module.ModuleName }
if ($module.RequiredVersion) { $saveParam.RequiredVersion = $module.RequiredVersion }
elseif ($module.ModuleVersion) { $saveParam.MinimumVersion = $module.ModuleVersion }

Save-Module @saveParam -Path "$($buildFolder.FullName)/modules" -Force -Repository $Repository
}

#region Handle the Requirements for Flex Consumption Plans
if ($config.General.FlexConsumption) {
# Resolve New Dependencies
$mdepsFile = Join-Path -Path $buildFolder.FullName -ChildPath 'requirements.psd1'
$mDeps = Import-PowerShellDataFile -Path $mdepsFile
$requiredModules = foreach ($name in $mDeps.Keys) {
if ($mDeps.$name -match '\*') {
@{
Name = $name
MinimumVersion = '{0}.0.0' -f $mDeps.$name.Split('.')[0]
MaximumVersion = '{0}.999.999' -f $mDeps.$name.Split('.')[0]
}
}
else {
@{
Name = $name
RequiredVersion = $mDeps.$name
}
}
}

# Inject New Dependencies
foreach ($module in $requiredModules) {
Save-Module @module -Path "$($buildFolder.FullName)/modules" -Force -Repository $Repository
}

# Disable Managed Dependencies
$hostFile = Join-Path -Path $buildFolder.FullName -ChildPath 'host.json'
$hostCfg = Get-Content -Path $hostFile | ConvertFrom-Json
$hostCfg.managedDependency.enabled = $false
$hostCfg | ConvertTo-Json -Depth 99 | Set-Content -Path $hostFile
}
#endregion Handle the Requirements for Flex Consumption Plans

# Process Function Module
Copy-Item -Path "$workingDirectory/þnameþ" -Destination "$($buildFolder.FullName)/modules" -Force -Recurse
$commands = Get-ChildItem -Path "$($buildFolder.FullName)/modules/þnameþ/Functions" -Recurse -Filter *.ps1 | ForEach-Object BaseName
Update-ModuleManifest -Path "$($buildFolder.FullName)/modules/þnameþ/þnameþ.psd1" -FunctionsToExport $commands
#endregion Handle Modules

#region Triggers
# Generate Http Trigger
$httpCode = Get-Content -Path "$PSScriptRoot\functionHttp\run.ps1" | Join-String -Separator "`n"
$httpConfig = Get-Content -Path "$PSScriptRoot\functionHttp\function.json" | Join-String -Separator "`n"
Expand Down Expand Up @@ -74,13 +131,22 @@ foreach ($command in Get-ChildItem -Path "$workingDirectory\þnameþ\functions\t
$timerCode -replace '%COMMAND%',$command.BaseName | Set-Content -Path "$($endpointFolder.FullName)\run.ps1"
$timerConfig -replace '%SCHEDULE%', $schedule | Set-Content -Path "$($endpointFolder.FullName)\function.json"
}
#endregion Triggers

# Package & Cleanup
Remove-Item -Path "$workingDirectory/Function.zip" -Recurse -Force -ErrorAction Ignore
Compress-Archive -Path "$($buildFolder.FullName)/*" -DestinationPath "$workingDirectory/Function.zip"
Remove-Item -Path $buildFolder.FullName -Recurse -Force -ErrorAction Ignore

if ($AppRg -and $AppName) {
Write-Host "Publishing Function App to $AppRg/$AppName"
Publish-AzWebApp -ResourceGroupName $AppRG -Name $AppName -ArchivePath "$workingDirectory/Function.zip" -Confirm:$false -Force
}
if (-not $AppRg -or -not $AppName) { return }

Write-Host "Publishing Function App to $AppRg/$AppName"
$null = Publish-AzWebApp -ResourceGroupName $AppRG -Name $AppName -ArchivePath "$workingDirectory/Function.zip" -Confirm:$false -Force
Write-Host "Publishing Function App to $AppRg/$AppName - Done"

if (-not $Restart) { return }

Write-Host "Restarting Function App"
$null = Stop-AzWebApp -ResourceGroupName $AppRG -Name $AppName
$null = Start-AzWebApp -ResourceGroupName $AppRG -Name $AppName
Write-Host "Restarting Function App - Done"
152 changes: 152 additions & 0 deletions templates/AzureFunction/build/psf-build.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
[CmdletBinding()]
param (
[string]
$Repository = 'PSGallery',

[string]
$AppRg,

[string]
$AppName,

[switch]
$Restart
)

$ErrorActionPreference = 'Stop'
trap {
Write-Warning "Script failed: $_"
throw $_
}

Invoke-WebRequest 'https://raw.githubusercontent.com/PowershellFrameworkCollective/PSFramework.NuGet/refs/heads/master/bootstrap.ps1' -UseBasicParsing | Invoke-Expression

$workingDirectory = Split-Path $PSScriptRoot
$config = Import-PowerShellDataFile -Path "$PSScriptRoot\build.config.psd1"

# Prepare output path and copy function folder
Remove-Item -Path "$workingDirectory/publish" -Recurse -Force -ErrorAction Ignore
$buildFolder = New-Item -Path $workingDirectory -Name 'publish' -ItemType Directory -Force -ErrorAction Stop
Copy-Item -Path "$workingDirectory/function/*" -Destination $buildFolder.FullName -Recurse -Force

#region Handle Modules
# Process Dependencies
$requiredModules = (Import-PowerShellDataFile -Path "$workingDirectory/þnameþ/þnameþ.psd1").RequiredModules
foreach ($module in $requiredModules) {
if ($module -is [string]) {
Save-PSFModule -Name $module -Path "$($buildFolder.FullName)/modules" -Force -Repository $Repository
}
else {
$versionParam = @{}
if ($module.RequiredVersion) { $versionParam.Version = $module.RequiredVersion }
elseif ($module.ModuleVersion) { $versionParam.Version = "[$($module.ModuleVersion)-" }
Save-PSFModule -Name $module.ModuleName @versionParam -Path "$($buildFolder.FullName)/modules" -Force -Repository $Repository
}
}

#region Handle the Requirements for Flex Consumption Plans
if ($config.General.FlexConsumption) {
# Resolve New Dependencies
$mdepsFile = Join-Path -Path $buildFolder.FullName -ChildPath 'requirements.psd1'
$mDeps = Import-PowerShellDataFile -Path $mdepsFile
$requiredModules = foreach ($name in $mDeps.Keys) {
if ($mDeps.$name -match '\*') {
@{
Name = $name
Version = '[{0}.0.0-{1}.0.0)' -f $mDeps.$name.Split('.')[0], (1 + $mDeps.$name.Split('.')[0])
}
}
else {
@{
Name = $name
Version = $mDeps.$name
}
}
}

# Inject New Dependencies
foreach ($module in $requiredModules) {
Save-PSFModule @module -Path "$($buildFolder.FullName)/modules" -Force -Repository $Repository
}

# Disable Managed Dependencies
$hostFile = Join-Path -Path $buildFolder.FullName -ChildPath 'host.json'
$hostCfg = Get-Content -Path $hostFile | ConvertFrom-Json
$hostCfg.managedDependency.enabled = $false
$hostCfg | ConvertTo-Json -Depth 99 | Set-Content -Path $hostFile
}
#endregion Handle the Requirements for Flex Consumption Plans

# Process Function Module
Copy-Item -Path "$workingDirectory/þnameþ" -Destination "$($buildFolder.FullName)/modules" -Force -Recurse
$commands = Get-ChildItem -Path "$($buildFolder.FullName)/modules/þnameþ/Functions" -Recurse -Filter *.ps1 | ForEach-Object BaseName
Update-PSFModuleManifest -Path "$($buildFolder.FullName)/modules/þnameþ/þnameþ.psd1" -FunctionsToExport $commands
#endregion Handle Modules

#region Triggers
# Generate Http Trigger
$httpCode = Get-Content -Path "$PSScriptRoot\functionHttp\run.ps1" | Join-String -Separator "`n"
$httpConfig = Get-Content -Path "$PSScriptRoot\functionHttp\function.json" | Join-String -Separator "`n"
foreach ($command in Get-ChildItem -Path "$workingDirectory\þnameþ\functions\httpTrigger" -Recurse -File -Filter *.ps1) {
$authLevel = $config.HttpTrigger.AuthLevel
if ($config.HttpTrigger.AuthLevelOverrides.$($command.BaseName)) {
$authLevel = $config.HttpTrigger.AuthLevelOverrides.$($command.BaseName)
}
$methods = $config.HttpTrigger.Methods
if ($config.HttpTrigger.MethodOverrides.$($command.BaseName)) {
$methods = $config.HttpTrigger.MethodOverrides.$($command.BaseName)
}
$endpointFolder = New-Item -Path $buildFolder.FullName -Name $command.BaseName -ItemType Directory
$httpCode -replace '%COMMAND%', $command.BaseName | Set-Content -Path "$($endpointFolder.FullName)\run.ps1"
$httpConfig -replace '%AUTHLEVEL%', $authLevel -replace '%METHODS%', ($methods -join '", "') | Set-Content -Path "$($endpointFolder.FullName)\function.json"
}


# Generate Event Grid Trigger
$eventGridCode = Get-Content -Path "$PSScriptRoot\functionEventGrid\run.ps1" | Join-String -Separator "`n"
$eventGridConfig = Get-Content -Path "$PSScriptRoot\functionEventGrid\function.json" | Join-String -Separator "`n"
foreach ($command in Get-ChildItem -Path "$workingDirectory\þnameþ\functions\eventGridTrigger" -Recurse -File -Filter *.ps1) {
$authLevel = $config.EventGridTrigger.AuthLevel
if ($config.EventGridTrigger.AuthLevelOverrides.$($command.BaseName)) {
$authLevel = $config.EventGridTrigger.AuthLevelOverrides.$($command.BaseName)
}
$methods = $config.EventGridTrigger.Methods
if ($config.EventGridTrigger.MethodOverrides.$($command.BaseName)) {
$methods = $config.EventGridTrFigger.MethodOverrides.$($command.BaseName)
}
$endpointFolder = New-Item -Path $buildFolder.FullName -Name $command.BaseName -ItemType Directory
$eventGridCode -replace '%COMMAND%', $command.BaseName | Set-Content -Path "$($endpointFolder.FullName)\run.ps1"
$eventGridConfig -replace '%AUTHLEVEL%', $authLevel -replace '%METHODS%', ($methods -join '", "') | Set-Content -Path "$($endpointFolder.FullName)\function.json"
}

# Generate Timer Trigger
$timerCode = Get-Content -Path "$PSScriptRoot\functionTimer\run.ps1" | Join-String -Separator "`n"
$timerConfig = Get-Content -Path "$PSScriptRoot\functionTimer\function.json" | Join-String -Separator "`n"
foreach ($command in Get-ChildItem -Path "$workingDirectory\þnameþ\functions\timerTrigger" -Recurse -File -Filter *.ps1) {
$schedule = $config.TimerTrigger.Schedule
if ($config.TimerTrigger.ScheduleOverrides.$($command.BaseName)) {
$schedule = $config.TimerTrigger.ScheduleOverrides.$($command.BaseName)
}
$endpointFolder = New-Item -Path $buildFolder.FullName -Name $command.BaseName -ItemType Directory
$timerCode -replace '%COMMAND%', $command.BaseName | Set-Content -Path "$($endpointFolder.FullName)\run.ps1"
$timerConfig -replace '%SCHEDULE%', $schedule | Set-Content -Path "$($endpointFolder.FullName)\function.json"
}
#endregion Triggers

# Package & Cleanup
Remove-Item -Path "$workingDirectory/Function.zip" -Recurse -Force -ErrorAction Ignore
Compress-Archive -Path "$($buildFolder.FullName)/*" -DestinationPath "$workingDirectory/Function.zip"
Remove-Item -Path $buildFolder.FullName -Recurse -Force -ErrorAction Ignore

if (-not $AppRg -or -not $AppName) { return }

Write-Host "Publishing Function App to $AppRg/$AppName"
$null = Publish-AzWebApp -ResourceGroupName $AppRG -Name $AppName -ArchivePath "$workingDirectory/Function.zip" -Confirm:$false -Force
Write-Host "Publishing Function App to $AppRg/$AppName - Done"

if (-not $Restart) { return }

Write-Host "Restarting Function App"
$null = Stop-AzWebApp -ResourceGroupName $AppRG -Name $AppName
$null = Start-AzWebApp -ResourceGroupName $AppRG -Name $AppName
Write-Host "Restarting Function App - Done"
2 changes: 1 addition & 1 deletion templates/AzureFunction/function/host.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,6 @@
},
"extensionBundle": {
"id": "Microsoft.Azure.Functions.ExtensionBundle",
"version": "[3.*, 4.0.0)"
"version": "[4.*, 5.0.0)"
}
}
11 changes: 8 additions & 3 deletions templates/AzureFunction/function/profile.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ if ($env:MSI_SECRET -and (Get-Module -ListAvailable Az.Accounts))
Connect-AzAccount -Identity
}

# Uncomment the next line to enable legacy AzureRm alias in Azure PowerShell.
# Enable-AzureRmAlias
# You can also define functions or aliases that can be referenced in any of your PowerShell functions.
$pathDelimiter = ';'
if (-not $IsWindows) { $pathDelimiter = ':' }
$modulePaths = $env:PSModulePath -split $pathDelimiter
$ourModulePath = Join-Path -Path $PSScriptRoot -ChildPath Modules

if ($modulePaths -notcontains $ourModulePath) {
$env:PSModulePath = $ourModulePath, $env:PSModulePath -join $pathDelimiter
}
Loading