Skip to content
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

fix(scripts): correct and prettify copy kv secrets script #15

Merged
Merged
Changes from all 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
167 changes: 92 additions & 75 deletions scripts/powershell/Copy-AzKeyVaultSecret.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,23 @@
- RBAC as Key Vault Contributor for Source and Destination Vault
- Read access policy for secrets at source Key Vault and Write access policy for secrets at destination Key Vault

.PARAMETER VaultName
Specifies the name of the key vault.
.PARAMETER SourceVaultName
Specifies the name of the source key vault.

.PARAMETER TargetVaultName
Specifies the name of the target key vault.

.PARAMETER SubscriptionId
Specifies the ID of Azure Subscription.
Specifies the ID of source Azure Subscription.

.PARAMETER TargetSubscriptionId
Specifies the ID of Target Azure Subscription.
Specifies the ID of target Azure Subscription.

.PARAMETER Name
Specifies the name of the secret to copy. You can as well specify multiple Names for multiple secrets to copy.
.PARAMETER SecretName
Specifies the name of the secret to copy. You can as well specify multiple SecretNames for multiple secrets to copy.

.PARAMETER SkipSecrets
Specifies the names of the secret skip copy operation.
Specifies the names of the secrets to skip during copy operation.

.PARAMETER Force
Forces the script to run without asking for user confirmation.
Expand All @@ -33,37 +33,33 @@
.EXAMPLE
This example shows how to copy all secrets from source to target vault.
It requires manual user confirmation before executing copy operation for every secret separately:
.\Copy-AzKeyVaultSecret.ps1 -VaultName <String> -TargetVaultName <String> -Subscription $Subscription
.\Copy-AzKeyVaultSecret.ps1 -SourceVaultName <String> -TargetVaultName <String> -SubscriptionId <String>

.EXAMPLE
Similar to example above, this shows how to copy all secrets from source to target vault.
But this time script does not require user confirmation as it uses -Force argument.
.\Copy-AzKeyVaultSecret.ps1 -VaultName <String> -TargetVaultName <String> -Subscription $Subscription -Force
But this time the script does not require user confirmation as it uses -Force argument.
.\Copy-AzKeyVaultSecret.ps1 -SourceVaultName <String> -TargetVaultName <String> -SubscriptionId <String> -Force

.EXAMPLE
This example shows how to use $Name Parameter to copy a single secret:
.\Copy-AzKeyVaultSecret.ps1 -VaultName <String> -TargetVaultName <String> -Subscription $Subscription -Name <String>
This example shows how to use $SecretName Parameter to copy a single secret:
.\Copy-AzKeyVaultSecret.ps1 -SourceVaultName <String> -TargetVaultName <String> -SubscriptionId <String> -SecretName <String>

.EXAMPLE
This example shows how to use $Name Parameter to copy multiple secrets:
.\Copy-AzKeyVaultSecret.ps1 -VaultName <String> -TargetVaultName <String> -Subscription $Subscription -Name <String>, <String>, <String>
This example shows how to use $SecretName Parameter to copy multiple secrets:
.\Copy-AzKeyVaultSecret.ps1 -SourceVaultName <String> -TargetVaultName <String> -SubscriptionId <String> -SecretName <String>, <String>, <String>

.EXAMPLE
This example shows how to copy all secrets from source to target vault without any confirmation:
.\Copy-AzKeyVaultSecret.ps1 -VaultName <String> -TargetVaultName <String> -Subscription $Subscription -Force
This example shows how to copy all secrets without confirmation when vaults reside in different Azure Subscriptions:
.\Copy-AzKeyVaultSecret.ps1 -SourceVaultName <String> -TargetVaultName <String> -SubscriptionId <String> -TargetSubscriptionId <String> -Force

.EXAMPLE
This example shows how to copy all secrets without confirmation when vaults resides in different Azure Subscriptions:
.\Copy-AzKeyVaultSecret.ps1 -VaultName <String> -TargetVaultName <String> -SubscriptionId <String> -TargetSubscriptionId <String> -Force

.EXAMPLE
This example shows same example as previous + use of SkipSecrets Parameter. SkipSecrets support multiple values:
.\Copy-AzKeyVaultSecret.ps1 -VaultName <String> -TargetVaultName <String> -SubscriptionId <String> -TargetSubscriptionId <String> -Force -SkipSecrets <String>, <String>, <String>
This example shows the same example as previous + use of SkipSecrets Parameter. SkipSecrets supports multiple values:
.\Copy-AzKeyVaultSecret.ps1 -SourceVaultName <String> -TargetVaultName <String> -SubscriptionId <String> -TargetSubscriptionId <String> -Force -SkipSecrets <String>, <String>, <String>
#>

