Skip to content

Instantly share code, notes, and snippets.

@vgribok
Last active June 21, 2023 18:40
Show Gist options
  • Save vgribok/9e68de07e03f0266a573aa3159af95de to your computer and use it in GitHub Desktop.
Save vgribok/9e68de07e03f0266a573aa3159af95de to your computer and use it in GitHub Desktop.
Allows copying RDS parameter groups between AWS Regions and Accounts. Enables version-controlling values of RDS param groups via JSON files. Makes possible RDS cluster provisioning automation for custom parameter groups.
<#
Allows exporting RDS Parameter Group parameters as a file and importing it in another AWS Account or a region.
Inspired by https://gist.github.com/phill-tornroth/f0ef50f9402c7c94cbafd8c94bbec9c9.
PowerShell is a *cross-platform* shell compatible with Linux, MacOS and Windows.
PowerShell can be installed in seconds: https://learn.microsoft.com/en-us/powershell/scripting/install/installing-powershell
Usage:
To *export* a cluster parameter group named "custom-aurora-postgresql13-babelfish-compat-1", run:
Get-RdsClusterParamGroupDetails custom-aurora-postgresql13-babelfish-compat-1 | ConvertTo-Json -Depth 3 | Out-File RDS-BBF-params.json
or (the easier way):
Get-RdsClusterParamGroupDetailsJson custom-aurora-postgresql13-babelfish-compat-1 -FilePath RDS-BBF-params.json
To create a parameter group named apg-bbf-params from JSON file:
Import-RdsClusterParamGroupDetails RDS-BBF-params.json -ProfileName default -Region us-west-2 `
-Name "apg-bbf-params"
Note: ProfileName and Region parameters are optional.
#>
Write-Host "Adding all AWS PowerShell commands. It may take a few seconds.."
Import-Module awspowershell.netcore
function Get-RdsClusterParamGroupDetailsJson {
param (
[Parameter(mandatory=$true, HelpMessage='Use Get-RDSDBClusterParameterGroup cmdlet to list all cluster parameter groups')]
[string] $Name,
[Parameter(mandatory=$false)] [string] $Region,
[Parameter(mandatory=$false)] [string] $ProfileName,
[Parameter(mandatory=$false)] [string] $FilePath
)
$result = Get-RdsClusterParamGroupDetails -Name $Name `
-Region $Region `
-ProfileName $ProfileName
if(-not $result) {
return
}
if(-not $FilePath) { $FilePath = "$Name--param-group-details.json" }
$result | ConvertTo-Json -Depth 9 | Out-File $FilePath
}
function Get-RdsClusterParamGroupDetails {
param (
[Parameter(mandatory=$true, HelpMessage='Use Get-RDSDBClusterParameterGroup cmdlet to list all cluster parameter groups')] [string] $Name,
[Parameter(mandatory=$false)] [string] $Region,
[Parameter(mandatory=$false)] [string] $ProfileName
)
$prevRegion = Get-DefaultAWSRegion
try{
if($Region) { Set-DefaultAWSRegion $Region}
if($ProfileName -eq "") { $ProfileName = $null }
$sourceParamGroup = Get-RDSDBClusterParameterGroup $Name `
-ProfileName $ProfileName
$parameters = Get-RDSDBClusterParameter $Name `
-ProfileName $ProfileName `
| Where { $_.ParameterValue -and $_.IsModifiable }
$result = [PSCustomObject]@{
ParameterGroupName = $sourceParamGroup.DBClusterParameterGroupName
Description = $sourceParamGroup.Description
GroupFamily = $sourceParamGroup.DBParameterGroupFamily
Parameters = $parameters
}
return $result
}
finally{
if($prevRegion) { Set-DefaultAWSRegion $prevRegion} else {Clear-DefaultAWSRegion}
}
}
# Test command for export debugging puposes:
# $exportedData = Get-RdsClusterParamGroupDetails custom-aurora-postgresql13-babelfish-compat-1 | ConvertTo-Json -Depth 3
# $exportedData | Out-File custom-aurora-postgresql13-babelfish-compat-1-rds-param-group-details.json
# Get-RdsClusterParamGroupDetailsJson custom-aurora-postgresql13-babelfish-compat-1 -FilePath RDS-BBF-params.json
function Import-RdsClusterParamGroupDetails {
param (
[Parameter(mandatory=$true, ValueFromPipeline = $true,
HelpMessage='Use Get-RdsClusterParamGroupDetails cmdlet to export details of the cluster parameter groups')
] $ImportedParamGroupDetails,
[Parameter(mandatory=$false)] [string] $Name,
[Parameter(mandatory=$false)] [string] $Region,
[Parameter(mandatory=$false)] [string] $ProfileName
)
# Write-Debug "Imported data:\n$ImportedParamGroupDetails"
# The ImportedParamGroupDetails can either be piped-in, already parsed Json,
# or a path to th eJSON file.
if($ImportedParamGroupDetails -is [string]) {
$ImportedParamGroupDetails = Get-Content $ImportedParamGroupDetails | ConvertFrom-Json -Depth 9
}
$prevRegion = Get-DefaultAWSRegion
try{
if($Region) { Set-DefaultAWSRegion $Region}
if(-not $Name) {$Name = $ImportedParamGroupDetails.ParameterGroupName}
New-RDSDBClusterParameterGroup `
-DBClusterParameterGroupName $Name `
-DBParameterGroupFamily $ImportedParamGroupDetails.GroupFamily `
-Description GroupFamily.Description `
-ProfileName $ProfileName
$importedParameters = _FilterOutIdenticalValues $ImportedParamGroupDetails.Parameters $Name $ProfileName
$paramCollectionLength = $importedParameters.Count
Write-Information "Will update $paramCollectionLength parameters"
for($i=0 ; $i -lt $paramCollectionLength ; $i += 20) {
$chunkSize = $paramCollectionLength - $i
if($chunkSize -gt 20) { $chunkSize = 20 }
# Slice out no more than 20 parameters for the batch update
$params = $importedParameters[$i..($i+$chunkSize-1)] ?? $importedParameters
foreach ($param in $params) {
# ApplyMethod cannot be converted autpmatically
$param.ApplyMethod = [Amazon.RDS.ApplyMethod]::New($param.ApplyMethod.Value)
}
# Update the batch of values
if($params -and ($params.Count -gt 0)){
Edit-RDSDBClusterParameterGroup -DBClusterParameterGroupName $Name -ProfileName $ProfileName `
-Parameter $params
}
## Debug version - updates parameters one by one instead of in chunks of 20, to see which ones are failing
# foreach($p in $params) {
# try{
# Edit-RDSDBClusterParameterGroup -DBClusterParameterGroupName $Name -ProfileName $ProfileName `
# -Parameter $p
# }
# catch {
# Write-Host "==> UPDATE ERROR: Failed to modify parameter `"$($p.ParameterName)`" with value `"$($p.ParameterValue)`" due to the `"$($_))`" error, with allowed values of `"$($p.AllowedValues)`"."
# # throw
# }
# }
}
}
finally{
if($prevRegion) { Set-DefaultAWSRegion $prevRegion} else {Clear-DefaultAWSRegion}
}
}
function _FilterOutIdenticalValues($importedParameters, [string] $Name, [string] $ProfileName)
{
$stockParameters = Get-RDSDBClusterParameter $Name `
-ProfileName $ProfileName `
| Where-Object { $_.ParameterValue -and $_.IsModifiable }
$paramsToSkip = [System.Collections.ArrayList]@();
foreach($importedParam in $importedParameters) {
$sameParam = $stockParameters | Where-Object { ($_.ParameterName -eq $importedParam.ParameterName) -and ($_.ParameterValue -eq $importedParam.ParameterValue) } | Select-Object -First 1
if($sameParam) {
$paramsToSkip.Add($sameParam.ParameterName) | out-null
}
}
# Remove parameters that should be skipped
$importedParameters = $importedParameters | Where-Object { -not $paramsToSkip.Contains($_.ParameterName) }
return $importedParameters
}
#Testing import
# Get-Content RDS-BBF-params.json `
# | ConvertFrom-Json -Depth 4 `
# | Import-RdsClusterParamGroupDetails -ProfileName default -Region us-west-2
#Import-RdsClusterParamGroupDetails RDS-BBF-params.json -ProfileName default -Region us-west-2 `
# -Name "apg-bbf-params"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment