我正在编写一个脚本,它从 Azure Key Vault 获取机密的详细信息,并将机密重新创建到不同租户的另一个 Key Vault 中。我使用 Windows 窗体提供 GUI 来选择所需的机密。 该脚本在 Windows 11 中运行良好,但在 Windows 10 中尝试执行时,脚本有时会卡住。它似乎挂在 Get-AzKeyvaultSecret cmdlet 的第 183 行处。这个问题偶尔会发生,我不明白为什么会发生。 以下是 PowerShell 版本:
Windows 10: PowerShell 版本:5.1.19041.3031
Windows 11: PowerShell版本:5.1.22621.2506
代码:
# Load Windows Forms
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing
# Load configuration from JSON file
Try {
$configFilePath = "D:\AxKVMigrationInfo\config.json"
$config = Get-Content $configFilePath -ErrorAction Stop | ConvertFrom-Json
}
Catch {
Write-Host "[ERROR] Config file not found: $_"
exit 1
}
# Set log file path from the configuration
$logFilePath = $config.FilePaths.Log
# Subscription details for Source Tenant
$sourceSubscriptionId = $config.SourceTenant.SubscriptionId
$sourceVaultName = $config.SourceTenant.VaultName
# Subscription details for Target Tenant
$targetSubscriptionId = $config.TargetTenant.SubscriptionId
$targetVaultName = $config.TargetTenant.VaultName
# CSV file path where migrated secrets will get exported
$logDirectory = Split-Path -Path $LogFilePath -Parent
$CSVFilePath = Join-Path -Path $logDirectory -ChildPath "Migrated_Secrets.csv"
# Function to log messages
function Write-Log {
param (
[string]$message,
[string]$logType
)
$logMessage = "$logType - $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') - $message"
Add-Content -Path $logFilePath -Value $logMessage
}
# Function to check and install requried modules
function Import-RequiredModule {
Try {
Import-Module -Name Az.KeyVault -ErrorAction Stop
Import-Module -Name Az.Accounts -ErrorAction Stop
}
catch {
Write-Host "The required PowerShell module is not found on the machine. Installing the modules Az.KeyVault and Az.Accounts."
Write-Log "The required PowerShell module is not found on the machine. Installing the modules Az.KeyVault and Az.Accounts." "[INFO]"
Install-Module -Name Az.KeyVault
Install-Module -Name Az.Accounts
Import-Module -Name Az.KeyVault
Import-Module -Name Az.Accounts
Write-Host "Az.KeyVault and Az.Accounts successfully installed and imported"
Write-Log "Az.KeyVault and Az.Accounts successfully installed and imported" "[INFO]"
}
}
# Function to prompt the user to enter tenant credentials
function Prompt-ForCredentials {
param (
[string]$account
)
$title = "Enter $account Tenant Credentials"
$message = "Please enter the credentials for $account Tenant."
[System.Windows.Forms.MessageBox]::Show($message, $title, [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Information)
}
# Function to connect to Azure account and log connection event
function Connect-ToAzureAccount {
param (
[string]$SubscriptionId,
[string]$Account
)
Write-Host "Enter $Account Tenant credentials"
# Prompt user for credentials
Prompt-ForCredentials -account $Account
# Connect to Azure account
try {
Connect-AzAccount -Subscription $SubscriptionId -ErrorAction Stop
# Get the current user
$currentUser = (Get-AzContext).Account.Id
Write-Log "Connected to $Account Azure account. User: $currentUser" "[INFO]"
}
catch {
Write-Log "Error connecting to $Account Azure account: $_" "[ERROR]"
throw "Error connecting to $Account Azure account."
}
}
# Function to disconnect from Azure account and log disconnection event
function Disconnect-FromAzureAccount {
param (
[string]$Account
)
# Disconnect from Azure account
try {
Disconnect-AzAccount -ErrorAction Stop
Write-Log "Disconnected from $Account Azure account" "[INFO]"
}
catch {
Write-Log "Error disconnecting from $Account Azure account: $_" "[ERROR]"
throw "Error disconnecting from $Account Azure account."
}
}
# Function to display a confirmation message box
function Show-ConfirmationBox {
param (
[string]$message
)
return [System.Windows.Forms.MessageBox]::Show(
$message,
"Confirmation",
[System.Windows.Forms.MessageBoxButtons]::OKCancel,
[System.Windows.Forms.MessageBoxIcon]::Warning
)
}
# Function to export specific secrets from source Key Vault based on CSV file
function Export-SpecificSecretsFromSourceKeyVault {
param (
[string]$SourceVaultName,
[string]$SourceSubscriptionId,
[string[]]$ArrSelectedSecretName
)
# Update the minimum and maximum of the progress bar.
$ProgressBar.Minimum = 0
$ProgressBar.Maximum = $ArrSelectedSecretName.Count
# Update the text of the Progress label.
$progressLable.Text = "Exporting....."
Write-Host "Exporting specific secrets from source Key Vault ($SourceVaultName) in Subscription ($SourceSubscriptionId)"
Write-Log "Exporting specific secrets from source Key Vault ($SourceVaultName) in Subscription ($SourceSubscriptionId)" "[INFO]"
# Export specific secrets based on Selected Items
$SpecificSecretDetailsExport = foreach ($secretName in $ArrSelectedSecretName) {
# update progress status:
$ProgressBar.Value = $ProgressBarValue
try {
Get-AzKeyVaultSecret -VaultName $SourceVaultName -Name $secretName -ErrorAction Stop
}
catch {
Write-Log "Error exporting secret '$secretName' from source Key Vault: $_" "[ERROR]"
}
$ProgressBarValue++
}
# Update the text of the Progress label.
$progressLable.Text = "Exported"
Write-Host "Exporting Successful."
Write-Log "Exporting Successful." "[INFO]"
Disconnect-FromAzureAccount -Account "Source"
return $SpecificSecretDetailsExport
}
# Function to export migrated or imported secrets to CSV
function Export-SecretsToCSV {
param (
[string]$CSVFilePath,
[PSCustomObject]$SecretDetail
)
# Extract specific details for each secret
$exportData = [PSCustomObject]@{
SecretName = $SecretDetail.Name
ContentType = $SecretDetail.ContentType
ActivationDate = $SecretDetail.NotBefore
ExpiryDate = $SecretDetail.Expires
State = $SecretDetail.Enabled
}
$exportData | Export-Csv -Path $CSVFilePath -NoTypeInformation -Append
Write-Log "Secrets exported to CSV file: $CSVFilePath" "[INFO]"
}
# Function to import secrets into target Key Vault
function Import-SecretsIntoTargetKeyVault {
param (
[string]$TargetVaultName,
[string]$TargetSubscriptionId,
[Array]$SecretDetails
)
Connect-ToAzureAccount -SubscriptionId $TargetSubscriptionId -Account "Target"
# Update the minimum and maximum of the progress bar.
$ProgressBar.Minimum = 0
$ProgressBar.Maximum = $SecretDetails.Count
# Update the text of the Progress label.
$progressLable.Text = "Importing....."
Write-Log "Importing secrets into target Key Vault ($TargetVaultName) in Subscription ($TargetSubscriptionId)." "[INFO]"
# Core logic
foreach ($SD in $SecretDetails) {
$SecretName = $SD.Name
# update progress status:
$ProgressBar.Value = $ProgressBarValue
if ($SecretName -ne $null) {
# Check if the secret already exists in the target Key Vault
$existingSecret = Get-AzKeyVaultSecret -VaultName $TargetVaultName -Name $SecretName -ErrorAction SilentlyContinue
if ($existingSecret -eq $null) {
# Create secret into the Target Tenant
try {
Set-AzKeyVaultSecret -VaultName $TargetVaultName -Name $SecretName -SecretValue $SD.SecretValue -Expires $SD.Expires -NotBefore $SD.NotBefore -ContentType $SD.ContentType -Tag $SD.Tags -ErrorAction Stop
Write-Log "Secret '$SecretName' imported successfully." "[SUCCESS]"
# Call Export-SecretsToCSV function after importing secrets into the target Key Vault
Export-SecretsToCSV -CSVFilePath $CSVFilePath -SecretDetail $SD
}
catch {
Write-Log "Error importing secret '$SecretName' into target Key Vault: $_" "[ERROR]"
}
}
else {
Write-Log "Secret '$SecretName' already exists in the target Key Vault. Skipping." "[WARNING]"
}
}
$ProgressBarValue++
}
# Update the text of the Progress label.
$progressLable.Text = "Imported"
Disconnect-FromAzureAccount -Account "Target"
Write-Log "Import completed successfully." "[INFO]"
}
# Define minimum form size
$minimumFormSize = New-Object System.Drawing.Size(500, 550)
# Function to dynamically adjust the size of $checkedListBox based on form size
function Resize-CheckedListBox {
$checkedListBox.Size = New-Object System.Drawing.Size(($form.ClientSize.Width - 30), ($form.ClientSize.Height - 150))
$buttonSelectAll.Location = New-Object System.Drawing.Point(10, ($form.ClientSize.Height - 120))
$buttonDeselectAll.Location = New-Object System.Drawing.Point(170, ($form.ClientSize.Height - 120))
$buttonMigrate.Location = New-Object System.Drawing.Point(($form.ClientSize.Width - $buttonMigrate.Width - 30), ($form.ClientSize.Height - 120))
$progressBar.Location = New-Object System.Drawing.Point(10, ($form.ClientSize.Height - 60))
$progressLable.Location = New-Object System.Drawing.Point(350, ($form.ClientSize.Height - 60))
}
# Function to enforce minimum form size
function Enforce-MinimumFormSize {
if ($form.ClientSize.Width -lt $minimumFormSize.Width -or $form.ClientSize.Height -lt $minimumFormSize.Height) {
$form.ClientSize = $minimumFormSize
}
}
# Function to enable or disable the migration button based on selection
function Update-MigrationButton {
if ($checkedListBox.CheckedItems.Count -gt 0) {
$buttonMigrate.Enabled = $true
}
else {
$buttonMigrate.Enabled = $false
}
}
# Create Form
$form = New-Object System.Windows.Forms.Form
$form.Text = "AzKV Secret Migration Tool"
$form.Size = $minimumFormSize # Set initial form size to minimum
$form.StartPosition = "CenterScreen"
# Attach Resize event handler to form
$form.add_Resize({
Resize-CheckedListBox
Enforce-MinimumFormSize
})
# Attach Load event handler to form to ensure minimum size is enforced initially
$form.add_Load({
Enforce-MinimumFormSize
})
# Create CheckedListBox
$checkedListBox = New-Object System.Windows.Forms.CheckedListBox
$checkedListBox.Location = New-Object System.Drawing.Point(10, 10)
$checkedListBox.Font = New-Object System.Drawing.Font("Arial", 12) # Adjust the font size as needed
$checkedListBox.ItemHeight = 30 # Adjust the height as needed
# Create Button for Select All
$buttonSelectAll = New-Object System.Windows.Forms.Button
$buttonSelectAll.Size = New-Object System.Drawing.Size(150, 30)
$buttonSelectAll.Location = New-Object System.Drawing.Point(10, ($form.ClientSize.Height - 120))
$buttonSelectAll.Text = "Select All"
# Add Click event to the Select All Button
$buttonSelectAll.Add_Click({
# Check all checkboxes
for ($i = 0; $i -lt $checkedListBox.Items.Count; $i++) {
$checkedListBox.SetItemChecked($i, $true)
# Update migration button status
Update-MigrationButton
}
})
# Create Button for Deselect All
$buttonDeselectAll = New-Object System.Windows.Forms.Button
$buttonDeselectAll.Size = New-Object System.Drawing.Size(150, 30)
$buttonDeselectAll.Location = New-Object System.Drawing.Point(170, ($form.ClientSize.Height - 120))
$buttonDeselectAll.Text = "Deselect All"
# Add Click event to the Deselect All Button
$buttonDeselectAll.Add_Click({
# Uncheck all checkboxes
for ($i = 0; $i -lt $checkedListBox.Items.Count; $i++) {
$checkedListBox.SetItemChecked($i, $false)
# Update migration button status
Update-MigrationButton
}
})
# Create Button for Migrate
$buttonMigrate = New-Object System.Windows.Forms.Button
$buttonMigrate.Size = New-Object System.Drawing.Size(150, 30)
$buttonMigrate.Text = "Migrate Secrets"
$buttonMigrate.Enabled = $false # Initially disable the button
# Import required Module
Import-RequiredModule
# Get secrets from source Key Vault
Connect-ToAzureAccount -SubscriptionId $SourceSubscriptionId -Account "Source"
Try {
$secrets = Get-AzKeyVaultSecret -VaultName $sourceVaultName
}
catch {
Write-Log "Error Listing secret '$secretName' from source Key Vault: $_" "[ERROR]"
exit 1
}
# Populate CheckedListBox with secret names
$secrets | ForEach-Object {
[void]$checkedListBox.Items.Add($_.Name)
}
# Progress Bar
$progressBar = New-Object System.Windows.Forms.ProgressBar
$progressBar.Style = "Continuous"
$progressBar.Width = 310
$progressBar.Height = 25
$progressBar.Location = New-Object System.Drawing.Point(10, ($form.ClientSize.Height - 60))
$ProgressBarValue = 1
# Progress bar label
$progressLable = New-Object System.Windows.Forms.Label
$progressLable.AutoSize = $true
$progressLable.Width = 100
$progressLable.Height = 25
$progressLable.Location = New-Object System.Drawing.Point(350, ($form.ClientSize.Height - 60))
# Add Click event to the Button
$buttonMigrate.Add_Click({
# Display confirmation message box
$confirmation = Show-ConfirmationBox "The selected secrets will be Imported into the target tenant. If you want to migrate click on OK"
# Check if the user clicked OK
if ($confirmation -eq [System.Windows.Forms.DialogResult]::OK) {
# Get selected secrets
$selectedSecrets = Export-SpecificSecretsFromSourceKeyVault -SourceVaultName $sourceVaultName -SourceSubscriptionId $sourceSubscriptionId -ArrSelectedSecretName $checkedListBox.CheckedItems
# Proceed with migration
Import-SecretsIntoTargetKeyVault -TargetVaultName $targetVaultName -TargetSubscriptionId $targetSubscriptionId -SecretDetails $selectedSecrets
# Show Import successful message
[System.Windows.Forms.MessageBox]::Show("Imported successfully! For more details, check the logs file.", "Import Successful", [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Information)
$form.Close()
}
})
# Add controls to the form
$form.Controls.AddRange(@($checkedListBox, $buttonSelectAll, $buttonDeselectAll, $buttonMigrate, $progressBar, $progressLable))
# Add Change event to the checkedListBox to update migration button status
$checkedListBox.Add_SelectedIndexChanged({ Update-MigrationButton })
# Call Resize-CheckedListBox to set initial sizes and positions
Resize-CheckedListBox
# Show the form
[void]$form.ShowDialog()
# Display a message indicating the completion of the script
Write-Log "Key Vault migration script completed successfully." "[INFO]"
# Display the log file path
Write-Host "Log file exported to: $logFilePath"
# Display the log file path
Write-Host "CSV file exported to: $CSVFilePath"
# Remove all variables for no longer access.
Remove-Variable * -ErrorAction SilentlyContinue
我已尝试以下步骤:
我希望该脚本能够在 Windows 10 上运行,就像它在 Windows 11 中运行一样。