param (
[Parameter(Mandatory = $true)]
[string]$VaultName,
[string]$SourceVaultName,

[Parameter(Mandatory = $true)]
[string]$TargetVaultName,
Expand All @@ -75,7 +71,7 @@ param (
[string]$TargetSubscriptionId,

[Parameter(Mandatory = $false)]
[string[]]$Name,
[string[]]$SecretName,

[Parameter(Mandatory = $false)]
[string[]]$SkipSecrets,
Expand All @@ -89,22 +85,25 @@ param (

$InformationPreference = 'Continue'

# Log into Azure using a managed identity, for use in an Azure Automation Runbook.
function aaLogin {
Disable-AzContextAutosave -Scope Process
$AzureContext = (Connect-AzAccount -Identity).context
$AzureContext = Set-AzContext -SubscriptionId $subscriptionId -DefaultProfile $AzureContext
Set-AzContext -SubscriptionId $SubscriptionId -DefaultProfile $AzureContext
}
function Set-AzSubscription {

# Set and verify Azure Subscription Context
function Select-SubscriptionContext {

param (
[Parameter(Mandatory = $true)]
[string]$SubscriptionId
)

# Set Context to correct Subscription.
Set-AzContext -Subscription $SubscriptionId
Set-AzContext -SubscriptionId $SubscriptionId

# Check Subscription Context
# Verify Subscription Context
$subCheck = (Get-AzContext).Subscription.Id
try {
if ($subCheck -ne $SubscriptionId) {
Expand All @@ -113,99 +112,117 @@ function Set-AzSubscription {
}
}
catch {
Write-Error "Error: $($_.Exception)"
Write-Error "Error: $($_.Exception.Message)"
}
}

# Temporarily disable Key Vault firewall to allow script to read secrets in Source Vault and write secrets in Target Vault.
function Disable-KeyVaultFirewall {

param (
[Parameter(Mandatory = $true)]
[string]$VaultName
)

$vault = Get-AzKeyVault -VaultName $VaultName -ErrorAction SilentlyContinue
$firewallDefaultAction = $vault.NetworkAcls.DefaultAction
$firewallEnabled = $firewallDefaultAction -eq 'Deny'
if ($firewallEnabled) {
$Vault = Get-AzKeyVault -VaultName $VaultName -ErrorAction SilentlyContinue
$FirewallDefaultAction = $Vault.NetworkAcls.DefaultAction
$FirewallEnabled = $FirewallDefaultAction -eq 'Deny'

if ($FirewallEnabled) {
$null = Update-AzKeyVaultNetworkRuleSet -VaultName $VaultName -DefaultAction 'Allow'
}

return $FirewallEnabled
}

try {
if ($Runbook) {
aaLogin
}
# Set AzContext and make sure FireWall is set to "Allowed" to be able to access secrets
Set-AzSubscription -SubscriptionId $SubscriptionId
Disable-KeyVaultFirewall -VaultName $VaultName
Disable-KeyVaultFirewall -VaultName $TargetVaultName

#Check if Source and Target Vaults exists
$srcVault = Get-AzKeyVault -VaultName $VaultName -ErrorAction SilentlyContinue
$dstVault = Get-AzKeyVault -VaultName $TargetVaultName -ErrorAction SilentlyContinue
# Set Context and make sure FireWall is set to "Allowed" to be able to access secrets
Select-SubscriptionContext -SubscriptionId $SubscriptionId
$SourceVaultFirewall = Disable-KeyVaultFirewall -VaultName $SourceVaultName
$TargetVaultFirewall = Disable-KeyVaultFirewall -VaultName $TargetVaultName

# Check if Source and Target Vaults exist
$SourceVault = Get-AzKeyVault -VaultName $SourceVaultName -ErrorAction SilentlyContinue
$TargetVault = Get-AzKeyVault -VaultName $TargetVaultName -ErrorAction SilentlyContinue

# Skip Secrets form Copy operation if specified in $SkipSecrets Parameter
if ($SkipSecrets) {
if ($SourceVault -and $TargetVault) {
# Skip Secrets from Copy operation if specified in $SkipSecrets Parameter
$exclusionList = @()
$exclusionList += $SkipSecrets
}
if ($srcVault -and $dstVault) {
# Run this block if no input in parameter $Name specified
if (!$Name) {
if ($SkipSecrets) {
$exclusionList += $SkipSecrets
}

# Run this block if no input in parameter $SecretName specified
if (!$SecretName) {
if ($SkipSecrets) {
$Name = (Get-AzKeyVaultSecret -VaultName $VaultName).Name | Where-Object { -not $exclusionList.Contains("$($_)") } | Sort-Object
$SecretName = (Get-AzKeyVaultSecret -VaultName $SourceVaultName).Name | Where-Object { -not $exclusionList.Contains($_) } | Sort-Object
}

else {
$Name = (Get-AzKeyVaultSecret -VaultName $VaultName).Name | Sort-Object
$SecretName = (Get-AzKeyVaultSecret -VaultName $SourceVaultName).Name | Sort-Object
}
}
foreach ($n in $Name) {
# Get key vault secret
$srcSecretValue = Get-AzKeyVaultSecret -VaultName $VaultName -Name $n -AsPlainText -ErrorAction SilentlyContinue
$srcSecret = Get-AzKeyVaultSecret -VaultName $VaultName -Name $n -ErrorAction SilentlyContinue

# Set Destination Subscription Context if available
foreach ($n in $SecretName) {
# Get secret from source Key Vault
$SourceSecretValue = Get-AzKeyVaultSecret -VaultName $SourceVaultName -Name $n -AsPlainText -ErrorAction SilentlyContinue
$SourceSecret = Get-AzKeyVaultSecret -VaultName $SourceVaultName -Name $n -ErrorAction SilentlyContinue

# Set Target Subscription Context if available
if ($TargetSubscriptionId) {
$null = Set-AzContext -SubscriptionId $TargetSubscriptionId
}
# Compare Source and Destination Secret Values
<# Only copy if value of secret and existing destination do not match.
We need to convert secrets to plain text to be able to compare secret values in different keyvaults.
This is needed when copying/updating/backing_up secrets with a powershell script.
Default action of PowerShell command "Set-AzKeyVaultSecret" is creating new versions even when secrets have same SecretValue.
This is why we are comparing secrets, to be able to prevent new sercret versions with exact same secret values.#>
$dstSecretValue = Get-AzKeyVaultSecret -VaultName $TargetVaultName -Name $n -AsPlainText -ErrorAction SilentlyContinue
if ($srcSecretValue -ne $dstSecretValue) {
$SecretValue = $srcSecretValue | ConvertTo-SecureString -AsPlainText -Force

# Get target secret from target Key Vault as plaintext for comparison
$TargetSecretValue = Get-AzKeyVaultSecret -VaultName $TargetVaultName -Name $n -AsPlainText -ErrorAction SilentlyContinue

# Compare Source and Target Secret Values
if ($SourceSecretValue -ne $TargetSecretValue) {
$SecretValue = $SourceSecretValue | ConvertTo-SecureString -AsPlainText -Force
$Copy = Set-AzKeyVaultSecret `
-VaultName $TargetVaultName `
-SecretValue $SecretValue `
-Name $srcSecret.Name `
-Expires $srcSecret.Expires `
-ContentType $srcSecret.ContentType `
-Tag $srcSecret.Tags `
-Name $SourceSecret.Name `
-Expires $SourceSecret.Expires `
-ContentType $SourceSecret.ContentType `
-Tag $SourceSecret.Tags `
-Confirm:(!$Force)

Write-Output "Successfully copied secret name '$($Copy.Name)' with id '$($Copy.Id)'"
}

else {
Write-Output "Secret Name '$($srcSecret.Name)' with id '$($srcSecret.id)'already existing in destination vault"
Write-Output "Secret Name '$($SourceSecret.Name)' with id '$($SourceSecret.Id)' already exists in destination vault"
}
}
}
}

catch {
Write-Error "Error: $($_.Exception)"
Write-Error "Error: $($_.Exception.Message)"
}

finally {
# Void secrets
$srcSecretValue = ""
$dstSecretValue = ""
$SourceSecretValue = ""
$TargetSecretValue = ""
$SecretValue = ""

# Switch Source Vault Firewall back to Deny if default action was 'Deny'
if ($srcFirewallEnabled) {
$null = Update-AzKeyVaultNetworkRuleSet -VaultName $VaultName -DefaultAction 'Deny'
if ($SourceVaultFirewall) {
$null = Update-AzKeyVaultNetworkRuleSet -VaultName $SourceVaultName -DefaultAction 'Deny'

Write-Output "Firewall for Source Vault is set to $SourceVaultFirewall."
}
# Switch Destination Vault Firewall back to Deny if default action was 'Deny'
if ($dstFirewallEnabled) {

# Switch Target Vault Firewall back to Deny if default action was 'Deny'
if ($TargetVaultFirewall) {
$null = Update-AzKeyVaultNetworkRuleSet -VaultName $TargetVaultName -DefaultAction 'Deny'

Write-Output "Firewall for Target Vault is set to $SourceVaultFirewall."
}
}
Loading