-
Notifications
You must be signed in to change notification settings - Fork 0
CI/CD: Implement reusable workflows and release automation (v0.2.3) #7
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
Conversation
…ias for Sync-DBPoolContainer function
…to-DBPool_Refresh into Verbose-ConfirmOutput
Resolves MS KB5074596 in PS5.1
…omation, Dependabot, and CodeQL security scanning - Restructured CI/CD with reusable workflows for build, test, and deployment - Implemented automated GitHub release creation and PowerShell Gallery publishing - Added Dependabot configuration for automated dependency updates (github-actions ecosystem) - Added CodeQL security scanning workflow for vulnerability detection - Updated GitHub Actions to latest versions: checkout@v6, cache@v5, UpdatePWSHAction@v1.0.3 - Removed fixed PowerShell version pins to use latest stable 7.x - Enhanced documentation publishing with proper GitHub environment variables - Added build process improvements with initialization script appending - Updated module version to 0.2.3 and Datto.DBPool.API dependency to 0.2.3 minimum
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 implements comprehensive CI/CD automation infrastructure for the Datto-DBPool_Refresh PowerShell module, mirroring the v0.2.3 implementation from a related project. The changes focus on automating build, test, and release processes while updating the module version from 0.1.6.1 to 0.2.3.
Changes:
- Added reusable GitHub Actions workflows for CI/CD (ci.yml, build-and-release.yml, publish-psgallery.yml, codeql.yml) with automated testing, security scanning, and publishing
- Enhanced build process with PowerShellBuild 0.7.3, added initialization script appending, and function export management
- Updated module dependencies (Datto.DBPool.API to 0.2.3, PSScriptAnalyzer 1.24.0, psake 4.9.1) and added Dependabot for automated dependency updates
Reviewed changes
Copilot reviewed 23 out of 24 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| .github/workflows/ci.yml | New reusable CI workflow with test, analyze, and quality-gate jobs |
| .github/workflows/build-and-release.yml | Automated GitHub release creation workflow on version tags |
| .github/workflows/publish-psgallery.yml | PowerShell Gallery publishing with artifact handling |
| .github/workflows/codeql.yml | CodeQL security scanning on push, PR, and weekly schedule |
| .github/dependabot.yml | Automated dependency updates for github-actions ecosystem |
| .github/workflows/Build_Tests.yaml | Updated to UpdatePWSHAction@v1.0.3 and checkout@v6 |
| .github/workflows/Build_DocsSite.yml | Updated action versions and added GitHub environment variables |
| .github/workflows/PSScriptAnalyzer.yml | Upgraded to PSScriptAnalyzer action@v1.1 and checkout@v6 |
| psakeFile.ps1 | Added AppendInitialization task and enhanced PublishDocs with environment variables |
| build.ps1 | Added PowerShellGet v2 enforcement to avoid PSDepend compatibility issues |
| requirements.psd1 | Updated build dependencies (PowerShellBuild 0.7.3, PSScriptAnalyzer 1.24.0, psake 4.9.1) |
| src/Datto.DBPool.Refresh.psd1 | Updated module version to 0.2.3, Datto.DBPool.API dependency to 0.2.3, added NestedModules list |
| src/Datto.DBPool.Refresh.psm1 | Removed module file (now uses compiled monolithic PSM1) |
| src/Public/Sync-DBPoolContainer.ps1 | Added aliases and verbose output with container name during confirmation |
| src/Public/Copy-DBPoolParentContainer.ps1 | Added AllowBeta parameter, improved error messaging, fixed runspace disposal |
| src/Private/scheduledTask/Register-RefreshDBPoolTask.ps1 | Added Documentation property, changed non-Windows message to Write-Error |
| src/Initialize-RefreshDBPool.ps1 | Updated PowerShell installer to 7.5.4, improved execution policy check, added script download handling |
| README.md | Updated installation script command to use shortened URL |
| CHANGELOG.md | Added v0.2.3 release notes |
| docs/* | Updated documentation for Copy-DBPoolParentContainer with AllowBeta parameter |
| Datto.DBPool.Refresh/0.2.1/* | Built module artifacts (version 0.2.1) |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
README.md
Outdated
| @@ -11,13 +11,15 @@ The recommendation is ~30 - 60 minutes prior to the start of your shift. | |||
|
|
|||
| Use the following script to easily install and handle **all** dependancies. | |||
Copilot
AI
Jan 30, 2026
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.
Typo in 'dependancies' should be 'dependencies'.
| Use the following script to easily install and handle **all** dependancies. | |
| Use the following script to easily install and handle **all** dependencies. |
|
|
||
| - name: Trigger docs publish | ||
| run: | | ||
| gh workflow run publish-docs.yml |
Copilot
AI
Jan 30, 2026
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 workflow references 'publish-docs.yml' which doesn't exist in the repository. The existing documentation workflow is named 'Build_DocsSite.yml'. Either create a 'publish-docs.yml' workflow or update this reference to match the existing workflow name.
| gh workflow run publish-docs.yml | |
| gh workflow run Build_DocsSite.yml |
|
|
||
| #Region | ||
|
|
||
| function Set-DBPoolSecurityProtocol { | ||
| <# | ||
| .SYNOPSIS | ||
| The Set-DBPoolSecurityProtocol function is used to set the Security Protocol in the current context. | ||
|
|
||
| .DESCRIPTION | ||
| Sets the Security Protocol for a .NET application to use TLS 1.2 by default. | ||
| This function is useful for ensuring secure communication in .NET applications. | ||
|
|
||
| .PARAMETER Protocol | ||
| The security protocol to use. Can be set to 'Ssl3', 'SystemDefault', 'Tls', 'Tls11', 'Tls12', and 'Tls13'. | ||
|
|
||
| .EXAMPLE | ||
| Set-DBPoolSecurityProtocol -Protocol Tls12 | ||
|
|
||
| Sets the Security Protocol to use TLS 1.2 | ||
|
|
||
| .INPUTS | ||
| [string] - The security protocol to use. | ||
|
|
||
| .OUTPUTS | ||
| N/A | ||
|
|
||
| .NOTES | ||
| Make sure to run this function in the appropriate context, as it affects .NET-wide security settings. | ||
|
|
||
| .LINK | ||
| https://datto-dbpool-refresh.kentsapp.com/Internal/Set-DBPoolSecurityProtocol/ | ||
| #> | ||
| [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Low')] | ||
| param ( | ||
| [Parameter(Position = 0, Mandatory = $False, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)] | ||
| [ValidateSet('Ssl3', 'SystemDefault', 'Tls', 'Tls11', 'Tls12', 'Tls13')] | ||
| [string]$Protocol = 'Tls12' | ||
| ) | ||
|
|
||
| Process{ | ||
|
|
||
| if ($PSCmdlet.ShouldProcess($Protocol, "Set Security Protocol")) { | ||
| try { | ||
| [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::$Protocol | ||
| Write-Verbose "Security Protocol set to: $Protocol" | ||
| } catch { | ||
| Write-Error "Failed to set Security Protocol. $_" | ||
| } | ||
| } | ||
|
|
||
| } | ||
| } | ||
|
|
||
| #EndRegion | ||
| #Region | ||
|
|
||
| function Get-RefreshDBPoolApiKey { | ||
| <# | ||
| .SYNOPSIS | ||
| This function gets the DBPool API key from the default PowerShell SecretManagement vault and sets the global variable. | ||
|
|
||
| .DESCRIPTION | ||
| This function gets the DBPool API key from the default PowerShell SecretManagement vault and sets the global variable. | ||
| If the global variable is already set, confirm with the user before overwriting the value or set the value without confirmation using the -Force switch. | ||
|
|
||
| .PARAMETER SecretName | ||
| The name to use for the secret in the SecretManagement vault. Defaults to 'DBPool_ApiKey'. | ||
|
|
||
| .PARAMETER SecretStoreName | ||
| The name of the SecretManagement vault where the secret will be stored. Defaults to the value of the environment variable 'Datto_SecretStore'. | ||
|
|
||
| .PARAMETER AsPlainText | ||
| If specified, the function will return the API key as a plaintext string. | ||
|
|
||
| .PARAMETER Force | ||
| If specified, forces the function to overwrite the existing secret if it already exists in the vault. | ||
|
|
||
| .EXAMPLE | ||
| Get-RefreshDBPoolApiKey | ||
|
|
||
| Retrieves the DBPool API key from the default SecretManagement vault with the default name 'DBPool_ApiKey' as a secure string. | ||
|
|
||
| .EXAMPLE | ||
| Get-RefreshDBPoolApiKey -AsPlainText | ||
|
|
||
| Retrieves the DBPool API key from the default SecretManagement vault with the default name 'DBPool_ApiKey' as a plaintext string. | ||
|
|
||
| .EXAMPLE | ||
| Get-RefreshDBPoolApiKey -SecretName 'Different_SecretName' -SecretStoreName 'Custom_SecretsVault' -Force | ||
|
|
||
| Retrieves the DBPool API key and adds it to the 'Custom_SecretsVault' SecretManagement vault with the name 'Different_SecretName'. | ||
| If the secret already exists, it will be overwritten. | ||
|
|
||
| .INPUTS | ||
| N/A | ||
|
|
||
| .OUTPUTS | ||
| [securestring] - The DBPool API key as a secure string. | ||
| [string] - The DBPool API key as a plaintext string. | ||
|
|
||
| .NOTES | ||
| This function is designed to work with the default SecretManagement vault. Ensure the vault is installed and configured before using this function. | ||
|
|
||
| .LINK | ||
| https://datto-dbpool-refresh.kentsapp.com/Internal/apiKey/Get-RefreshDBPoolApiKey/ | ||
| #> | ||
|
|
||
| [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')] | ||
| param ( | ||
| [Parameter(Mandatory = $false)] | ||
| [ValidateNotNullOrEmpty()] | ||
| [string]$SecretName = 'DBPool_ApiKey', | ||
|
|
||
| [Parameter(Mandatory = $false)] | ||
| [string]$SecretStoreName = 'Datto_SecretStore', | ||
|
|
||
| [switch]$AsPlainText, | ||
|
|
||
| [switch]$Force | ||
|
|
||
| ) | ||
|
|
||
| begin { | ||
|
|
||
| $secretExists = Get-SecretInfo -Vault $SecretStoreName -WarningAction SilentlyContinue -ErrorAction SilentlyContinue -Verbose:$false | Where-Object { $_.Name -eq $SecretName } | ||
|
|
||
| } | ||
|
|
||
| process { | ||
|
|
||
| if ( !(Test-SecretVault -Name $SecretStoreName -ErrorAction SilentlyContinue -Verbose:$false) -or !($secretExists) ) { | ||
| Write-Error "Ensure the default SecretManagement Vault is installed and configured. Use 'Set-RefreshDBPoolApiKey' first!" | ||
| return | ||
| } else { | ||
| try { | ||
|
|
||
| if (!$DBPool_ApiKey) { | ||
| Add-DBPoolApiKey -apiKey $( Get-Secret -Name $SecretName -Vault $SecretStoreName -ErrorAction Stop ) -Verbose:$VerbosePreference | ||
| } elseif (Get-Variable -Name 'DBPool_ApiKey' -ErrorAction SilentlyContinue) { | ||
| if ($Force -or $PSCmdlet.ShouldProcess('$DBPool_ApiKey', 'Set DBPool API Key')) { | ||
| Add-DBPoolApiKey -apiKey $( Get-Secret -Name $SecretName -Vault $SecretStoreName -ErrorAction Stop ) -Verbose:$VerbosePreference | ||
| } | ||
| } | ||
|
|
||
| } catch { | ||
| Write-Error $_ | ||
| } | ||
| } | ||
|
|
||
| } | ||
|
|
||
| end { | ||
| (Get-DBPoolApiKey -AsPlainText:$AsPlainText -WarningAction SilentlyContinue -ErrorAction SilentlyContinue).ApiKey | ||
| } | ||
|
|
||
| } | ||
|
|
||
| #EndRegion | ||
| #Region | ||
|
|
||
| function Remove-RefreshDBPoolApiKey { | ||
| <# | ||
| .SYNOPSIS | ||
| This function removes the DBPool API key to the default PowerShell SecretManagement vault. | ||
|
|
||
| .DESCRIPTION | ||
| This function removes the DBPool API key from the specified SecretManagement vault. | ||
|
|
||
| .PARAMETER SecretName | ||
| The name to use for the secret in the SecretManagement vault. Defaults to 'DBPool_ApiKey'. | ||
|
|
||
| .PARAMETER SecretStoreName | ||
| The name of the SecretManagement vault where the secret is stored. Defaults to the value of the environment variable 'Datto_SecretStore'. | ||
|
|
||
| .PARAMETER Force | ||
| If specified, forces the function to remove the secret from the vault. | ||
|
|
||
| .EXAMPLE | ||
| Remove-RefreshDBPoolApiKey | ||
|
|
||
| Removes the API key from the SecretManagement vault. | ||
|
|
||
| .EXAMPLE | ||
| Remove-RefreshDBPoolApiKey -SecretName 'Different_SecretName' -SecretStoreName 'Custom_SecretsVault' -Force | ||
|
|
||
| Removes the API key from the 'Custom_SecretsVault' SecretManagement vault with the name 'Different_SecretName'. | ||
|
|
||
| .INPUTS | ||
| N/A | ||
|
|
||
| .OUTPUTS | ||
| N/A | ||
|
|
||
| .NOTES | ||
| N/A | ||
|
|
||
| .LINK | ||
| https://datto-dbpool-refresh.kentsapp.com/Internal/apiKey/Remove-RefreshDBPoolApiKey/ | ||
| #> | ||
| [CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact = 'Medium')] | ||
| param ( | ||
| [Parameter(Mandatory = $false)] | ||
| [ValidateNotNullOrEmpty()] | ||
| [string]$SecretName = 'DBPool_ApiKey', | ||
|
|
||
| [Parameter(Mandatory = $false)] | ||
| [string]$SecretStoreName = 'Datto_SecretStore', | ||
|
|
||
| [switch]$Force | ||
| ) | ||
|
|
||
| begin { | ||
|
|
||
| if ( !(Test-SecretVault -Name $SecretStoreName -ErrorAction Stop) ) { | ||
| Write-Error "Ensure the default SecretManagement Vault is installed and configured. Use 'Register-SecretVault' -Name $SecretStoreName -DefaultVault' first!" -ErrorAction Stop | ||
| } | ||
|
|
||
| } | ||
|
|
||
| process { | ||
|
|
||
| $secretExists = Get-Secret -Name $SecretName -Vault $SecretStoreName -ErrorAction SilentlyContinue | ||
| if ($secretExists) { | ||
| if ($Force -or $PSCmdlet.ShouldProcess("Secret name [ $SecretName ] from vault [ $SecretStoreName ]")) { | ||
| Remove-Secret -Name $SecretName -Vault $SecretStoreName | ||
| } | ||
| } else { | ||
| Write-Warning "The secret '$SecretName' does not exist in the vault '$SecretStoreName'." | ||
| } | ||
|
|
||
| } | ||
|
|
||
| end {} | ||
|
|
||
| } | ||
|
|
||
| #EndRegion | ||
| #Region | ||
|
|
||
| function Set-RefreshDBPoolApiKey { | ||
| <# | ||
| .SYNOPSIS | ||
| This function adds the DBPool API key to the default PowerShell SecretManagement vault. | ||
|
|
||
| .DESCRIPTION | ||
| This function securely stores the DBPool API key in the specified SecretManagement vault. | ||
| It can be used to add or update the API key for later use in scripts and automation tasks. | ||
| If the secret already exists, the function can overwrite it if the -Force switch is used. | ||
|
|
||
| .PARAMETER SecretName | ||
| The name to use for the secret in the SecretManagement vault. Defaults to 'DBPool_ApiKey'. | ||
|
|
||
| .PARAMETER DBPool_ApiKey | ||
| The secure string containing the DBPool API key. This parameter is mandatory. | ||
| DBPool API key can be retrieved from the web interface at "$DBPool_Base_URI/web/self". | ||
|
|
||
| .PARAMETER SecretStoreName | ||
| The name of the SecretManagement vault where the secret will be stored. | ||
| Default value is 'Datto_SecretStore'. | ||
|
|
||
| .PARAMETER Force | ||
| If specified, forces the function to overwrite the existing secret if it already exists in the vault. | ||
|
|
||
| .EXAMPLE | ||
| Set-RefreshDBPoolApiKey -DBPool_ApiKey $secureApiKey -Verbose | ||
|
|
||
| Adds the DBPool API key to the default SecretManagement vault with the name 'DBPool_ApiKey'. | ||
|
|
||
| .EXAMPLE | ||
| Set-RefreshDBPoolApiKey -DBPool_ApiKey $secureApiKey -SecretName 'Custom_ApiKey' -SecretStoreName 'MySecretStore' -Force | ||
|
|
||
| Adds the DBPool API key to the 'MySecretStore' SecretManagement vault with the name 'Custom_ApiKey'. | ||
| If the secret already exists, it will be overwritten. | ||
|
|
||
| .INPUTS | ||
| [securestring] - The secure string containing the DBPool API key. | ||
|
|
||
| .OUTPUTS | ||
| N/A | ||
|
|
||
| .NOTES | ||
| Ensure that the PowerShell SecretManagement module is installed and configured before using this function. | ||
|
|
||
| .LINK | ||
| https://datto-dbpool-refresh.kentsapp.com/Internal/apiKey/Set-RefreshDBPoolApiKey/ | ||
| #> | ||
| [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')] | ||
| [Alias('Add-RefreshDBPoolApiKey')] | ||
| param ( | ||
| [Parameter(Mandatory = $false)] | ||
| [ValidateNotNullOrEmpty()] | ||
| [string]$SecretName = 'DBPool_ApiKey', | ||
|
|
||
| [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, HelpMessage = { "Get API key from '$(Get-DBPoolBaseURI)/web/self'" })] | ||
| [ValidateNotNullOrEmpty()] | ||
| [securestring]$DBPool_ApiKey, | ||
|
|
||
| [Parameter(Mandatory = $false)] | ||
| [string]$SecretStoreName = 'Datto_SecretStore', | ||
|
|
||
| [Parameter()] | ||
| [switch]$Force | ||
| ) | ||
|
|
||
| begin { | ||
|
|
||
| # Pass the InformationAction parameter if bound, default to 'Continue' | ||
| if ($PSBoundParameters.ContainsKey('InformationAction')) { $InformationPreference = $PSBoundParameters['InformationAction'] } else { $InformationPreference = 'Continue' } | ||
|
|
||
| if ( !(Test-SecretVault -Name $SecretStoreName -ErrorAction Stop) ) { | ||
| Write-Error "Ensure the default SecretManagement Vault is installed and configured. Use 'Register-SecretVault' -Name $SecretStoreName -DefaultVault' first!" -ErrorAction Stop | ||
| } | ||
|
|
||
| } | ||
|
|
||
| process { | ||
|
|
||
| $secretExists = Get-Secret -Name $SecretName -Vault $SecretStoreName -ErrorAction SilentlyContinue | ||
| if ($secretExists) { | ||
| $confirmValue = -not $Force | ||
|
|
||
| try { | ||
| if ($Force) { Write-Verbose "Overwriting secret [ $SecretName ]" } | ||
| Set-Secret -Name $SecretName -Secret $DBPool_ApiKey -Vault $SecretStoreName -Confirm:$confirmValue -ErrorAction Stop | ||
| } catch { | ||
| Write-Error $_ | ||
| } | ||
| } else { | ||
| if ($PSCmdlet.ShouldProcess("Secret [ $SecretName ]", "Set secret in vault [ $SecretStoreName ]")) { | ||
| try { | ||
| Set-Secret -Name $SecretName -Secret $DBPool_ApiKey -Vault $SecretStoreName -ErrorAction Stop | ||
| Write-Information "Secret [ $SecretName ] has been successfully set." | ||
| } catch { | ||
| Write-Error $_ | ||
| } | ||
| } | ||
| } | ||
|
|
||
| } | ||
|
|
||
| end { | ||
|
|
||
| Add-DBPoolApiKey -apiKey $DBPool_ApiKey -Verbose:$false -Force | ||
|
|
||
| } | ||
|
|
||
| } | ||
|
|
||
| #EndRegion | ||
| #Region | ||
|
|
||
| function Add-DattoSecretStore { | ||
| <# | ||
| .SYNOPSIS | ||
| Adds a local secret store using the Microsoft.PowerShell.SecretStore module. | ||
|
|
||
| .DESCRIPTION | ||
| This function adds a local secret store using the Microsoft.PowerShell.SecretStore module. Checks if the secret store is installed and install if not found. | ||
| The function also sets the secret store configuration for the default vault. | ||
|
|
||
| .PARAMETER Name | ||
| The name of the secret store to add. Defaults to 'Datto_SecretStore'. | ||
|
|
||
| .PARAMETER ModuleName | ||
| The name of the module to use for the secret store. Defaults to 'Microsoft.PowerShell.SecretStore'. | ||
|
|
||
| .EXAMPLE | ||
| Add-DattoSecretStore | ||
|
|
||
| Adds a local secret store named 'Datto_SecretStore' using the Microsoft.PowerShell.SecretStore module. | ||
|
|
||
| .EXAMPLE | ||
| Add-DattoSecretStore -Name 'Custom_SecretsVault' -ModuleName 'Custom.SecretStore' | ||
|
|
||
| Adds a local secret store named 'Custom_SecretsVault' using the 'Custom.SecretStore' module. | ||
|
|
||
| .INPUTS | ||
| N/A | ||
|
|
||
| .OUTPUTS | ||
| N/A | ||
|
|
||
| .NOTES | ||
| N/A | ||
|
|
||
| .LINK | ||
| https://datto-dbpool-refresh.kentsapp.com/Internal/apiKey/Add-DattoSecretStore/ | ||
| #> | ||
| [CmdletBinding()] | ||
| param ( | ||
| [Parameter(Mandatory = $false)] | ||
| [string]$Name = 'Datto_SecretStore', | ||
|
|
||
| [Parameter(Mandatory = $false)] | ||
| [string]$ModuleName = 'Microsoft.PowerShell.SecretStore' | ||
| ) | ||
|
|
||
| begin { | ||
|
|
||
| # Pass the InformationAction parameter if bound, default to 'Continue' | ||
| if ($PSBoundParameters.ContainsKey('InformationAction')) { $InformationPreference = $PSBoundParameters['InformationAction'] } else { $InformationPreference = 'Continue' } | ||
|
|
||
| # Check if the SecretManagement module is installed | ||
| $installedModule = Get-InstalledModule -Name $ModuleName -ErrorAction SilentlyContinue | ||
| if ($null -eq $installedModule) { | ||
| try { | ||
| # Use PSResourceGet to install the module | ||
| if (Get-InstalledModule -Name 'PSResourceGet' -ErrorAction SilentlyContinue) { | ||
| try { | ||
| Install-PSResource -Name $ModuleName -Scope CurrentUser -TrustRepository -Reinstall -NoClobber -ErrorAction Stop | ||
| } catch { | ||
| Write-Error "Failed to install $ModuleName module using 'PSResourceGet': $_" | ||
| return | ||
| } | ||
| } else { | ||
| # Fall back to using Install-Module | ||
| try { | ||
| Install-Module -Name $ModuleName -Scope CurrentUser -Force -ErrorAction Stop -AllowClobber | ||
| } catch { | ||
| Write-Error "Failed to install $ModuleName module using 'Install-Module': $_" | ||
| return | ||
| } | ||
| } | ||
| } catch { | ||
| Write-Error $_ | ||
| return | ||
| } | ||
| } | ||
|
|
||
| } | ||
|
|
||
| process { | ||
|
|
||
| if ($ModuleName -eq 'Microsoft.PowerShell.SecretStore') { | ||
| $storeConfiguration = @{ | ||
| Authentication = 'None' | ||
| PasswordTimeout = 600 # 10 minutes | ||
| Interaction = 'None' | ||
| #Password = $password | ||
| Confirm = $False | ||
| } | ||
| Set-SecretStoreConfiguration @storeConfiguration | ||
| } | ||
|
|
||
| $secretStore = Get-SecretVault -ErrorAction SilentlyContinue | Where-Object { $_.Name -eq $Name } | ||
| if ($null -eq $secretStore) { | ||
| # Add a local secret store if the specified one is not found | ||
| try { | ||
| Register-SecretVault -Name $Name -ModuleName $ModuleName -DefaultVault -ErrorAction Stop | ||
| Write-Information "Local secret store [ $Name ] has been added and set as the default vault." | ||
| } catch { | ||
| Write-Error "Failed to register the local secret store: $_" | ||
| } | ||
| } else { | ||
| Write-Information "The secret store [ $Name ] is already set." | ||
| } | ||
|
|
||
| } | ||
|
|
||
| end { | ||
| #$Env:Datto_SecretStore = $Name | ||
| } | ||
| } | ||
|
|
||
| #EndRegion | ||
| #Region | ||
|
|
||
| function Update-RefreshDBPoolModule { | ||
| <# | ||
| .SYNOPSIS | ||
| Updates the Datto.DBPool.Refresh module if a newer version is available online. | ||
|
|
||
| .DESCRIPTION | ||
| This function checks for updates to the Datto.DBPool.Refresh module and updates it if a newer version is available online. | ||
| The auto-update feature can be disabled by setting the AutoUpdate parameter to $false otherwise, it will default to $true. | ||
|
|
||
| .PARAMETER ModuleName | ||
| The name of the module to update. Defaults to 'Datto.DBPool.Refresh'. | ||
|
|
||
| .PARAMETER AutoUpdate | ||
| If specified, the module will be updated if a newer version is available online. Defaults to $RefreshDBPool_Enable_AutoUpdate variable. | ||
|
|
||
| .PARAMETER AllowPrerelease | ||
| If specified, the module will be updated to the latest prerelease version if available. Defaults to $false. | ||
|
|
||
| .INPUTS | ||
| [string] - ModuleName | ||
|
|
||
| .OUTPUTS | ||
| N/A | ||
|
|
||
| .EXAMPLE | ||
| Update-RefreshDBPoolModule -ModuleName 'Datto.DBPool.Refresh' -AutoUpdate:$true -AllowPrerelease:$false | ||
|
|
||
| Updates the Datto.DBPool.Refresh module if a newer version is available online. | ||
|
|
||
| .NOTES | ||
| N/A | ||
|
|
||
| .LINK | ||
| https://datto-dbpool-refresh.kentsapp.com/Internal/autoUpdate/Update-RefreshDBPoolModule/ | ||
| #> | ||
| [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Low')] | ||
| param ( | ||
| [Parameter( Position = 0, Mandatory = $False, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True )] | ||
| [String]$ModuleName = 'Datto.DBPool.Refresh', | ||
|
|
||
| [Parameter(Position = 1, Mandatory = $False)] | ||
| [switch]$AutoUpdate = $RefreshDBPool_Enable_AutoUpdate, | ||
|
|
||
| [Parameter(Position = 2, Mandatory = $False)] | ||
| [switch]$AllowPrerelease = $False | ||
| ) | ||
|
|
||
| begin { | ||
|
|
||
| # Pass the InformationAction parameter if bound, default to 'Continue' | ||
| if ($PSBoundParameters.ContainsKey('InformationAction')) { $InformationPreference = $PSBoundParameters['InformationAction'] } else { $InformationPreference = 'Continue' } | ||
|
|
||
| if ($null -eq $PSBoundParameters['AutoUpdate'] -and $null -eq $RefreshDBPool_Enable_AutoUpdate) { | ||
| $AutoUpdate = $true | ||
| Write-Warning "[ RefreshDBPool_Enable_AutoUpdate ] variable not set, defaulting to $AutoUpdate." | ||
| } | ||
| } | ||
|
|
||
| process { | ||
|
|
||
| switch ($AutoUpdate) { | ||
| $True { | ||
| # Check to update the module if the online version seen is higher than the installed version | ||
| Write-Verbose "Module AutoUpdate is enabled, checking for updates to the module [ $ModuleName ]..." | ||
| try { | ||
|
|
||
| $installedModule = if (Get-Command -Name Get-InstalledPSResource -ErrorAction SilentlyContinue) { | ||
| Get-InstalledPSResource -Name $ModuleName -ErrorAction SilentlyContinue -Verbose:$false -Debug:$false | ||
| } else { | ||
| Get-InstalledModule -Name $ModuleName -AllowPrerelease:$AllowPrerelease -ErrorAction SilentlyContinue -Verbose:$false -Debug:$false | ||
| } | ||
| $onlineModule = if (Get-Command -Name Find-PSResource -ErrorAction SilentlyContinue) { | ||
| Find-PSResource -Name $ModuleName -Prerelease:$AllowPrerelease -ErrorAction SilentlyContinue -Verbose:$false -Debug:$false | ||
| } else { | ||
| Find-Module -Name $ModuleName -AllowPrerelease:$AllowPrerelease -ErrorAction SilentlyContinue -Verbose:$false -Debug:$false | ||
| } | ||
| $installedModule = $installedModule | Sort-Object -Property { [version]$_.Version } -Descending | Select-Object -First 1 | ||
| $onlineModule = $onlineModule | Sort-Object -Property { [version]$_.Version } -Descending | Select-Object -First 1 | ||
| Write-Debug "Installed module: [ $($installedModule.Name) ] and Online module: [ $($onlineModule.Name) ]" | ||
|
|
||
| if (!$installedModule) { | ||
| try { | ||
| Write-Warning "Module [ $ModuleName ] does not appear to be installed, attempting to install." | ||
| if (Get-Command -Name Install-PSResource -ErrorAction SilentlyContinue) { | ||
| Install-PSResource -Name $ModuleName -Scope 'CurrentUser' -TrustRepository -Prerelease:$AllowPrerelease -ErrorAction Stop -Verbose:$false -Debug:$false | ||
| } else { | ||
| Install-Module $ModuleName -Scope 'CurrentUser' -Force -AllowPrerelease:$AllowPrerelease -SkipPublisherCheck -ErrorAction Stop -Verbose:$false -Debug:$false | ||
| } | ||
| Write-Information "Module [ $ModuleName ] successfully installed." | ||
| Import-Module -Name $ModuleName -Force -Verbose:$false -Debug:$false | ||
| } catch { | ||
| throw "Error installing module $ModuleName`: $_" | ||
| } | ||
| } else { | ||
| Write-Verbose "Module [ $($installedModule.Name) ] is already installed on the local system." | ||
|
|
||
| $installedVersion = [version]$installedModule.Version | ||
|
|
||
| if ($null -ne $onlineModule -and $onlineModule.Version) { | ||
| $onlineVersion = [version]$onlineModule.Version | ||
|
|
||
| Write-Debug "Installed version: [ $( $installedVersion.ToString() ) ] and Online version: [ $( $onlineVersion.ToString() ) ]" | ||
|
|
||
| if ($installedVersion -eq $onlineVersion) { | ||
| Write-Host "$ModuleName version installed is [ $( $installedVersion.ToString() ) ] which matches the online version [ $( $onlineVersion.ToString() ) ]" -ForegroundColor Green | ||
| } elseif ($installedVersion -gt $onlineVersion) { | ||
| Write-Host "$ModuleName version installed is [ $( $installedVersion.ToString() ) ] which is greater than the online version [ $( $onlineVersion.ToString() ) ]`nStrange, but okay I guess?`n" -ForegroundColor Gray | ||
| } elseif ($installedVersion -lt $onlineVersion) { | ||
| Write-Warning "$ModuleName version installed is [ $( $installedVersion.ToString() ) ] which is less than the online version [ $( $onlineVersion.ToString() ) ]" | ||
|
|
||
| Write-Information "Updating [ $ModuleName ] from version [ $( $installedVersion.ToString() ) ] to [ $( $onlineVersion.ToString() ) ]." | ||
| if (Get-Command -Name Update-PSResource -ErrorAction SilentlyContinue) { | ||
| Update-PSResource -Name $ModuleName -Force -Prerelease:$AllowPrerelease -ErrorAction Stop -Verbose:$false -Debug:$false | ||
| } else { | ||
| Update-Module -Name $ModuleName -Force -TrustRepository -AllowPrerelease:$AllowPrerelease -ErrorAction Stop -Verbose:$false -Debug:$false | ||
| } | ||
|
|
||
| Import-Module -Name $ModuleName -Force -Verbose:$false -Debug:$false | ||
| } | ||
| } else { | ||
| Write-Warning "Failed to retrieve the online version of $ModuleName. Skipping update." | ||
| } | ||
| } | ||
| } catch { | ||
| Write-Error $_ | ||
| } | ||
|
|
||
| } Default { | ||
| Write-Information "Module AutoUpdate is disabled, skipping update for module '$ModuleName'." | ||
| } | ||
|
|
||
| } | ||
|
|
||
| } | ||
|
|
||
| end {} | ||
|
|
||
| } | ||
|
|
||
| #EndRegion | ||
| #Region | ||
|
|
||
| function Remove-RefreshDBPoolLog { | ||
| <# | ||
| .SYNOPSIS | ||
| Remove log files older than a specified number of days. | ||
|
|
||
| .DESCRIPTION | ||
| The Remove-RefreshDBPoolLog cmdlet removes log files older than a specified number of days. | ||
|
|
||
| By default, log files are stored in the following location and will be removed: | ||
| $env:USERPROFILE\RefreshDBPool\Logs | ||
|
|
||
| .PARAMETER LogPath | ||
| Define the location of the log files. | ||
|
|
||
| By default, log files are stored in the following location: | ||
| $env:USERPROFILE\RefreshDBPool\Logs | ||
|
|
||
| .PARAMETER LogFileName | ||
| Define the name of the log files. | ||
|
|
||
| By default, log files are named: | ||
| RefreshDBPool_*.log | ||
|
|
||
| .PARAMETER LogRotationDays | ||
| Define the number of days to keep log files. | ||
| By default, log files older than 90 days will be removed. | ||
|
|
||
| .PARAMETER Force | ||
| If specified, the function will not prompt for confirmation before removing the log files. | ||
|
|
||
| .EXAMPLE | ||
| Remove-RefreshDBPoolLog | ||
|
|
||
| Remove log files older than 90 days. | ||
|
|
||
| .EXAMPLE | ||
| Remove-RefreshDBPoolLog -LogPath C:\RefreshDBPool\Logs -LogFileName "RefreshDBPool_*.log" -LogRotationDays 7 -Force | ||
|
|
||
| Remove log files older than 7 days from the specified location. | ||
|
|
||
| .INPUTS | ||
| N/A | ||
|
|
||
| .OUTPUTS | ||
| N/A | ||
|
|
||
| .NOTES | ||
| N/A | ||
|
|
||
| .LINK | ||
| https://datto-dbpool-refresh.kentsapp.com/Internal/logging/Remove-RefreshDBPoolLog/ | ||
| #> | ||
| [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] | ||
| param ( | ||
| [string]$LogPath = $RefreshDBPool_LogPath, | ||
|
|
||
| [string]$LogFileName = $RefreshDBPool_LogFileName, | ||
|
|
||
| [int]$LogRotationDays = $RefreshDBPool_LogRotationDays, | ||
|
|
||
| [switch]$Force | ||
| ) | ||
|
|
||
| begin { | ||
|
|
||
| if (-not (Test-Path -Path $LogPath -ErrorAction SilentlyContinue)) { | ||
| throw "Log path does not exist. Run 'Export-RefreshDBPoolModuleSetting' first." | ||
| } | ||
|
|
||
| } | ||
|
|
||
| process { | ||
|
|
||
| $logFiles = Get-ChildItem -Path $LogPath -Filter "*$LogFileName" -File -ErrorAction SilentlyContinue | ||
| if (-not $logFiles) { | ||
| Write-Warning "No log files matching '*$LogFileName' found in '$LogPath'." | ||
| return | ||
| } | ||
|
|
||
| $logFilesToRemove = $logFiles | Where-Object { $_.LastWriteTime -lt (Get-Date).AddDays(-$LogRotationDays) } | ||
| if (-not $logFilesToRemove) { | ||
| Write-Warning "No log files found in [ $LogPath ] older than '$LogRotationDays' days." | ||
| return | ||
| } | ||
|
|
||
| foreach ($log in $logFilesToRemove) { | ||
|
|
||
| if ($Force -or $PSCmdlet.ShouldProcess("[ $log ]", 'Remove Log file')) { | ||
| try { | ||
| Remove-Item -Path $log.FullName -Force -ErrorAction Stop | ||
| Write-Verbose -Message "Removed log file: [ $($log.FullName) ]" | ||
| } | ||
| catch { | ||
| Write-Verbose -Message "Failed to remove log file: [ $($log.FullName) ]" | ||
| Write-Error -Message "$_" | ||
| } | ||
| } | ||
|
|
||
| } | ||
|
|
||
| } | ||
|
|
||
| end {} | ||
|
|
||
| } | ||
|
|
||
| #EndRegion | ||
| #Region | ||
|
|
||
| function Export-RefreshDBPoolModuleSetting { | ||
| <# | ||
| .SYNOPSIS | ||
| Exports various module settings to a configuration file. | ||
|
|
||
| .DESCRIPTION | ||
| The Export-RefreshDBPoolSettings cmdlet exports various module settings to a configuration file which can be used to override default settings. | ||
|
|
||
| .PARAMETER RefreshDBPoolConfPath | ||
| Define the location to store the Refresh DBPool configuration file. | ||
|
|
||
| By default the configuration file is stored in the following location: | ||
| $env:USERPROFILE\RefreshDBPool | ||
|
|
||
| .PARAMETER RefreshDBPoolConfFile | ||
| Define the name of the refresh DBPool configuration file. | ||
|
|
||
| By default the configuration file is named: | ||
| config.psd1 | ||
|
|
||
| .EXAMPLE | ||
| Export-RefreshDBPoolSettings | ||
|
|
||
| Validates that the BaseURI, and JSON depth are set then exports their values | ||
| to the current user's DBPool configuration file located at: | ||
| $env:USERPROFILE\RefreshDBPool\config.psd1 | ||
|
|
||
| .EXAMPLE | ||
| Export-RefreshDBPoolSettings -DBPoolConfPath C:\RefreshDBPool -DBPoolConfFile MyConfig.psd1 | ||
|
|
||
| Validates that the BaseURI, and JSON depth are set then exports their values | ||
| to the current user's DBPool configuration file located at: | ||
| C:\RefreshDBPool\MyConfig.psd1 | ||
|
|
||
| .INPUTS | ||
| N/A | ||
|
|
||
| .OUTPUTS | ||
| N/A | ||
|
|
||
| .NOTES | ||
| N/A | ||
|
|
||
| .LINK | ||
| https://datto-dbpool-refresh.kentsapp.com/Internal/moduleSettings/Export-RefreshDBPoolModuleSetting/ | ||
| #> | ||
|
|
||
| [CmdletBinding(DefaultParameterSetName = 'set')] | ||
| Param ( | ||
| [Parameter(ParameterSetName = 'set')] | ||
| [string]$RefreshDBPoolConfPath = $(Join-Path -Path $home -ChildPath $(if ($IsWindows -or $PSEdition -eq 'Desktop'){"RefreshDBPool"}else{".RefreshDBPool"}) ), | ||
|
|
||
| [Parameter(ParameterSetName = 'set')] | ||
| [string]$RefreshDBPoolConfFile = 'config.psd1' | ||
| ) | ||
|
|
||
| begin {} | ||
|
|
||
| process { | ||
|
|
||
| $RefreshDBPoolConfig = Join-Path -Path $RefreshDBPoolConfPath -ChildPath $RefreshDBPoolConfFile | ||
| Write-Verbose "Exporting 'Refresh DBPool Module' settings to [ $RefreshDBPoolConfig ]" | ||
|
|
||
| # Confirm variables exist and are not null before exporting | ||
| if ($DBPool_Base_URI -and $DBPool_JSON_Conversion_Depth) { | ||
|
|
||
| if ($IsWindows -or $PSEdition -eq 'Desktop') { | ||
| New-Item -Path $RefreshDBPoolConfPath -ItemType Directory -Force | ForEach-Object { $_.Attributes = $_.Attributes -bor "Hidden" } | ||
| } | ||
| else{ | ||
| New-Item -Path $RefreshDBPoolConfPath -ItemType Directory -Force | ||
| } | ||
| @" | ||
| @{ | ||
| ### DBPOOL REFRESH OVERRIDE CONFIG VARIABLES ### | ||
| ## This config file is used to override variables for the DBPool Refresh module. | ||
| ## Variables can be set below and uncommented as required. | ||
|
|
||
|
|
||
| # Container IDs to refresh, by default all containers will be refreshed. | ||
|
|
||
| # RefreshDBPool_Container_Ids = @( 123, 456, 789 ) | ||
|
|
||
|
|
||
| # URL of the API to be checked. | ||
| # Defaulted to "$DBPool_Base_URI" in the script already and should not need to be changed or uncommented. | ||
|
|
||
| # DBPool_Base_URI = 'https://dbpool.domain.tld' | ||
|
|
||
|
|
||
| # Enable / Disable Auto-Update of the Refresh DBPool Module and its dependencies. | ||
|
|
||
| RefreshDBPool_Enable_AutoUpdate = "True" | ||
|
|
||
|
|
||
| # Enable / Disable Logging for the Refresh DBPool Module. | ||
|
|
||
| RefreshDBPool_Logging_Enabled = "True" | ||
| RefreshDBPool_LogPath = "$(Join-Path -Path $RefreshDBPoolConfPath -ChildPath 'Logs')" | ||
| RefreshDBPool_LogFileName = 'RefreshDBPool.log' | ||
| RefreshDBPool_LogRotationEnabled = "True" | ||
| RefreshDBPool_LogRotationDays = 90 | ||
|
|
||
|
|
||
| # Script timeout wait for API reachable, and child process jobs to "complete" and return a response (success or failure error) before exit. | ||
| # Default in the script is set to 3600 seconds (60 minutes). | ||
|
|
||
| # RefreshDBPool_TimeoutSeconds = 300 | ||
|
|
||
| # Refresh DBPool Script Verbose Preference | ||
| # RefreshDBPool_VerbosePreference = "True" | ||
|
|
||
|
|
||
| ## END OF CONFIG FILE | ||
| } | ||
| "@ | Out-File -FilePath $RefreshDBPoolConfig -Force | ||
| } | ||
| else { | ||
| Write-Error "Failed to export DBPool Module settings to [ $RefreshDBPoolConfig ]" | ||
| Write-Error $_ -ErrorAction Stop | ||
| } | ||
|
|
||
| } | ||
|
|
||
| end {} | ||
|
|
||
| } | ||
|
|
||
| #EndRegion | ||
| #Region | ||
|
|
||
| function Get-RefreshDBPoolModuleSetting { | ||
| <# | ||
| .SYNOPSIS | ||
| Gets the saved DBPool configuration settings | ||
|
|
||
| .DESCRIPTION | ||
| The Get-RefreshDBPoolModuleSetting cmdlet gets the saved DBPool refresh configuration settings | ||
| from the local system. | ||
|
|
||
| By default the configuration file is stored in the following location: | ||
| $env:USERPROFILE\RefreshDBPool | ||
|
|
||
| .PARAMETER RefreshDBPoolConfPath | ||
| Define the location to store the DBPool configuration file. | ||
|
|
||
| By default the configuration file is stored in the following location: | ||
| $env:USERPROFILE\RefreshDBPool | ||
|
|
||
| .PARAMETER RefreshDBPoolConfFile | ||
| Define the name of the DBPool configuration file. | ||
|
|
||
| By default the configuration file is named: | ||
| config.psd1 | ||
|
|
||
| .PARAMETER openConfFile | ||
| Opens the DBPool configuration file | ||
|
|
||
| .EXAMPLE | ||
| Get-RefreshDBPoolModuleSetting | ||
|
|
||
| Gets the contents of the configuration file that was created with the | ||
| Export-RefreshDBPoolModuleSettings | ||
|
|
||
| The default location of the DBPool configuration file is: | ||
| $env:USERPROFILE\RefreshDBPool\config.psd1 | ||
|
|
||
| .EXAMPLE | ||
| Get-RefreshDBPoolModuleSetting -RefreshDBPoolConfig C:\RefreshDBPool -DBPoolConfFile MyConfig.psd1 -openConfFile | ||
|
|
||
| Opens the configuration file from the defined location in the default editor | ||
|
|
||
| The location of the DBPool configuration file in this example is: | ||
| C:\RefreshDBPool\MyConfig.psd1 | ||
|
|
||
| .INPUTS | ||
| N/A | ||
|
|
||
| .OUTPUTS | ||
| N/A | ||
|
|
||
| .NOTES | ||
| N/A | ||
|
|
||
| .LINK | ||
| https://datto-dbpool-refresh.kentsapp.com/Internal/moduleSettings/Get-RefreshDBPoolModuleSetting/ | ||
| #> | ||
|
|
||
| [CmdletBinding(DefaultParameterSetName = 'index')] | ||
| Param ( | ||
| [Parameter(Mandatory = $false, ParameterSetName = 'index')] | ||
| [string]$RefreshDBPoolConfPath = $(Join-Path -Path $home -ChildPath $(if ($IsWindows -or $PSEdition -eq 'Desktop'){"RefreshDBPool"}else{".RefreshDBPool"}) ), | ||
|
|
||
| [Parameter(Mandatory = $false, ParameterSetName = 'index')] | ||
| [String]$RefreshDBPoolConfFile = 'config.psd1', | ||
|
|
||
| [Parameter(Mandatory = $false, ParameterSetName = 'show')] | ||
| [Switch]$openConfFile | ||
| ) | ||
|
|
||
| begin { | ||
| $RefreshDBPoolConfig = Join-Path -Path $RefreshDBPoolConfPath -ChildPath $RefreshDBPoolConfFile | ||
| } | ||
|
|
||
| process { | ||
|
|
||
| if ( Test-Path -Path $RefreshDBPoolConfig ){ | ||
|
|
||
| if($openConfFile){ | ||
| Invoke-Item -Path $RefreshDBPoolConfig | ||
| } | ||
| else{ | ||
| Import-LocalizedData -BaseDirectory $RefreshDBPoolConfPath -FileName $RefreshDBPoolConfFile | ||
| } | ||
|
|
||
| } | ||
| else{ | ||
| Write-Verbose "No configuration file found at [ $RefreshDBPoolConfig ] run 'Export-RefreshDBPoolModuleSetting' to create one." | ||
| } | ||
|
|
||
| } | ||
|
|
||
| end {} | ||
|
|
||
| } | ||
|
|
||
| #EndRegion | ||
| #Region | ||
|
|
||
| function Import-RefreshDBPoolModuleSetting { | ||
| <# | ||
| .SYNOPSIS | ||
| Imports the DBPool BaseURI, API, & JSON configuration information to the current session. | ||
|
|
||
| .DESCRIPTION | ||
| The Import-RefreshDBPoolModuleSetting cmdlet imports the DBPool BaseURI, API, & JSON configuration | ||
| information stored in the DBPool refresh configuration file to the users current session. | ||
|
|
||
| By default the configuration file is stored in the following location: | ||
| $env:USERPROFILE\RefreshDBPool | ||
|
|
||
| .PARAMETER RefreshDBPoolConfPath | ||
| Define the location to store the DBPool configuration file. | ||
|
|
||
| By default the configuration file is stored in the following location: | ||
| $env:USERPROFILE\RefreshDBPool | ||
|
|
||
| .PARAMETER RefreshDBPoolConfFile | ||
| Define the name of the DBPool configuration file. | ||
|
|
||
| By default the configuration file is named: | ||
| config.psd1 | ||
|
|
||
| .EXAMPLE | ||
| Import-RefreshDBPoolModuleSetting | ||
|
|
||
| Validates that the configuration file created with the Export-RefreshDBPoolModuleSettings cmdlet exists | ||
| then imports the stored data into the current users session. | ||
|
|
||
| The default location of the DBPool configuration file is: | ||
| $env:USERPROFILE\RefreshDBPool\config.psd1 | ||
|
|
||
| .EXAMPLE | ||
| Import-RefreshDBPoolModuleSetting -RefreshDBPoolConfPath C:\RefreshDBPool -RefreshDBPoolConfFile MyConfig.psd1 | ||
|
|
||
| Validates that the configuration file created with the Export-RefreshDBPoolModuleSettings cmdlet exists | ||
| then imports the stored data into the current users session. | ||
|
|
||
| The location of the DBPool configuration file in this example is: | ||
| C:\RefreshDBPool\MyConfig.psd1 | ||
|
|
||
| .INPUTS | ||
| N/A | ||
|
|
||
| .OUTPUTS | ||
| N/A | ||
|
|
||
| .NOTES | ||
| N/A | ||
|
|
||
| .LINK | ||
| https://datto-dbpool-refresh.kentsapp.com/Internal/moduleSettings/Import-RefreshDBPoolModuleSetting/ | ||
| #> | ||
|
|
||
| [CmdletBinding(DefaultParameterSetName = 'set')] | ||
| Param ( | ||
| [Parameter(ParameterSetName = 'set')] | ||
| [string]$RefreshDBPoolConfPath = $(Join-Path -Path $home -ChildPath $(if ($IsWindows -or $PSEdition -eq 'Desktop'){"RefreshDBPool"}else{".RefreshDBPool"}) ), | ||
|
|
||
| [Parameter(ParameterSetName = 'set')] | ||
| [string]$RefreshDBPoolConfFile = 'config.psd1' | ||
| ) | ||
|
|
||
| begin { | ||
| $RefreshDBPoolConfig = Join-Path -Path $RefreshDBPoolConfPath -ChildPath $RefreshDBPoolConfFile | ||
| } | ||
|
|
||
| process { | ||
|
|
||
| if ( Test-Path $RefreshDBPoolConfig ) { | ||
| Import-LocalizedData -BaseDirectory $RefreshDBPoolConfPath -FileName $RefreshDBPoolConfFile -BindingVariable tmp_config | ||
|
|
||
| foreach ($key in $tmp_config.Keys) { | ||
| #Write-Verbose "Setting variable [ $key ] to [ $($tmp_config[$key]) ]" | ||
| $value = $tmp_config[$key] | ||
| if ($value -eq 'True') { $value = $true } elseif ($value -eq 'False') { $value = $false } | ||
| if (-not [string]::IsNullOrEmpty($value)) { | ||
| Set-Variable -Name $key -Value $value -Scope Global -Force -Verbose:$VerbosePreference | ||
| } | ||
| } | ||
|
|
||
| if ($tmp_config.DBPool_Base_URI) { | ||
| # Send to function to strip potentially superfluous slash (/) | ||
| Add-DBPoolBaseURI $tmp_config.DBPool_Base_URI -Verbose:$VerbosePreference | ||
| } else { | ||
| Add-DBPoolBaseURI -Verbose:$VerbosePreference | ||
| } | ||
|
|
||
| Write-Verbose "RefreshDBPool Module configuration loaded successfully from [ $RefreshDBPoolConfig ]" | ||
|
|
||
| # Clean things up | ||
| Remove-Variable "tmp_config" -Force | ||
| } | ||
| else { | ||
| Write-Verbose "No configuration file found at [ $RefreshDBPoolConfig ] run 'Set-RefreshDBPoolApiKey' to get started." | ||
|
|
||
| Add-DBPoolBaseURI -Verbose:$VerbosePreference | ||
|
|
||
| Set-Variable -Name 'RefreshDBPool_Enable_AutoUpdate' -Value $true -Option ReadOnly -Scope Global -Force -Verbose:$VerbosePreference | ||
| } | ||
|
|
||
| } | ||
|
|
||
| end {} | ||
|
|
||
| } | ||
|
|
||
| #EndRegion | ||
| #Region | ||
|
|
||
| # Used to auto load either baseline settings or saved configurations when the module is imported | ||
| Import-RefreshDBPoolModuleSetting -Verbose:$VerbosePreference | ||
|
|
||
| if (Test-SecretVault -Name 'Datto_SecretStore' -WarningAction SilentlyContinue -ErrorAction SilentlyContinue) { | ||
| try { | ||
| Get-RefreshDBPoolApiKey -Force -ErrorAction SilentlyContinue | Out-Null | ||
| } | ||
| catch { | ||
| Write-Warning $_ | ||
| } | ||
| } | ||
|
|
||
| #EndRegion | ||
| #Region | ||
|
|
||
| function Remove-RefreshDBPoolModuleSetting { | ||
| <# | ||
| .SYNOPSIS | ||
| Removes the stored Refresh DBPool configuration folder. | ||
|
|
||
| .DESCRIPTION | ||
| The Remove-RefreshDBPoolModuleSetting cmdlet removes the Refresh DBPool folder and its files. | ||
| This cmdlet also has the option to remove sensitive Refresh DBPool variables as well. | ||
|
|
||
| By default configuration files are stored in the following location and will be removed: | ||
| $env:USERPROFILE\RefreshDBPool | ||
|
|
||
| .PARAMETER RefreshDBPoolConfPath | ||
| Define the location of the Refresh DBPool configuration folder. | ||
|
|
||
| By default the configuration folder is located at: | ||
| $env:USERPROFILE\RefreshDBPool | ||
|
|
||
| .PARAMETER andVariables | ||
| Define if sensitive Refresh DBPool variables should be removed as well. | ||
|
|
||
| By default the variables are not removed. | ||
|
|
||
| .EXAMPLE | ||
| Remove-RefreshDBPoolModuleSetting | ||
|
|
||
| Checks to see if the default configuration folder exists and removes it if it does. | ||
|
|
||
| The default location of the Refresh DBPool configuration folder is: | ||
| $env:USERPROFILE\RefreshDBPool | ||
|
|
||
| .EXAMPLE | ||
| Remove-RefreshDBPoolModuleSetting -RefreshDBPoolConfPath C:\RefreshDBPool -andVariables | ||
|
|
||
| Checks to see if the defined configuration folder exists and removes it if it does. | ||
| If sensitive Refresh DBPool variables exist then they are removed as well. | ||
|
|
||
| The location of the Refresh DBPool configuration folder in this example is: | ||
| C:\RefreshDBPool | ||
|
|
||
| .INPUTS | ||
| N/A | ||
|
|
||
| .OUTPUTS | ||
| N/A | ||
|
|
||
| .NOTES | ||
| N/A | ||
|
|
||
| .LINK | ||
| https://datto-dbpool-refresh.kentsapp.com/Internal/moduleSettings/Remove-RefreshDBPoolModuleSetting/ | ||
| #> | ||
|
|
||
| [CmdletBinding(SupportsShouldProcess, DefaultParameterSetName = 'set')] | ||
| Param ( | ||
| [Parameter(ParameterSetName = 'set')] | ||
| [string]$RefreshDBPoolConfPath = $(Join-Path -Path $home -ChildPath $(if ($IsWindows -or $PSEdition -eq 'Desktop'){"RefreshDBPool"}else{".RefreshDBPool"}) ), | ||
|
|
||
| [Parameter(ParameterSetName = 'set')] | ||
| [switch]$andVariables | ||
| ) | ||
|
|
||
| begin { | ||
|
|
||
| # Pass the InformationAction parameter if bound, default to 'Continue' | ||
| if ($PSBoundParameters.ContainsKey('InformationAction')) { $InformationPreference = $PSBoundParameters['InformationAction'] } else { $InformationPreference = 'Continue' } | ||
|
|
||
| } | ||
|
|
||
| process { | ||
|
|
||
| if (Test-Path $RefreshDBPoolConfPath) { | ||
|
|
||
| Remove-Item -Path $RefreshDBPoolConfPath -Recurse -Force -WhatIf:$WhatIfPreference | ||
|
|
||
| If ($andVariables) { | ||
| Remove-RefreshDBPoolAPIKey -Force -Confirm:$ConfirmPreference -WhatIf:$WhatIfPreference | ||
| Remove-DBPoolBaseURI | ||
| } | ||
|
|
||
| if (!(Test-Path $RefreshDBPoolConfPath)) { | ||
| Write-Information "The RefreshDBPool configuration folder has been removed successfully from [ $RefreshDBPoolConfPath ]" | ||
| } | ||
| else { | ||
| Write-Error "The RefreshDBPool configuration folder could not be removed from [ $RefreshDBPoolConfPath ]" | ||
| } | ||
|
|
||
| } | ||
| else { | ||
| Write-Warning "No configuration folder found at [ $RefreshDBPoolConfPath ]" | ||
| } | ||
|
|
||
| } | ||
|
|
||
| end {} | ||
|
|
||
| } | ||
|
|
||
| #EndRegion | ||
| #Region | ||
|
|
||
| function Register-RefreshDBPoolTask { | ||
| <# | ||
| .SYNOPSIS | ||
| Creates a scheduled task to automate the refresh of Datto DBPool containers. | ||
|
|
||
| .DESCRIPTION | ||
| This function sets up a scheduled task that runs a PowerShell script to refresh Datto DBPool containers. | ||
| The task can be configured to run on specific days of the week and at a specified time. | ||
|
|
||
| .PARAMETER TriggerTime | ||
| Specifies the time of day at which the scheduled task should run. | ||
| This should be set to roughly ~1 hour before shift start, so that all containers are refreshed and ready for use. | ||
|
|
||
| .PARAMETER ExcludeDaysOfWeek | ||
| Specifies the days of the week on which the scheduled task should NOT be run. | ||
| This will generally be days off work, by default the task will not run on Sundays and Saturdays. | ||
|
|
||
| .INPUTS | ||
| N/A | ||
|
|
||
| .OUTPUTS | ||
| N/A | ||
|
|
||
| .EXAMPLE | ||
| Register-RefreshDBPoolTask -TriggerTime "7AM" | ||
|
|
||
| This example creates a scheduled task that runs every day at 7:00 AM, except on Sundays and Saturdays. | ||
|
|
||
| .EXAMPLE | ||
| Register-RefreshDBPoolTask -TriggerTime "15:00" | ||
|
|
||
| This example creates a scheduled task that runs every day at 3:00 PM, except on Sundays and Saturdays. | ||
|
|
||
| .EXAMPLE | ||
| Register-RefreshDBPoolTask -ExcludeDaysOfWeek 'Sunday','Monday' -TriggerTime "4:30PM" | ||
|
|
||
| This example creates a scheduled task that runs every day at 4:30 PM, except on Sunday and Monday. | ||
|
|
||
| .NOTES | ||
| This function is currently designed to work only on Windows systems. It uses the Task Scheduler to create and manage the scheduled task. | ||
| Will look to add support for Linux/MacOS using cron jobs or similar such as anacron in the future. | ||
|
|
||
| .LINK | ||
| https://datto-dbpool-refresh.kentsapp.com/Internal/scheduledTask/Register-RefreshDBPoolTask/ | ||
|
|
||
| .LINK | ||
| https://docs.microsoft.com/en-us/powershell/module/scheduledtasks/new-scheduledtask | ||
| #> | ||
|
|
||
| [CmdletBinding()] | ||
| [Alias('New-RefreshDBPoolTask')] | ||
| [OutputType([System.Void])] | ||
| param ( | ||
| [Parameter(Mandatory = $true, HelpMessage = "The time of day at which the scheduled task should run.")] | ||
| [DateTime]$TriggerTime, | ||
|
|
||
| [Parameter(Mandatory = $false, HelpMessage = "The days of the week on which the scheduled task should NOT be run.")] | ||
| [ValidateSet('Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday')] | ||
| [string[]]$ExcludeDaysOfWeek = @('Sunday','Saturday') | ||
| ) | ||
|
|
||
| begin { | ||
|
|
||
| # Days of the week to run the task | ||
| $daysToRun = $( [System.DayOfWeek].GetEnumValues() ) | Where-Object { $ExcludeDaysOfWeek -notcontains [System.DayOfWeek]::$_ } | ||
|
|
||
| if ($PSEdition -eq 'Desktop') { | ||
| #$PSExecutable = Join-Path -Path $PSHOME -ChildPath 'powershell.exe' | ||
| $PSExecutable = if (Get-Command -Name 'pwsh' -ErrorAction SilentlyContinue) { | ||
| (Get-Command pwsh).Source | ||
| } else { | ||
| (Get-Command powershell).Source | ||
| } | ||
| } elseif ($PSEdition -eq 'Core') { | ||
| if ($IsWindows) { | ||
| $PSExecutable = Join-Path -Path $PSHOME -ChildPath 'pwsh.exe' | ||
| } elseif ($IsLinux) { | ||
| } elseif ($IsMacOS) { | ||
| } | ||
| } | ||
|
|
||
| } | ||
|
|
||
| process { | ||
|
|
||
| $moduleBasePath = $( Split-Path -Path $((Get-Command Register-RefreshDBPoolTask).Module).path ) | ||
| $scriptDir = $( Join-Path -Path $moduleBasePath -ChildPath 'scripts' ) | ||
| $scriptFile = $( Join-Path -Path $scriptDir -ChildPath 'Invoke-RefreshDBPoolContainer.ps1' ) | ||
|
|
||
| if ($IsWindows -or $PSEdition -eq 'Desktop') { | ||
|
|
||
| $taskPath = 'Datto' | ||
| $taskName = 'DBPool-Refresh' | ||
| $taskDescription = 'Scheduled task to automate refresh of Datto DBPool containers.' | ||
|
|
||
| # Task trigger | ||
| $triggerParams = @{ | ||
| Weekly = $true | ||
| DaysOfWeek = $daysToRun | ||
| At = $TriggerTime | ||
| } | ||
| $taskTrigger = New-ScheduledTaskTrigger @triggerParams | ||
|
|
||
| # Task Action | ||
| $actionParams = @{ | ||
| Execute = "`"$PSExecutable`"" | ||
| Argument = "-WindowStyle Minimized -NoProfile -ExecutionPolicy Bypass -File `"$scriptFile`" -Bootstrap" | ||
| WorkingDirectory = "$moduleBasePath" | ||
| } | ||
| $taskAction = New-ScheduledTaskAction @actionParams | ||
|
|
||
| # Task Settings | ||
| $settingsParams = @{ | ||
| AllowStartIfOnBatteries = $true | ||
| Compatibility = 'Win8' | ||
| ExecutionTimeLimit = (New-TimeSpan -Hours 2) | ||
| RestartCount = 3 | ||
| RestartInterval = (New-TimeSpan -Minutes 5) | ||
| StartWhenAvailable = $true | ||
| WakeToRun = $true | ||
| } | ||
| $taskSettings = New-ScheduledTaskSettingsSet @settingsParams | ||
| # 3 corresponds to 'Stop the existing instance' https://stackoverflow.com/questions/59113643/stop-existing-instance-option-when-creating-windows-scheduled-task-using-powersh/59117015#59117015 | ||
| $taskSettings.CimInstanceProperties.Item('MultipleInstances').Value = 3 | ||
|
|
||
| # Task | ||
| $taskParams = @{ | ||
| Action = $taskAction | ||
| Description = $taskDescription | ||
| Settings = $taskSettings | ||
| Trigger = $taskTrigger | ||
| } | ||
| $task = New-ScheduledTask @taskParams | ||
| $task.Author = "Kent Sapp (@cksapp)" | ||
| $task.Documentation = 'https://datto-dbpool-refresh.kentsapp.com' | ||
|
|
||
| $registerParams = @{ | ||
| InputObject = $task | ||
| TaskName = $taskName | ||
| TaskPath = $taskPath | ||
| User = $env:USERNAME | ||
| Force = $true | ||
| ErrorAction = 'Stop' | ||
| } | ||
| try { | ||
| $scheduledTask = Register-ScheduledTask @registerParams | ||
|
|
||
| try { | ||
| $scheduledTask.Date = '2023-08-30T12:34:56.7890000' | ||
| Set-ScheduledTask -InputObject $scheduledTask -Verbose:$VerbosePreference -ErrorAction Stop | Out-Null | ||
| } | ||
| catch { | ||
| Write-Warning "Error updating 'Created Date' for scheduled task [ $taskName ]: $_" | ||
| } | ||
| } | ||
| catch { | ||
| Write-Error $_.Exception.Message | ||
| } | ||
| } | ||
| else { | ||
| Write-Error "This function is currently only supported on Windows." | ||
| #TODO: Add support for Linux/MacOS using cron jobs or similar such as anacron | ||
| } | ||
|
|
||
| } | ||
|
|
||
| end {} | ||
|
|
||
| } | ||
|
|
||
| #EndRegion | ||
| #Region | ||
|
|
||
| function Update-RefreshDBPoolTask { | ||
| <# | ||
| .SYNOPSIS | ||
| Updates the refresh DBPool scheduled task. | ||
|
|
||
| .DESCRIPTION | ||
| This function updates the scheduled task that runs the refresh DBPool script by updating path and arguments. | ||
|
|
||
| .PARAMETER Force | ||
| Forces the update of the scheduled task. | ||
|
|
||
| .INPUTS | ||
| N/A | ||
|
|
||
| .OUTPUTS | ||
| N/A | ||
|
|
||
| .EXAMPLE | ||
| Update-RefreshDBPoolTask | ||
|
|
||
| This example updates the scheduled task that runs the refresh DBPool script. | ||
|
|
||
| .NOTES | ||
| This function is currently only supported on Windows systems. | ||
|
|
||
| .LINK | ||
| https://datto-dbpool-refresh.kentsapp.com/Internal/scheduledTask/Update-RefreshDBPoolTask/ | ||
| #> | ||
| [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Low')] | ||
| param ( | ||
| [switch]$Force | ||
| ) | ||
|
|
||
| begin { | ||
|
|
||
| if ($PSEdition -eq 'Desktop') { | ||
| $PSExecutable = if (Get-Command -Name 'pwsh' -ErrorAction SilentlyContinue) { | ||
| (Get-Command pwsh).Source | ||
| } else { | ||
| (Get-Command powershell).Source | ||
| } | ||
| } elseif ($PSEdition -eq 'Core') { | ||
| if ($IsWindows) { | ||
| $PSExecutable = Join-Path -Path $PSHOME -ChildPath 'pwsh.exe' | ||
| } elseif ($IsLinux) { | ||
| } elseif ($IsMacOS) { | ||
| } | ||
| } | ||
|
|
||
| } | ||
|
|
||
| process { | ||
|
|
||
| $moduleBasePath = $( Split-Path -Path $((Get-Command Register-RefreshDBPoolTask).Module).path ) | ||
| $scriptDir = $( Join-Path -Path $moduleBasePath -ChildPath 'scripts' ) | ||
| $scriptFile = $( Join-Path -Path $scriptDir -ChildPath 'Invoke-RefreshDBPoolContainer.ps1' ) | ||
|
|
||
| if ($IsWindows -or $PSEdition -eq 'Desktop') { | ||
|
|
||
| $taskPath = 'Datto' | ||
| $taskName = 'DBPool-Refresh' | ||
| try { | ||
| $task = Get-ScheduledTask -TaskPath "*$taskPath*" -TaskName $taskName -ErrorAction SilentlyContinue | ||
|
|
||
| if (-not $task) { | ||
| Write-Warning "Scheduled task [ $taskName ] not found. Run 'Register-RefreshDBPoolTask' first." | ||
| return | ||
| } | ||
|
|
||
| if ($Force -or $PSCmdlet.ShouldProcess("Scheduled task [ $taskName ]", 'Update')) { | ||
| $actionParams = @{ | ||
| Execute = "$PSExecutable" | ||
| Argument = "-WindowStyle Minimized -NoProfile -ExecutionPolicy Bypass -File `"$scriptFile`"" | ||
| WorkingDirectory = "$moduleBasePath" | ||
| } | ||
| $task.Actions = New-ScheduledTaskAction @actionParams | ||
|
|
||
| Set-ScheduledTask -InputObject $task -Verbose:$VerbosePreference | ||
| } | ||
|
|
||
| } | ||
| catch { | ||
| Write-Error $_ | ||
| } | ||
| } | ||
| else { | ||
| Write-Warning "This function is currently only supported on Windows." | ||
| #TODO: Add support for Linux/MacOS using cron jobs or similar such as anacron | ||
| } | ||
|
|
||
| } | ||
|
|
||
| end {} | ||
|
|
||
| } | ||
|
|
||
| #EndRegion | ||
| #Region | ||
|
|
||
| function Copy-DBPoolParentContainer { | ||
| <# | ||
| .SYNOPSIS | ||
| Clones the specified DBPool parent container(s) using the DBPool API. | ||
|
|
||
| .DESCRIPTION | ||
| This function clones the specified DBPool parent container(s) using the DBPool API. The cloned container(s) will have the same parent container as the original container(s) and will be appended with the specified string. | ||
|
|
||
| .PARAMETER Id | ||
| The ID(s) of the parent container(s) to clone. | ||
|
|
||
| .PARAMETER DefaultDatabase | ||
| The DefaultDatabase(s) of the parent container(s) to clone. | ||
|
|
||
| .PARAMETER ContainerName_Append | ||
| The string to append to the cloned container name. The default value is 'clone'. | ||
|
|
||
| .PARAMETER Duplicate | ||
| If specified, the function will clone the parent container(s) even if a similar container already exists. | ||
|
|
||
| .PARAMETER AllowBeta | ||
| If specified, the function will allow cloning of parent containers with 'BETA' in the name. | ||
| By default, BETA containers are excluded from cloning. | ||
|
|
||
| .INPUTS | ||
| [int] - Array of ID(s) of the parent container(s) to clone. | ||
| [string] - Array of DefaultDatabase(s) of the parent container(s) to clone. | ||
|
|
||
| .OUTPUTS | ||
| [PSCustomObject] - Object containing the cloned container(s) information. | ||
|
|
||
| .EXAMPLE | ||
| Copy-DBPoolParentContainer -Id 1234 | ||
|
|
||
| Clones the DBPool parent container with the ID 1234. | ||
|
|
||
| .EXAMPLE | ||
| Copy-DBPoolParentContainer -DefaultDatabase 'exampleParentA' | ||
|
|
||
| Clones the DBPool parent container with the DefaultDatabase 'exampleParentA'. | ||
|
|
||
| .EXAMPLE | ||
| Copy-DBPoolParentContainer -Id 1234, 5678 -ContainerName_Append 'copy' | ||
|
|
||
| Clones the DBPool parent containers with the IDs 1234 and 5678 and appends 'copy' to the cloned container name. | ||
|
|
||
| .EXAMPLE | ||
| Copy-DBPoolParentContainer -DefaultDatabase 'exampleParentA', 'exampleParentB' -Duplicate | ||
|
|
||
| Clones the DBPool parent containers with the DefaultDatabase 'exampleParentA' and 'exampleParentB' even if similar containers already exist. | ||
|
|
||
| .EXAMPLE | ||
| Copy-DBPoolParentContainer -DefaultDatabase 'exampleParentA', 'exampleParentB', 'exampleParentA | ||
|
|
||
| Clones the DBPool parent containers with the DefaultDatabase 'exampleParentA' and 'exampleParentB' and appends a number to any duplicate clones. | ||
|
|
||
| ---------------------------------------------------------------- | ||
|
|
||
| Parent Container [ Id: 7, Name: exampleParentB staging ] 'create' command sent for new Container [ exampleB staging(clone) ] | ||
| Parent Container [ Id: 3, Name: exampleParentA on SQL 1.2.3 ] 'create' command sent for new Container [ exampleA(clone-1) ] | ||
| Parent Container [ Id: 3, Name: exampleParentA on SQL 1.2.3 ] 'create' command sent for new Container [ exampleA(clone-2) ] | ||
| Parent Container [ Id: 4, Name: exampleParentB on 4.5.6 ] 'create' command sent for new Container [ exampleB(clone) ] | ||
|
|
||
| .NOTES | ||
| Does not clone any parent containers with 'BETA' in the name, unless specified with '-AllowBeta' switch. | ||
| Also removes parent name suffixes like 'on Database v1.2.3' before appending the ContainerName_Append string. | ||
| Appends a number to the cloned container name if multiple matching clones are created with same parent at once, or for any matching clones that already exist when using the '-Duplicate' switch. | ||
|
|
||
| .LINK | ||
| https://datto-dbpool-refresh.kentsapp.com/Copy-DBPoolParentContainer/ | ||
| #> | ||
| [CmdletBinding(DefaultParameterSetName = 'byId')] | ||
| [Alias('Clone-DBPoolParentContainer')] | ||
| param ( | ||
| [Parameter(Mandatory = $true, ValueFromPipeline, ValueFromPipelineByPropertyName, ParameterSetName = 'byId')] | ||
| [int[]]$Id, | ||
|
|
||
| [Parameter(Mandatory = $true, ParameterSetName = 'byDefaultDatabase')] | ||
| [string[]]$DefaultDatabase, | ||
|
|
||
| [Parameter()] | ||
| [string]$ContainerName_Append = 'clone', | ||
|
|
||
| [switch]$Duplicate, | ||
|
|
||
| [Parameter(DontShow = $true)] | ||
| [switch]$AllowBeta | ||
| ) | ||
|
|
||
| begin { | ||
|
|
||
| # Pass the InformationAction parameter if bound, default to 'Continue' | ||
| if ($PSBoundParameters.ContainsKey('InformationAction')) { $InformationPreference = $PSBoundParameters['InformationAction'] } else { $InformationPreference = 'Continue' } | ||
|
|
||
| if (-not $DBPool_ApiKey) { | ||
| Write-Warning "DBPool_ApiKey is not set. Please run 'Get-RefreshDBPoolAPIKey' to set the API key." | ||
| return | ||
| } | ||
|
|
||
| } | ||
|
|
||
| process { | ||
|
|
||
| # Retrieve current parent containers | ||
| $parentContainer = Get-DBPoolContainer -ParentContainer | ||
|
|
||
| switch ($PSCmdlet.ParameterSetName) { | ||
| 'byId' { | ||
| $myContainers = Get-DBPoolContainer | ||
| $filteredParentContainer = if ($AllowBeta) { | ||
| $parentContainer | Where-Object { $_.Id -in $Id } | ||
| } else { | ||
| $parentContainer | Where-Object { $_.Id -in $Id -and $_.Name -notmatch 'BETA' } | ||
| } | ||
| } | ||
|
|
||
| 'byDefaultDatabase' { | ||
| $myContainers = Get-DBPoolContainer -DefaultDatabase $DefaultDatabase -WarningAction SilentlyContinue | ||
| $filteredParentContainer = if ($AllowBeta) { | ||
| $parentContainer | Where-Object { $_.defaultDatabase -in $DefaultDatabase } | ||
| } else { | ||
| $parentContainer | Where-Object { $_.defaultDatabase -in $DefaultDatabase -and $_.Name -notmatch 'BETA' } | ||
| } | ||
| } | ||
| } | ||
| if ($filteredParentContainer.Count -eq 0) { | ||
| Write-Error 'No matching parent container(s) found to clone.' | ||
| return | ||
| } | ||
|
|
||
| # Create clones of the matched containers | ||
| $maxRunspaces = [Math]::Min($filteredParentContainer.Count, ([Environment]::ProcessorCount * 2)) | ||
| $runspacePool = [runspacefactory]::CreateRunspacePool(1, $maxRunspaces) | ||
| $runspacePool.Open() | ||
| $runspaces = New-Object System.Collections.ArrayList | ||
| foreach ($parent in $filteredParentContainer) { | ||
| # Extract the first part of the container name before 'on'; i.e. 'Parent Container Name on Database v1.2.3' | ||
| $baseContainerName = $parent.Name -split ' on ' | Select-Object -First 1 | ||
|
|
||
| Write-Verbose "Checking DBPool for any container matching Parent: $($parent | Select-Object -Property 'id','name','defaultDatabase')" | ||
| # Check if similar container already exists based on the parent container | ||
| $existingContainerClone = $myContainers | Where-Object { $_.parent -match $parent } | ||
|
|
||
| if ($existingContainerClone -and -not $Duplicate) { | ||
| $existingContainerCloneInfo = ($existingContainerClone | ForEach-Object { "Id: $($_.Id), Name: $($_.Name)" }) -join '; ' | ||
| Write-Warning "Container with parent [ $($parent | Select-Object -Property 'id','name','defaultDatabase') ] already exists for container(s) [ $existingContainerCloneInfo ] - Skipping clone." | ||
| Write-Debug "Use '-Duplicate' switch to force clone of existing containers." | ||
| continue | ||
| } | ||
|
|
||
| # Determine the starting index for the clone name | ||
| $existingCloneCount = ($existingContainerClone | Measure-Object).Count + 1 | ||
|
|
||
| # Clone the parent container as many times as it appears in the Id or DefaultDatabase parameter | ||
| $cloneCount = switch ($PSCmdlet.ParameterSetName) { | ||
| 'byId' { ($Id | Where-Object { $_ -eq $parent.Id }).Count } | ||
| 'byDefaultDatabase' { ($DefaultDatabase | Where-Object { $_ -eq $parent.defaultDatabase }).Count } | ||
| } | ||
|
|
||
| for ($i = 0; $i -lt $cloneCount; $i++) { | ||
| try { | ||
| if ($existingCloneCount + $i -eq 1 -and $cloneCount -eq 1) { | ||
| $newContainerName = "$baseContainerName($ContainerName_Append)" | ||
| } else { | ||
| $newContainerName = "$baseContainerName($ContainerName_Append-$($existingCloneCount + $i))" | ||
| } | ||
| $runspace = [powershell]::Create().AddScript({ | ||
| param ($containerName, $parentId, $apiKey) | ||
| try { | ||
| Import-Module 'Datto.DBPool.API' | ||
| Add-DBPoolApiKey -apiKey $apiKey | ||
| New-DBPoolContainer -ParentId $parentId -ContainerName $containerName -Force | ||
| } catch { | ||
| Write-Error "Error in runspace execution: $_" | ||
| } | ||
| }).AddArgument($newContainerName).AddArgument($parent.Id).AddArgument($DBPool_ApiKey) | ||
|
|
||
| $runspace.RunspacePool = $runspacePool | ||
| $runspaces.Add(@{Runspace = $runspace; Handle = $runspace.BeginInvoke(); ContainerName = $newContainerName }) | Out-Null | ||
| Write-Information "Parent Container [ Id: $($parent.Id), Name: $($parent.Name) ] 'create' command sent for new Container [ $newContainerName ]" | ||
| } catch { | ||
| Write-Error "Error sending 'create' command for new Container [ $newContainerName ]: $_" | ||
| } | ||
| } | ||
| Start-Sleep -Milliseconds 500 | ||
|
|
||
| } | ||
|
|
||
| while ($runspaces.Count -gt 0) { | ||
| for ($i = 0; $i -lt $runspaces.Count; $i++) { | ||
| $runspace = $runspaces[$i].Runspace | ||
| $handle = $runspaces[$i].Handle | ||
| if ($handle.IsCompleted) { | ||
| Write-Information "Success: Created DBPool container [ $($runspaces[$i].ContainerName) ]" | ||
| $runspace.EndInvoke($handle) | ||
| $runspace.Dispose() | ||
| $runspaces.RemoveAt($i) | ||
| $i-- | ||
| } | ||
| } | ||
| Start-Sleep -Milliseconds 500 | ||
| } | ||
|
|
||
| } | ||
|
|
||
| end { | ||
|
|
||
| if ($filteredParentContainer.Count -ne 0) { | ||
| $runspacePool.Close() | ||
| $runspacePool.Dispose() | ||
| } | ||
|
|
||
| } | ||
|
|
||
| } | ||
|
|
||
| #EndRegion | ||
| #Region | ||
|
|
||
| function Sync-DBPoolContainer { | ||
| <# | ||
| .SYNOPSIS | ||
| Refreshes the specified DBPool container(s) using the DBPool API. By default, this function will refresh all containers if no IDs are provided. | ||
|
|
||
| .DESCRIPTION | ||
| This function refreshes the specified DBPool container(s) using the DBPool API. By default, this function will refresh all containers if no IDs are provided. | ||
|
|
||
| .PARAMETER Id | ||
| The ID(s) of the container(s) to refresh. If no IDs are provided, all containers will be refreshed. | ||
|
|
||
| .PARAMETER TimeoutSeconds | ||
| The maximum time in seconds to wait for the container(s) to refresh. The default value is 3600 seconds (1 hour). | ||
|
|
||
| .PARAMETER Force | ||
| If specified, the function will not prompt for confirmation before refreshing the container(s). | ||
|
|
||
| .INPUTS | ||
| [int] - Array of ID(s) of the container(s) to perform the refresh action on. | ||
|
|
||
| .OUTPUTS | ||
| [void] - No output is returned. | ||
|
|
||
| .EXAMPLE | ||
| Sync-DBPoolContainer | ||
|
|
||
| Refreshes all DBPool containers. | ||
|
|
||
| .EXAMPLE | ||
| Sync-DBPoolContainer -Id 1234 | ||
|
|
||
| Refreshes the DBPool container with the ID 1234. | ||
|
|
||
| .EXAMPLE | ||
| Sync-DBPoolContainer -Id 1234, 5678 | ||
|
|
||
| Refreshes the DBPool containers with the IDs 1234 and 5678. | ||
|
|
||
| .EXAMPLE | ||
| Sync-DBPoolContainer -Id $(Get-DBPoolContainer -DefaultDatabase "Database_Name").Id | ||
|
|
||
| Refreshes all DBPool containers matching the specified database name. | ||
|
|
||
| .EXAMPLE | ||
| Sync-DBPoolContainer -Id $(Get-DBPoolContainer -NotLike -Name "*Container_Name*").Id -Force | ||
|
|
||
| Refreshes all DBPool containers not matching the specified container name. | ||
|
|
||
| .NOTES | ||
| N/A | ||
|
|
||
| .LINK | ||
| https://datto-dbpool-refresh.kentsapp.com/Sync-DBPoolContainer/ | ||
| #> | ||
| [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')] | ||
| [Alias('Refresh-DBPoolContainer', 'Refresh-DBPool', 'Sync-DBPool')] | ||
| param ( | ||
| [Parameter(Mandatory = $false, Position = 0, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] | ||
| [AllowNull()] | ||
| [AllowEmptyCollection()] | ||
| [Alias('ContainerId')] | ||
| [int[]]$Id = $RefreshDBPool_Container_Ids, | ||
|
|
||
| [Parameter(DontShow = $true)] | ||
| [ValidateRange(0, [int]::MaxValue)] | ||
| [int]$TimeoutSeconds = $RefreshDBPool_TimeoutSeconds, | ||
|
|
||
| [switch]$Force | ||
| ) | ||
|
|
||
| begin { | ||
|
|
||
| if (!(Get-Variable -Name 'DBPool_ApiKey' -Scope Global -ErrorAction SilentlyContinue)) { | ||
| try { | ||
| Get-RefreshDBPoolApiKey -Force -Verbose:$false -ErrorAction Stop | ||
| } | ||
| catch { | ||
| throw $_ | ||
| } | ||
| } | ||
|
|
||
| if (-not $PSBoundParameters['TimeoutSeconds']) { | ||
| $TimeoutSeconds = 3600 | ||
| } | ||
| } | ||
|
|
||
| process { | ||
|
|
||
| if (!$Id) { | ||
| Write-Warning 'No container IDs provided. Retrieving all container IDs.' | ||
| try { | ||
| $Id = Get-DBPoolContainer -ListContainer -ErrorAction Stop | Select-Object -ExpandProperty Id | ||
| } catch { | ||
| Write-Error $_ | ||
| } | ||
| } | ||
|
|
||
| $IdsToRefresh = [System.Collections.ArrayList]::new() | ||
| foreach ($n in $Id) { | ||
| # Try to get the container name for the ID to output when using Verbose | ||
| if ( ($PSCmdlet.MyInvocation.BoundParameters['Verbose'].IsPresent) -and ( (-not $Force) -and ($ConfirmPreference -in @('Low', 'Medium')) ) ) { | ||
| try { | ||
| $containerName = (Get-DBPoolContainer -Id $n -WarningAction SilentlyContinue -ErrorAction Stop -Verbose:$false).name | ||
| } catch { | ||
| #Write-Warning "Failed to get the container name for ID $n. $_" | ||
| $containerName = '## FailedToGetContainerName ##' | ||
| } | ||
| Write-Verbose "Confirm action [ Refresh ] for Container [ ID: $n, Name: $containerName ]" | ||
| } | ||
| if ($Force -or $PSCmdlet.ShouldProcess("Container [ ID: $n ]", '[ Refresh ]') ) { | ||
| $IdsToRefresh.Add($n) | Out-Null | ||
| } | ||
| } | ||
|
|
||
| if ($IdsToRefresh.Count -gt 0) { | ||
| try { | ||
| Invoke-DBPoolContainerAction -Action refresh -Id $IdsToRefresh -Force -Verbose:$VerbosePreference -ThrottleLimit $IdsToRefresh.Count -TimeoutSeconds $TimeoutSeconds -ErrorAction Continue | ||
| } | ||
| catch { | ||
| Write-Error $_ | ||
| } | ||
| } elseif ($IdsToRefresh.Count -eq 0) { | ||
| Write-Warning 'No containers refreshed.' | ||
| } | ||
|
|
||
| } | ||
|
|
||
| end {} | ||
|
|
||
| } | ||
|
|
||
| #EndRegion |
Copilot
AI
Jan 30, 2026
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.
Build artifacts (compiled module files) are being committed to the repository. The Datto.DBPool.Refresh/0.2.1/ directory appears to be a build output artifact and should not be included in the repository. Consider adding build output directories to .gitignore to prevent committing compiled modules. Typically, only source files should be committed, and build artifacts should be generated during the build process.
.github/workflows/ci.yml
Outdated
|
|
||
| - uses: microsoft/psscriptanalyzer-action@v1.1 | ||
| with: | ||
| path: .\ |
Copilot
AI
Jan 30, 2026
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 path separator is incorrect for Linux runners. On line 33, Windows-style backslash .\ is used, but this workflow runs on ubuntu-latest. This should be a forward slash ./ or just . to work correctly on Linux.
| path: .\ | |
| path: . |
src/Initialize-RefreshDBPool.ps1
Outdated
| $pwshInstallerUrl = 'https://github.com/PowerShell/PowerShell/releases/download/v7.5.0/PowerShell-7.5.0-win-x64.msi' | ||
| $pwshInstallerHash = '6B988B7E236A8E1CF1166D3BE289D3A20AA344499153BDAADD2F9FEDFFC6EDA9' | ||
| $pwshInstallerUrl = 'https://github.com/PowerShell/PowerShell/releases/download/v7.5.4/PowerShell-7.5.4-win-x64.msi' | ||
| $pwshInstallerHash = 'sha256:84a39d39f113f884333686c4df70bc6c517f5b5d3982d88b4a0139f10ebb3fcb' |
Copilot
AI
Jan 30, 2026
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 hash format has changed from a plain hash string to include a 'sha256:' prefix. This change may cause issues with hash validation. The previous version used a plain uppercase hash, while the new version includes 'sha256:' prefix with lowercase hex. Verify that the hash validation logic in the download code can handle this format change, or update the hash to match the expected format without the 'sha256:' prefix.
| $pwshInstallerHash = 'sha256:84a39d39f113f884333686c4df70bc6c517f5b5d3982d88b4a0139f10ebb3fcb' | |
| $pwshInstallerHash = '84A39D39F113F884333686C4DF70BC6C517F5B5D3982D88B4A0139F10EBB3FCB' |
| - name: Create release archive | ||
| shell: pwsh | ||
| run: | | ||
| $modulePath = "out/Datto.DBPool.Refresh/${{ steps.version.outputs.version }}" |
Copilot
AI
Jan 30, 2026
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 module path may be incorrect. The psakeFile.ps1 sets the output directory to the project root ($PSBPreference.Build.OutDir = "$projectRoot"), which would typically create the module at Datto.DBPool.Refresh/$version in the root directory. However, this workflow is looking for the built module at out/Datto.DBPool.Refresh/$version. Verify that PowerShellBuild's actual output path matches the path used here, or update the path to match the actual build output location.
| $modulePath = "out/Datto.DBPool.Refresh/${{ steps.version.outputs.version }}" | |
| $modulePath = "Datto.DBPool.Refresh/${{ steps.version.outputs.version }}" |
psakeFile.ps1
Outdated
| Task PublishDocs -Depends Build { | ||
| $env:GITHUB_TOKEN = $env:GITHUB_TOKEN ?? '' | ||
| $env:GITHUB_REPOSITORY = $env:GITHUB_REPOSITORY ?? '' | ||
| $env:GITHUB_ACTOR = $env:GITHUB_ACTOR ?? '' | ||
|
|
||
| Exec { | ||
| docker run -v "$($psake.build_script_dir)`:/docs" ` | ||
| -e 'CI=true' ` | ||
| -e "GITHUB_TOKEN=$env:GITHUB_TOKEN" ` | ||
| -e "GITHUB_REPOSITORY=$env:GITHUB_REPOSITORY" ` | ||
| -e "GITHUB_ACTOR=$env:GITHUB_ACTOR" ` | ||
| --entrypoint 'sh' squidfunk/mkdocs-material:9 -c 'pip install -r requirements.txt && mkdocs gh-deploy --force' |
Copilot
AI
Jan 30, 2026
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 PublishDocs task runs a third-party Docker image squidfunk/mkdocs-material:9 via docker run while injecting GitHub secrets (GITHUB_TOKEN, GITHUB_REPOSITORY, GITHUB_ACTOR) as environment variables, and the image is only pinned by a mutable tag. If that upstream image or its registry is compromised, attacker-controlled code inside the container can exfiltrate these secrets and gain control over the repository or CI context. Prefer using an internally built or officially maintained image pinned to an immutable digest, and avoid passing GitHub tokens or other secrets into untrusted containers unless strictly necessary and tightly scoped.
Removed Build.Exclude and AppendInitialization task additions. Bootstrap helper scripts (Initialize-RefreshDBPool.ps1, Invoke-RefreshDBPoolInstall.ps1) are copied via CopyDirectories configuration and remain as standalone tools. Build process only compiles Public and Private directory functions.
Address all 6 Copilot review comments for PR #7: Workflow Fixes: - Fix ci.yml path separator for Linux compatibility (. instead of .\\) - Correct module output path in build-and-release.yml (removed 'out/' prefix) - Update publish-psgallery.yml workflow reference (publish-docs.yml → Build_DocsSite.yml) - Pin Docker image to immutable digest in psakeFile.ps1 for security * squidfunk/mkdocs-material:9 → squidfunk/mkdocs-material:9@sha256:3bba0a99bc6e635bb8e53f379d32ab9cecb554adee9cc8f59a347f93ecf82f3b Documentation: - Fix README.md typo: dependancies → dependencies Module Updates: - Normalize PowerShell installer hash format in Initialize-RefreshDBPool.ps1 * Remove 'sha256:' prefix, convert to uppercase for consistency Workflow Consolidation: - Remove deprecated workflows (Build_Tests.yaml, PSScriptAnalyzer.yml, codeql.yml) - Functionality consolidated into comprehensive ci.yml workflow These changes implement PSWrapper workflow patterns with enhanced: - Version validation and duplicate checking - CHANGELOG extraction and release notes generation - Quality gates and comprehensive testing - Security improvements with immutable Docker digests Co-authored-by: GitHub Copilot <noreply@github.com>
|
This pull request sets up GitHub code scanning for this repository. Once the scans have completed and the checks have passed, the analysis results for this pull request branch will appear on this overview. Once you merge this pull request, the 'Security' tab will show more code scanning analysis results (for example, for the default branch). Depending on your configuration and choice of analysis tool, future pull requests will be annotated with code scanning analysis results. For more information about GitHub code scanning, check out the documentation. |
Code scanning alert #2: Remove hardcoded credential in SecretStore initialization Replace hardcoded 'HardCodedPassword' with randomly generated 32-character password. This temporary password is only used for initial SecretStore configuration and is immediately replaced with no-authentication mode. The Microsoft.PowerShell.SecretStore module requires a password for initial configuration even when switching to authentication-free mode, so a temporary password is necessary but should not be hardcoded. Resolves: https://github.com/cksapp/Datto-DBPool_Refresh/security/code-scanning/2
… password usage Add SuppressMessageAttribute to ConvertTo-SecureString usage in Initialize-RefreshDBPool.ps1. The randomly generated temporary password is safe as it: - Is never stored or logged - Is only used to satisfy SecretStore's initial setup requirement - Is immediately discarded after switching to no authentication Resolves PSScriptAnalyzer code scanning alert about plaintext secure strings.
… PSWrapper Apply the same SuppressMessageAttribute pattern used in Datto-DBPool_PSWrapper's Get-DBPoolUser.ps1 function, which includes: - Justification parameter with clear explanation - Inline comment after the attribute The temporary password in Initialize-DattoSecretStoreVault is safe because: - It's randomly generated (32 characters) - It's never stored or logged - It's only used to satisfy SecretStore's initial setup requirement - It's immediately replaced with no authentication
Overview
Mirrors Datto-DBPool_PSWrapper v0.2.3 CI/CD implementation with comprehensive automation infrastructure.
Changes Implemented
New Workflows
Configuration
Updated Workflows
Build Process Enhancements
Version Updates
Documentation
Benefits