зеркало из https://github.com/Azure/RGCOPY.git
RGCOPY 0.9.65
This commit is contained in:
Родитель
4bbdffb9b5
Коммит
164439278e
|
@ -1,6 +1,6 @@
|
|||
# RGCOPY documentation
|
||||
***
|
||||
**Version: 0.9.64<BR>May 2024**
|
||||
**Version: 0.9.65<BR>September 2024**
|
||||
***
|
||||
|
||||
### Introduction
|
||||
|
@ -150,6 +150,8 @@ same az-user / same region|same az-user / different region|different az-user
|
|||
|
||||
\* Normally, a full snapshot is used. For `UltraSSD_LRS` and `PremiumV2_LRS` an incremental snapshot is used instead.
|
||||
|
||||
> :warning: **Warning:** RGCOPY uses the same name for incremental and full snapshots. When copying to a different region, an existing full snapshot cannot be used. Creating new snapshots can break parallel running RGCOPY runs that use the same source RG.
|
||||
|
||||
You can further change the behavior by setting the following parameters:
|
||||
|
||||
parameter|[DataType]: usage
|
||||
|
@ -263,7 +265,7 @@ The following resource configuration parameters exist:
|
|||
parameter|usage (data type is always [string] or [array])
|
||||
:---|:---
|
||||
**`setVmSize`** =<BR>`@("size@vm1,vm2,...", ...)` |Set VM Size: <ul><li>**size**: VM size (e.g. Standard_E32s_v3) </li><li>**vm**: VM name</li></ul>
|
||||
**`setDiskSku`** =<BR>`@("sku@disk1,disk2,...", ...)` |Set Disk SKU (default value is **Premium_LRS**): <ul><li>**sku** in {`Premium_LRS`, `StandardSSD_LRS`, `Standard_LRS`, `Premium_ZRS`, `StandardSSD_ZRS`, `PremiumV2_LRS`, `UltraSSD_LRS`} </li><li>**disk**: disk name</li></ul> :memo: **Note:** There are several restrictions:<ul><li> Converting **from** `PremiumV2_LRS` or `UltraSSD_LRS` requires incremental snapshots. RGCOPY does not support incremental snapshots if there already exists another incremental snapshot (not created by RGCOPY). Creating an 1TB disk from an incremental snapshot can take more then 1 hour.</li><li> Converting **to** `PremiumV2_LRS` or `UltraSSD_LRS` requires a zonal deployment. Therefore, parameter `setVmZone` must be used. </li><li> Converting **from** `PremiumV2_LRS` or `UltraSSD_LRS` to a different SKU is only possible if the logical sector size is 512.</li><li>When converting from a different SKU **to** `PremiumV2_LRS` or `UltraSSD_LRS`, the logical sector size is set to 512.</li></ul>:warning: **Warning:** If you want to avoid using the default value then you must explicitly <BR>set **setDiskSku = $Null**.
|
||||
**`setDiskSku`** =<BR>`@("sku@disk1,disk2,...", ...)` |Set Disk SKU (default value is **`Premium_LRS`**).<BR>When setting to `false` (or `$False` or `$Null`), the disk SKU is not changed.<ul><li>**sku** in {`Premium_LRS`, `StandardSSD_LRS`, `Standard_LRS`, `Premium_ZRS`, `StandardSSD_ZRS`, `PremiumV2_LRS`, `UltraSSD_LRS`, `false`} </li><li>**disk**: disk name</li></ul> :memo: **Note:** There are several restrictions:<ul><li> Converting **from** `PremiumV2_LRS` or `UltraSSD_LRS` requires incremental snapshots. Creating an 1TB disk from an incremental snapshot can take more then 1 hour.</li><li> Converting **to** `PremiumV2_LRS` or `UltraSSD_LRS` requires a zonal deployment. Therefore, parameter `setVmZone` must be used. </li><li> Converting **from** `PremiumV2_LRS` or `UltraSSD_LRS` to a different SKU is only possible if the logical sector size is 512.</li><li>When converting from a different SKU **to** `PremiumV2_LRS` or `UltraSSD_LRS`, the logical sector size is set to 512.</li><li>If the VM size does not support premium IO then the disk SKU is automatically adopted. For example, `Premium_ZRS` is converted to `StandardSSD_ZRS`.</li></ul>
|
||||
**`setDiskIOps`** = <BR>`@("iops@disk1,disk1,...", ...)`|Set Disk IOps: <ul><li>**iops**: maximum IOs per second</li><li>**disk**: disk name</li></ul>:memo: **Note:** This parameter only works for Premium V2 disks. It cannot be used for Ultra SSD disks.
|
||||
**`setDiskMBps`** = <BR>`@("mbps@disk1,disk1,...", ...)`|Set Disk MBps: <ul><li>**mbps**: maximum MB per second</li><li>**disk**: disk name</li></ul>:memo: **Note:** This parameter only works for Premium V2 disks. It cannot be used for Ultra SSD disks.
|
||||
**`setDiskSize`** = <BR>`@("size@disk1,disk1,...", ...)` |Set Disk Size: <ul><li>**size** in GB </li><li>**disk**: disk name</li></ul> :warning: **Warning:** It's only possible to *increase* the size of a disk. Partitions on the disk are not changed. This parameter was originally intended for increasing disk I/O on the target RG. Nowadays, you better should use parameter `setDiskTier` instead.
|
||||
|
@ -340,7 +342,7 @@ RGCOPY can change Availability Zones, Availability Sets and Proximity Placement
|
|||
|
||||
parameter|[DataType]: usage
|
||||
:---|:---
|
||||
**`setVmZone`** = <BR>`@("zone@vm1,vm2,...", ...)` |Set VM Availability Zone: <ul><li>**zone** in {none, 1, 2, 3} </li><li>**vm**: VM name </li></ul>:bulb: **Tip:** Rather than 'none', you can use '0' for removing zone information<BR>:bulb: **Tip:** Disks are always created in the same zone as their VMs. Detached disks are only copied when parameter `copyDetachedDisks` was set. In this case, the detached disks are created in the zone that is defined by parameter **`defaultDiskZone`** (with default value 0).
|
||||
**`setVmZone`** = <BR>`@("zone@vm1,vm2,...", ...)` |Set VM Availability Zone: <ul><li>**zone** in {none, 0, 1, 2, 3, false} </li><li>**vm**: VM name </li></ul>The default value is '0' which removes the zone configuration.<BR>:bulb: **Tip:** Rather than 'none', you can use '0' for removing zone configuration. When setting to 'false', the existing zone is not changed.<BR>:bulb: **Tip:** Disks are always created in the same zone as their VMs. Detached disks are only copied when parameter `copyDetachedDisks` was set. In this case, the detached disks are created in the zone that is defined by parameter **`defaultDiskZone`** (with default value 0).
|
||||
**`setVmFaultDomain`** = <BR>`@("fault@vm1,vm2,...", ...)` |Set VM Fault Domain: <ul><li>**fault**: Used Fault Domain in {none, 0, 1, 2} </li><li>**vm**: VM name </li></ul>:bulb: **Tip:** The value 'none' removes the Fault Domain configuration from the VM.<BR>:warning: **Warning:** Values {0, 1, 2} are only allowed if the VM is part of a VMSS Flex.
|
||||
**`skipVmssFlex`**|**[switch]**: do not copy existing VM Scale Sets Flexible. <BR>Hereby, the target RG does not contain any VM Scale Set.
|
||||
**`skipAvailabilitySet`**|**[switch]**: do not copy existing Availability Sets. <BR>Hereby, the target RG does not contain any Availability Set.
|
||||
|
@ -456,6 +458,8 @@ parameter|[DataType]: usage
|
|||
***
|
||||
## File Copy of NetApp Volumes
|
||||
|
||||
> :warning: **Warning:** File Copy of NetApp Volumes is a deprecated feature of RGCOPY. It will not be tested anymore. **It might or might not work in the future**. There will be no bug fixes for the RGCOPY file copy feature.
|
||||
|
||||
In Azure, you cannot export the snapshot of a NetApp volume to a BLOB or restore it in another region. Therefore, RGCOPY cannot directly copy NetApp volumes. However, RGCOPY supports NetApp volumes on LINUX using file copy (rather than disk or volume copy). Hereby, the following scenarios are possible:
|
||||
|
||||
source RG|target RG|procedure
|
||||
|
|
527
rgcopy.ps1
527
rgcopy.ps1
|
@ -1,7 +1,7 @@
|
|||
<#
|
||||
rgcopy.ps1: Copy Azure Resource Group
|
||||
version: 0.9.64
|
||||
version date: June 2024
|
||||
version: 0.9.65
|
||||
version date: September 2024
|
||||
Author: Martin Merdes
|
||||
Public Github: https://github.com/Azure/RGCOPY
|
||||
|
||||
|
@ -394,6 +394,8 @@ param (
|
|||
,[string] $monitorRG
|
||||
,$setVmTipGroup = @()
|
||||
,$setGroupTipSession = @()
|
||||
,[string] $setIpTag
|
||||
,[string] $setIpTagType = 'FirstPartyUsage'
|
||||
,[switch] $allowRunningVMs
|
||||
,[switch] $skipGreenlist
|
||||
,[switch] $skipStartSAP
|
||||
|
@ -3590,11 +3592,8 @@ function save-copyDisks {
|
|||
if (($sourceSubUser -ne $targetSubUser) `
|
||||
-or ($sourceSubTenant -ne $targetSubTenant)) {
|
||||
|
||||
# only possible for OSS version of RGCOPY
|
||||
if (!$msInternalVersion) {
|
||||
$blobCopy = $True
|
||||
$snapshotCopy = $False
|
||||
}
|
||||
$blobCopy = $True
|
||||
$snapshotCopy = $False
|
||||
}
|
||||
|
||||
# BLOB copy does not work for BLOBs larger than 4TiB using Start-AzStorageBlobCopy
|
||||
|
@ -3763,7 +3762,8 @@ function save-copyDisks {
|
|||
Caching = 'None' # will be updated below by VM info
|
||||
DiskControllerType = '' # will be updated below by VM info
|
||||
WriteAcceleratorEnabled = $False # will be updated below by VM info
|
||||
AbsoluteUri = '' # access token for copy to BLOB
|
||||
AbsoluteUri = '' # access token for source snapshot
|
||||
DelegationToken = '' # access token for target BLOB
|
||||
SkuName = $sku
|
||||
VmRestrictions = $False # will be updated later
|
||||
DiskIOPSReadWrite = $DiskIOPSReadWrite #e.g. 1024
|
||||
|
@ -5305,7 +5305,7 @@ function update-paramSetVmZone {
|
|||
$vmZone = 'none'
|
||||
}
|
||||
|
||||
test-values 'setVmZone' $vmZone @('none','1','2','3') 'zone'
|
||||
test-values 'setVmZone' $vmZone @('none','1','2','3', 'false') 'zone'
|
||||
# convert to internal syntax
|
||||
if ($vmZone -eq 'none') {
|
||||
$vmZone = 0
|
||||
|
@ -5316,7 +5316,12 @@ function update-paramSetVmZone {
|
|||
| Where-Object Name -in $script:paramVMs
|
||||
| ForEach-Object {
|
||||
|
||||
$_.VmZoneNew = $vmZone
|
||||
if ($vmZone -eq 'false') {
|
||||
$_.VmZoneNew = $_.VmZone
|
||||
}
|
||||
else {
|
||||
$_.VmZoneNew = $vmZone
|
||||
}
|
||||
}
|
||||
get-ParameterRule
|
||||
}
|
||||
|
@ -5480,13 +5485,18 @@ function update-paramSetDiskSku {
|
|||
while ($Null -ne $script:paramConfig) {
|
||||
|
||||
$sku = $script:paramConfig
|
||||
test-values 'setDiskSku' $sku @('Premium_LRS', 'StandardSSD_LRS', 'Standard_LRS', 'Premium_ZRS', 'StandardSSD_ZRS', 'PremiumV2_LRS', 'UltraSSD_LRS') 'sku'
|
||||
test-values 'setDiskSku' $sku @('false', 'Premium_LRS', 'StandardSSD_LRS', 'Standard_LRS', 'Premium_ZRS', 'StandardSSD_ZRS', 'PremiumV2_LRS', 'UltraSSD_LRS') 'sku'
|
||||
|
||||
$script:copyDisks.values
|
||||
| Where-Object {$_.Name -in $script:paramDisks}
|
||||
| ForEach-Object {
|
||||
|
||||
$_.SkuNameNew = $sku
|
||||
if ($sku -eq 'false') {
|
||||
$_.SkuNameNew = $_.SkuName
|
||||
}
|
||||
else {
|
||||
$_.SkuNameNew = $sku
|
||||
}
|
||||
}
|
||||
get-ParameterRule
|
||||
}
|
||||
|
@ -6600,7 +6610,8 @@ function copy-snapshots {
|
|||
$savedSub = $script:currentSub
|
||||
set-context $targetSub # *** CHANGE SUBSCRIPTION **************
|
||||
|
||||
$token = (Get-AzAccessToken).Token
|
||||
$secureToken= Get-AzAccessToken -AsSecureString -WarningAction 'SilentlyContinue'
|
||||
$token = ConvertFrom-SecureString $secureToken.Token -AsPlainText
|
||||
|
||||
# update $script:copyDisks
|
||||
$script:copyDisks.Values
|
||||
|
@ -6811,7 +6822,7 @@ function wait-completion {
|
|||
}
|
||||
}
|
||||
|
||||
write-logFile (Get-Date -Format 'yyyy-MM-dd HH:mm:ss \U\T\Cz')
|
||||
write-logFile "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss \U\T\Cz') $step COMPLETION"
|
||||
$percentAll = 100
|
||||
foreach ($item in $res) {
|
||||
if (($type -ne 'snapshots') -or ($item.Incremental -eq $True)) {
|
||||
|
@ -6896,10 +6907,104 @@ function remove-snapshots {
|
|||
write-stepEnd
|
||||
}
|
||||
|
||||
#--------------------------------------------------------------
|
||||
function add-ipRule {
|
||||
#--------------------------------------------------------------
|
||||
if (!$msInternalVersion) {
|
||||
return
|
||||
}
|
||||
|
||||
$savedSub = $script:currentSub
|
||||
set-context $targetSub # *** CHANGE SUBSCRIPTION **************
|
||||
|
||||
$ip = $Null
|
||||
|
||||
# get public IP of local PC: first try
|
||||
try {
|
||||
$ip = (Invoke-WebRequest ifconfig.me/ip -ErrorAction 'Stop').Content.Trim()
|
||||
}
|
||||
catch {
|
||||
$ip = $Null
|
||||
}
|
||||
|
||||
# check syntax for IPv4
|
||||
if ($ip -notmatch '^\d+\.\d+\.\d+\.\d+$') {
|
||||
# second try
|
||||
$ip = $Null
|
||||
try {
|
||||
$ip = (Resolve-DnsName `
|
||||
-Name myip.opendns.com `
|
||||
-Server 208.67.222.220 `
|
||||
-ErrorAction 'Stop' `
|
||||
-WarningAction 'SilentlyContinue').IPAddress
|
||||
}
|
||||
catch {
|
||||
$ip = $Null
|
||||
}
|
||||
}
|
||||
|
||||
# check syntax for IPv4
|
||||
if ($ip -notmatch '^\d+\.\d+\.\d+\.\d+$') {
|
||||
write-logFileError 'Getting public IP Address of local PC failed'
|
||||
}
|
||||
|
||||
write-logFile "Granting access to storage account '$targetSA' for public IP $ip ..."
|
||||
write-logFileWarning "All VPN connections must be closed"
|
||||
|
||||
Add-AzStorageAccountNetworkRule `
|
||||
-ResourceGroupName $targetRG `
|
||||
-Name $targetSA `
|
||||
-IPAddressOrRange $ip `
|
||||
-ErrorAction 'SilentlyContinue' | Out-Null
|
||||
test-cmdlet 'Add-AzStorageAccountNetworkRule' "Granting access to storage account '$targetSA' for public IP $ip failed"
|
||||
|
||||
set-context $savedSub # *** CHANGE SUBSCRIPTION **************
|
||||
}
|
||||
|
||||
#--------------------------------------------------------------
|
||||
function new-delegationToken {
|
||||
#--------------------------------------------------------------
|
||||
if (!$msInternalVersion) {
|
||||
return
|
||||
}
|
||||
|
||||
$savedSub = $script:currentSub
|
||||
set-context $targetSub # *** CHANGE SUBSCRIPTION **************
|
||||
|
||||
write-logFile "Creating delegation token..."
|
||||
|
||||
$context = New-AzStorageContext `
|
||||
-StorageAccountName $targetSA `
|
||||
-UseConnectedAccount `
|
||||
-ErrorAction 'SilentlyContinue'
|
||||
test-cmdlet 'New-AzStorageContext' "Getting Context for storage account '$targetSA' failed"
|
||||
|
||||
$StartTime = Get-Date
|
||||
$EndTime = $startTime.AddDays(7)
|
||||
|
||||
$script:delegationToken = New-AzStorageContainerSASToken `
|
||||
-Name $targetSaContainer `
|
||||
-Permission 'crwdl' `
|
||||
-StartTime $startTime `
|
||||
-ExpiryTime $endTime `
|
||||
-context $context `
|
||||
-ErrorAction 'SilentlyContinue'
|
||||
test-cmdlet 'New-AzStorageContainerSASToken' "Creating delegation token for storage account '$targetSA' failed"
|
||||
|
||||
# save token for each disk
|
||||
$script:copyDisks.values
|
||||
| ForEach-Object {
|
||||
$_.DelegationToken = $script:delegationToken
|
||||
}
|
||||
|
||||
set-context $savedSub # *** CHANGE SUBSCRIPTION **************
|
||||
}
|
||||
|
||||
#--------------------------------------------------------------
|
||||
function grant-access {
|
||||
#--------------------------------------------------------------
|
||||
$token = (Get-AzAccessToken).Token
|
||||
$secureToken = Get-AzAccessToken -AsSecureString -WarningAction 'SilentlyContinue'
|
||||
$token = ConvertFrom-SecureString $secureToken.Token -AsPlainText
|
||||
|
||||
# update $script:copyDisks
|
||||
$script:copyDisks.Values
|
||||
|
@ -7020,6 +7125,8 @@ function grant-access {
|
|||
#--------------------------------------------------------------
|
||||
# start execution
|
||||
write-stepStart "GRANT ACCESS TO SNAPSHOTS" $maxDOP
|
||||
add-ipRule
|
||||
write-logFile
|
||||
$param = get-scriptBlockParam $scriptParameter $script $maxDOP
|
||||
|
||||
$script:copyDisks.Values
|
||||
|
@ -7032,6 +7139,9 @@ function grant-access {
|
|||
if (!$?) {
|
||||
write-logFileError "Grant Access to snapshot failed"
|
||||
}
|
||||
|
||||
write-logFile
|
||||
new-delegationToken
|
||||
write-stepEnd
|
||||
}
|
||||
|
||||
|
@ -7079,21 +7189,77 @@ function revoke-access {
|
|||
write-stepEnd
|
||||
}
|
||||
|
||||
#--------------------------------------------------------------
|
||||
function get-saKey {
|
||||
#--------------------------------------------------------------
|
||||
param (
|
||||
$mySub,
|
||||
$myRG,
|
||||
$mySA
|
||||
)
|
||||
|
||||
if ($msInternalVersion -or ($Null -ne $script:targetSaKey)) {
|
||||
return
|
||||
}
|
||||
|
||||
$savedSub = $script:currentSub
|
||||
set-context $mySub # *** CHANGE SUBSCRIPTION **************
|
||||
|
||||
# Get Storage Account KEY
|
||||
$script:targetSaKey = (Get-AzStorageAccountKey `
|
||||
-ResourceGroupName $myRG `
|
||||
-AccountName $mySA `
|
||||
-WarningAction 'SilentlyContinue' `
|
||||
-ErrorAction 'SilentlyContinue' `
|
||||
| Where-Object KeyName -eq 'key1').Value
|
||||
|
||||
test-cmdlet 'Get-AzStorageAccountKey' "Could not get key for Storage Account '$mySA'"
|
||||
|
||||
set-context $savedSub # *** CHANGE SUBSCRIPTION **************
|
||||
}
|
||||
|
||||
#--------------------------------------------------------------
|
||||
function start-copyBlobs {
|
||||
#--------------------------------------------------------------
|
||||
write-stepStart "START COPY TO BLOB" $maxDOP
|
||||
|
||||
get-saKey $targetSub $targetRG $targetSA
|
||||
|
||||
# using parameters for parallel execution
|
||||
$scriptParameter = "`$targetSaContainer = '$targetSaContainer';"
|
||||
$scriptParameter += "`$targetSA = '$targetSA';"
|
||||
$scriptParameter += "`$targetSaKey = '$($script:targetSaKey -replace '''', '''''')';"
|
||||
|
||||
# parallel running script
|
||||
$script = {
|
||||
Write-Output "... copying '$($_.SnapshotName)' to BLOB '$($_.Name).vhd'"
|
||||
# header using Delegation Token
|
||||
if ($msInternalVersion) {
|
||||
write-logFile "Using Delegation Token"
|
||||
|
||||
$destinationContext = New-AzStorageContext `
|
||||
-StorageAccountName $targetSA `
|
||||
-UseConnectedAccount `
|
||||
-ErrorAction 'SilentlyContinue'
|
||||
$sc1 = {
|
||||
Write-Output "... copying '$($_.SnapshotName)' to BLOB '$($_.Name).vhd'"
|
||||
|
||||
$destinationContext = New-AzStorageContext `
|
||||
-StorageAccountName $targetSA `
|
||||
-SasToken $_.DelegationToken `
|
||||
-ErrorAction 'SilentlyContinue'
|
||||
}
|
||||
}
|
||||
|
||||
# header using Storage Account Key
|
||||
else {
|
||||
write-logFile "Using Storage Account Key"
|
||||
$sc1 = {
|
||||
Write-Output "... copying '$($_.SnapshotName)' to BLOB '$($_.Name).vhd'"
|
||||
|
||||
$destinationContext = New-AzStorageContext `
|
||||
-StorageAccountName $targetSA `
|
||||
-StorageAccountKey $targetSaKey `
|
||||
-ErrorAction 'SilentlyContinue' `
|
||||
-WarningAction 'SilentlyContinue'
|
||||
}
|
||||
}
|
||||
|
||||
# body
|
||||
$sc2 = {
|
||||
|
||||
$param = @{
|
||||
DestContainer = $targetSaContainer
|
||||
|
@ -7114,10 +7280,9 @@ function start-copyBlobs {
|
|||
Write-Output "$($_.Name).vhd"
|
||||
}
|
||||
|
||||
# start execution
|
||||
write-stepStart "START COPY TO BLOB" $maxDOP
|
||||
write-logFile "using role-based access control" -ForegroundColor DarkGray
|
||||
|
||||
# start execution
|
||||
$script = [Scriptblock]::Create(($sc1 -as [string]) + ($sc2 -as [string]))
|
||||
$param = get-scriptBlockParam $scriptParameter $script $maxDOP
|
||||
$script:copyDisks.Values
|
||||
| Where-Object Skip -ne $True
|
||||
|
@ -7134,20 +7299,49 @@ function start-copyBlobs {
|
|||
#--------------------------------------------------------------
|
||||
function stop-copyBlobs {
|
||||
#--------------------------------------------------------------
|
||||
write-stepStart "STOP COPY TO BLOB" $maxDOP
|
||||
|
||||
get-saKey $targetSub $targetRG $targetSA
|
||||
|
||||
# using parameters for parallel execution
|
||||
$scriptParameter = "`$targetSaContainer = '$targetSaContainer';"
|
||||
$scriptParameter += "`$targetSA = '$targetSA';"
|
||||
$scriptParameter += "`$targetSaKey = '$($script:targetSaKey -replace '''', '''''')';"
|
||||
|
||||
# header using Delegation Token
|
||||
if ($msInternalVersion) {
|
||||
write-logFile "Using Delegation Token"
|
||||
|
||||
$sc1 = {
|
||||
$diskname = $_.Name
|
||||
Write-Output "... stopping BLOB copy $diskname.vhd"
|
||||
|
||||
# parallel running script
|
||||
$script = {
|
||||
$diskname = $_.Name
|
||||
Write-Output "... stopping BLOB copy $diskname.vhd"
|
||||
try {
|
||||
$destinationContext = New-AzStorageContext `
|
||||
-StorageAccountName $targetSA `
|
||||
-UseConnectedAccount `
|
||||
-ErrorAction 'SilentlyContinue'
|
||||
-StorageAccountName $targetSA `
|
||||
-SasToken $_.DelegationToken `
|
||||
-ErrorAction 'SilentlyContinue'
|
||||
}
|
||||
}
|
||||
|
||||
# header using Storage Account Key
|
||||
else {
|
||||
write-logFile "Using Storage Account Key"
|
||||
|
||||
$sc1 = {
|
||||
$diskname = $_.Name
|
||||
Write-Output "... stopping BLOB copy $diskname.vhd"
|
||||
|
||||
$destinationContext = New-AzStorageContext `
|
||||
-StorageAccountName $targetSA `
|
||||
-StorageAccountKey $targetSaKey `
|
||||
-ErrorAction 'SilentlyContinue' `
|
||||
-WarningAction 'SilentlyContinue'
|
||||
}
|
||||
}
|
||||
|
||||
# body
|
||||
$sc2 = {
|
||||
try {
|
||||
Stop-AzStorageBlobCopy `
|
||||
-Container $targetSaContainer `
|
||||
-Context $destinationContext `
|
||||
|
@ -7164,9 +7358,7 @@ function stop-copyBlobs {
|
|||
}
|
||||
|
||||
# start execution
|
||||
write-stepStart "STOP COPY TO BLOB" $maxDOP
|
||||
write-logFile "using role-based access control" -ForegroundColor DarkGray
|
||||
|
||||
$script = [Scriptblock]::Create(($sc1 -as [string]) + ($sc2 -as [string]))
|
||||
$param = get-scriptBlockParam $scriptParameter $script $maxDOP
|
||||
$script:copyDisks.Values
|
||||
| Where-Object Skip -ne $True
|
||||
|
@ -7184,11 +7376,30 @@ function stop-copyBlobs {
|
|||
function wait-copyBlobs {
|
||||
#--------------------------------------------------------------
|
||||
write-stepStart "CHECK BLOB COPY COMPLETION" -skipLF
|
||||
|
||||
$destinationContext = New-AzStorageContext `
|
||||
-StorageAccountName $targetSA `
|
||||
-UseConnectedAccount `
|
||||
-ErrorAction 'SilentlyContinue'
|
||||
|
||||
get-saKey $targetSub $targetRG $targetSA
|
||||
|
||||
# using Delegation Token
|
||||
if ($msInternalVersion) {
|
||||
write-logFile "Using Delegation Token"
|
||||
|
||||
$destinationContext = New-AzStorageContext `
|
||||
-StorageAccountName $targetSA `
|
||||
-SasToken $script:delegationToken `
|
||||
-ErrorAction 'SilentlyContinue'
|
||||
}
|
||||
|
||||
# using Storage Account Key
|
||||
else {
|
||||
write-logFile "Using Storage Account Key"
|
||||
|
||||
$destinationContext = New-AzStorageContext `
|
||||
-StorageAccountName $targetSA `
|
||||
-StorageAccountKey $targetSaKey `
|
||||
-ErrorAction 'SilentlyContinue' `
|
||||
-WarningAction 'SilentlyContinue'
|
||||
}
|
||||
|
||||
test-cmdlet 'New-AzStorageContext' "Could not get context for Storage Account '$targetSA'"
|
||||
|
||||
# create tasks
|
||||
|
@ -7209,7 +7420,7 @@ function wait-copyBlobs {
|
|||
$script:waitCount = 0
|
||||
do {
|
||||
Write-logFile
|
||||
write-logFile (Get-Date -Format 'yyyy-MM-dd HH:mm:ss \U\T\Cz')
|
||||
write-logFile "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss \U\T\Cz') BLOB COPY COMPLETION"
|
||||
$done = $True
|
||||
foreach ($task in $runningBlobTasks) {
|
||||
|
||||
|
@ -7220,10 +7431,11 @@ function wait-copyBlobs {
|
|||
|
||||
try {
|
||||
$state = Get-AzStorageBlob `
|
||||
-Blob $task.blob `
|
||||
-Container $targetSaContainer `
|
||||
-Context $destinationContext `
|
||||
-ErrorAction 'Stop' `
|
||||
-Blob $task.blob `
|
||||
-Container $targetSaContainer `
|
||||
-Context $destinationContext `
|
||||
-WarningAction 'SilentlyContinue' `
|
||||
-ErrorAction 'Stop' ` `
|
||||
| Get-AzStorageBlobCopyState
|
||||
}
|
||||
catch {
|
||||
|
@ -7273,7 +7485,8 @@ function new-disks {
|
|||
$savedSub = $script:currentSub
|
||||
set-context $targetSub # *** CHANGE SUBSCRIPTION **************
|
||||
|
||||
$token = (Get-AzAccessToken).Token
|
||||
$secureToken= Get-AzAccessToken -AsSecureString -WarningAction 'SilentlyContinue'
|
||||
$token = ConvertFrom-SecureString $secureToken.Token -AsPlainText
|
||||
|
||||
# get storage account ID
|
||||
$blobsSaID = get-resourceString `
|
||||
|
@ -13204,17 +13417,21 @@ function new-storageAccount {
|
|||
SkuName = $SkuName
|
||||
Kind = $Kind
|
||||
AccessTier = $accessTier
|
||||
AllowSharedKeyAccess = $False
|
||||
MinimumTlsVersion = 'TLS1_2'
|
||||
AllowBlobPublicAccess = $False
|
||||
WarningAction = 'SilentlyContinue'
|
||||
ErrorAction = 'SilentlyContinue'
|
||||
}
|
||||
|
||||
if ($msInternalVersion) {
|
||||
$param.AllowSharedKeyAccess = $False
|
||||
$param.PublicNetworkAccess = 'Enabled'
|
||||
$param.NetworkRuleSet = @{defaultAction = 'Deny'}
|
||||
}
|
||||
|
||||
if ($fileStorage) {
|
||||
$param.DnsEndpointType = 'Standard'
|
||||
$param.PublicNetworkAccess = 'Disabled'
|
||||
$param.MinimumTlsVersion = 'TLS1_2'
|
||||
$param.AllowBlobPublicAccess = $False
|
||||
$param.NetworkRuleSet = @{defaultAction = 'Deny'}
|
||||
if ($nfsQuotaGiB -gt 5120) {
|
||||
$param.EnableLargeFileShare = $True
|
||||
}
|
||||
|
@ -15624,7 +15841,7 @@ function test-copyMode {
|
|||
'setVmMerge'
|
||||
# 'setVmName'
|
||||
# 'renameDisks'
|
||||
'allowExistingDisks'
|
||||
# 'allowExistingDisks'
|
||||
# Update Mode
|
||||
'updateMode'
|
||||
'deleteSnapshotsAll'
|
||||
|
@ -17834,6 +18051,29 @@ function add-az_publicIPAddresses {
|
|||
#--------------------------------------------------------------
|
||||
foreach ($az_res in $script:az_publicIPAddresses) {
|
||||
|
||||
$ipTags = @()
|
||||
# copy existing IP tags
|
||||
if ('setIpTag' -notin $boundParameterNames) {
|
||||
foreach ($tag in $az_res.IpTags) {
|
||||
if ($tag.Tag.Length) {
|
||||
$ipTags += @{
|
||||
ipTagType = $tag.IpTagType
|
||||
tag = $tag.Tag
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# create new IP tag
|
||||
if ($setIpTag.length -ne 0) {
|
||||
$ipTags = @(
|
||||
@{
|
||||
ipTagType = $setIpTagType
|
||||
tag = $setIpTag
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
# create resource
|
||||
$resource = @{
|
||||
type = 'Microsoft.Network/publicIPAddresses'
|
||||
|
@ -17845,6 +18085,7 @@ function add-az_publicIPAddresses {
|
|||
publicIPAddressVersion = $az_res.PublicIpAddressVersion -as [string]
|
||||
publicIPAllocationMethod = $az_res.PublicIpAllocationMethod -as [string]
|
||||
idleTimeoutInMinutes = $az_res.IdleTimeoutInMinutes
|
||||
ipTags = $ipTags
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -18678,6 +18919,11 @@ function step-copyBlobsAndSnapshots {
|
|||
}
|
||||
|
||||
if ($blobCopyNeeded -and !$skipRemoteCopy) {
|
||||
if ($restartRemoteCopy) {
|
||||
add-ipRule
|
||||
new-delegationToken
|
||||
write-logFile
|
||||
}
|
||||
wait-copyBlobs
|
||||
revoke-access
|
||||
}
|
||||
|
@ -18879,48 +19125,36 @@ function step-cleanup {
|
|||
#-------------------------------------------------------------
|
||||
function step-patchMode {
|
||||
#-------------------------------------------------------------
|
||||
|
||||
write-logFileConfirm "Patch and Reboot VMs in resource group '$SourceRG'"
|
||||
write-stepStart "Patching VMs"
|
||||
get-shellScripts
|
||||
|
||||
if ($skipPatch) {
|
||||
return
|
||||
if (!$simulate) {
|
||||
start-VMsParallel $sourceRG $patchVMs
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------
|
||||
write-stepStart "Patching LINUX VMs"
|
||||
|
||||
foreach ($vmName in $patchVMs) {
|
||||
if ($script:copyVMs[$vmName].OsDisk.osType -eq 'linux') {
|
||||
$script:linuxVMs += $vmName
|
||||
}
|
||||
else {
|
||||
write-logFileWarning "VM '$vmName' skipped because it is not a Linux VM"
|
||||
}
|
||||
}
|
||||
|
||||
if (!$simulate) {
|
||||
start-VMsParallel $sourceRG $script:linuxVMs
|
||||
}
|
||||
|
||||
foreach ($vmName in $script:linuxVMs) {
|
||||
$osType = $script:copyVMs[$vmName].OsDisk.OsType
|
||||
write-logFile
|
||||
write-logFile (Get-Date -Format 'yyyy-MM-dd HH:mm:ss \U\T\Cz') -ForegroundColor 'DarkGray'
|
||||
write-logfile '----------------------------------------------------------------------'
|
||||
write-logfile "Patch VM '$vmName'"
|
||||
write-logfile "Patch VM '$vmName' ($osType)"
|
||||
|
||||
if (!$simulate) {
|
||||
new-scriptPatch $vmName
|
||||
if ($script:copyVMs[$vmName].OsDisk.osType -eq 'linux') {
|
||||
new-scriptPatch $vmName -Linux
|
||||
}
|
||||
else {
|
||||
new-scriptPatch $vmName -Windows
|
||||
}
|
||||
}
|
||||
}
|
||||
write-logFile
|
||||
|
||||
if ($script:linuxVMs.count -eq 0) {
|
||||
write-logFileWarning "No Linux VMs to patch"
|
||||
if ($patchVMs.count -eq 0) {
|
||||
write-logFileWarning "No VMs to patch"
|
||||
}
|
||||
else {
|
||||
write-logfile "Waiting 30 seconds for patches to finish..."
|
||||
Start-Sleep -seconds 30
|
||||
write-logFileWarning "OS patches might still be installing"
|
||||
}
|
||||
write-stepEnd
|
||||
}
|
||||
|
@ -18929,20 +19163,30 @@ function step-patchMode {
|
|||
function new-scriptPatch {
|
||||
#--------------------------------------------------------------
|
||||
param (
|
||||
$vmName
|
||||
$vmName,
|
||||
[switch] $Linux,
|
||||
[switch] $Windows
|
||||
)
|
||||
|
||||
invoke-patchScript $sourceRG $vmName $script:blockPatch
|
||||
if ($Windows) {
|
||||
invoke-patchScript $sourceRG $vmName $script:WindowsPatchScript 'RunPowerShellScript'
|
||||
|
||||
}
|
||||
|
||||
if ($script:ZYPPER_EXIT_INF_RESTART_NEEDED -eq $True) {
|
||||
|
||||
write-logfile
|
||||
write-logfile "Waiting 30 seconds on reboot..."
|
||||
write-logfile
|
||||
Start-Sleep -seconds 30
|
||||
|
||||
invoke-patchScript $sourceRG $vmName $script:blockPatch
|
||||
$script:ZYPPER_EXIT_INF_RESTART_NEEDED = $False
|
||||
elseif ($Linux) {
|
||||
invoke-patchScript $sourceRG $vmName $script:LinuxPatchScript 'RunShellScript'
|
||||
|
||||
# second try for ZYPPER
|
||||
if ($script:ZYPPER_EXIT_INF_RESTART_NEEDED -eq $True) {
|
||||
|
||||
write-logfile
|
||||
write-logfile "Waiting 30 seconds on reboot..."
|
||||
write-logfile
|
||||
Start-Sleep -seconds 30
|
||||
|
||||
invoke-patchScript $sourceRG $vmName $script:LinuxPatchScript 'RunShellScript'
|
||||
$script:ZYPPER_EXIT_INF_RESTART_NEEDED = $False
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -18952,14 +19196,15 @@ function invoke-patchScript {
|
|||
param (
|
||||
$resourceGroup,
|
||||
$scriptVm,
|
||||
$scriptText
|
||||
$scriptText,
|
||||
$commandId = 'RunShellScript'
|
||||
)
|
||||
|
||||
# script parameters
|
||||
$parameter = @{
|
||||
ResourceGroupName = $resourceGroup
|
||||
VMName = $scriptVM
|
||||
CommandId = 'RunShellScript'
|
||||
CommandId = $commandId
|
||||
ScriptString = $scriptText
|
||||
ErrorAction = 'SilentlyContinue'
|
||||
}
|
||||
|
@ -19015,11 +19260,48 @@ function get-shellScripts {
|
|||
}
|
||||
|
||||
#--------------------------------------------------------------
|
||||
# install patches (running on VM1 and VM2)
|
||||
# Windows patches
|
||||
#--------------------------------------------------------------
|
||||
$category = ''
|
||||
if (!$patchAll) {
|
||||
$category = " -Category 'Security Updates' "
|
||||
}
|
||||
|
||||
$logDir = "C:\Packages\Plugins\RGCOPY"
|
||||
$logFile = "C:\Packages\Plugins\RGCOPY\RGCOPY_WindowsUpdate_$(Get-Date -Format 'yyyy-MM-dd_HH-mm-ss')"
|
||||
|
||||
$script:WindowsPatchScript = @"
|
||||
New-Item '$logDir' -ItemType Directory -Force -ErrorAction SilentlyContinue | Out-Null
|
||||
|
||||
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
|
||||
Install-PackageProvider -Name NuGet -Force *>>'$logFile'
|
||||
Install-Module -Name PSWindowsUpdate -Force *>>'$logFile'
|
||||
Import-Module PSWindowsUpdate *>>'$logFile'
|
||||
|
||||
Get-WUInstallerStatus | Format-Table
|
||||
Get-WURebootStatus | Format-Table
|
||||
Get-WULastResults | Format-Table
|
||||
|
||||
Write-Host 'Last 10 updates:'
|
||||
Get-WUHistory -Last 10 | Format-Table
|
||||
|
||||
Write-Host 'Starting updates as job and reboot...'
|
||||
|
||||
`$string = @'
|
||||
Import-Module PSWindowsUpdate
|
||||
Get-WindowsUpdate -MicrosoftUpdate -Install $category -AcceptAll -AutoReboot *>>'$logFile'
|
||||
'@
|
||||
|
||||
`$script = [Scriptblock]::Create(`$string)
|
||||
Start-Job -ScriptBlock `$script *>>'$logFile'
|
||||
"@
|
||||
|
||||
#--------------------------------------------------------------
|
||||
# Linux patches
|
||||
#--------------------------------------------------------------
|
||||
# runs as script.sh
|
||||
# header file not used. Only parameter patchSecurity is used.
|
||||
$script:blockPatch =
|
||||
$script:LinuxPatchScript =
|
||||
@"
|
||||
#!/bin/bash
|
||||
patchSecurity=$patchSecurity
|
||||
|
@ -19384,7 +19666,14 @@ function new-vmExtension {
|
|||
[switch] $autoUpgradePossible
|
||||
)
|
||||
|
||||
if ($extensionName -in $script:existingExtensions) {
|
||||
$found = $False
|
||||
foreach ($existingExtension in $script:existingExtensions) {
|
||||
if ($existingExtension -like "*$extensionName*") {
|
||||
$found = $True
|
||||
}
|
||||
}
|
||||
|
||||
if ($found) {
|
||||
write-logFileUpdates 'extensions' $extensionName 'keep'
|
||||
return
|
||||
}
|
||||
|
@ -19488,7 +19777,7 @@ else {
|
|||
Clear-Host
|
||||
$error.Clear()
|
||||
$pref = (get-Item 'Env:\rgcopyBreakingChangeWarnings' -ErrorAction 'SilentlyContinue').value
|
||||
if ($Null -eq $pref ) {
|
||||
if ($pref -ne 'True') {
|
||||
Set-Item Env:\SuppressAzurePowerShellBreakingChangeWarnings "true"
|
||||
}
|
||||
|
||||
|
@ -19868,7 +20157,15 @@ try {
|
|||
write-logFileError "Source Subscription '$sourceSub' not found" -lastError
|
||||
}
|
||||
# Check Source Resource Group
|
||||
$sourceLocation = (Get-AzResourceGroup -Name $sourceRG -ErrorAction 'SilentlyContinue').Location
|
||||
$sourceRgObject = Get-AzResourceGroup -Name $sourceRG -ErrorAction 'SilentlyContinue'
|
||||
# tag names are case insensitive
|
||||
$tagName = $sourceRgObject.Tags.Keys | Where-Object {$_ -eq 'Owner'}
|
||||
# result of (Get-AzResourceGroup).Tags.Keys is case sensitive
|
||||
if (($Null -ne $tagName) -and ($Null -ne $sourceRgObject)) {
|
||||
$rgOwner = $sourceRgObject.Tags.$tagName
|
||||
}
|
||||
|
||||
$sourceLocation = $sourceRgObject.Location
|
||||
$sourceRgNotFound = ''
|
||||
|
||||
if ($Null -eq $sourceLocation) {
|
||||
|
@ -19889,6 +20186,7 @@ try {
|
|||
}
|
||||
write-logFile 'Source:'
|
||||
write-logFileTab 'Subscription' $sourceSub
|
||||
write-logFileTab 'SubscriptionID' $sourceSubID
|
||||
write-logFileTab 'User' $sourceSubUser
|
||||
write-logFileTab 'Tenant' $sourceSubTenant -noColor
|
||||
write-logFileTab 'Region' $sourceLocation -noColor
|
||||
|
@ -19920,9 +20218,11 @@ try {
|
|||
}
|
||||
if ($targetSub -eq $sourceSub) {
|
||||
write-logFileTab 'Subscription' 'ditto' -noColor
|
||||
write-logFileTab 'SubscriptionID' 'ditto' -noColor
|
||||
}
|
||||
else {
|
||||
write-logFileTab 'Subscription' $targetSub
|
||||
write-logFileTab 'SubscriptionID' $targetSubID
|
||||
}
|
||||
|
||||
# Target User
|
||||
|
@ -20091,31 +20391,50 @@ try {
|
|||
elseif ($patchMode) {
|
||||
test-patchMode
|
||||
get-sourceVMs
|
||||
write-logFileConfirm "Patch and Reboot VMs in resource group '$SourceRG'"
|
||||
|
||||
# check RG Owner tag
|
||||
if ($Null -eq $rgOwner) {
|
||||
write-logFileWarning "Owner tag of resource group '$sourceRG' was not set" `
|
||||
"setting it to '$setOwner'"
|
||||
$tags = $sourceRgObject.Tags
|
||||
$tags += @{Owner = $setOwner}
|
||||
Set-AzResourceGroup -Name $sourceRG -Tag $tags -ErrorAction 'SilentlyContinue' | Out-Null
|
||||
test-cmdlet 'Set-AzResourceGroup' "Could not set tag to resource group '$sourceRG'"
|
||||
}
|
||||
elseif ($rgOwner -ne $setOwner) {
|
||||
write-logFileWarning "Owner tag of resource group '$sourceRG' is not set to '$setOwner'" `
|
||||
"Current value is '$rgOwner'"
|
||||
}
|
||||
|
||||
$script:linuxVMs = @()
|
||||
# install OS patches
|
||||
$script:patchesFailed = 0
|
||||
step-patchMode
|
||||
if (!$skipPatch) {
|
||||
step-patchMode
|
||||
}
|
||||
|
||||
# install VM extensions
|
||||
$script:vmsWithNewExtension = @()
|
||||
if ($forceExtensions -and $msInternalVersion) {
|
||||
step-patchExtensions
|
||||
show-vmExtensions
|
||||
}
|
||||
|
||||
$vmsToStop = ($script:linuxVMs + $script:vmsWithNewExtension) | Sort-Object -Unique
|
||||
|
||||
# stop VMs
|
||||
if (!$stopVMsSourceRG) {
|
||||
write-logFileWarning "VMs in resource group '$sourceRG' have not been stopped" `
|
||||
"Use parameter 'stopVMsSourceRG' the next time"
|
||||
}
|
||||
else {
|
||||
write-stepStart "Stopping VMs"
|
||||
stop-VMsParallel $sourceRG $vmsToStop
|
||||
stop-VMsParallel $sourceRG $patchVMs
|
||||
}
|
||||
|
||||
# display failed OS patches
|
||||
if ($script:patchesFailed -gt 0) {
|
||||
write-logFileError "Linux patches of $($script:patchesFailed) VMs failed"
|
||||
write-logFileError "Patches of $($script:patchesFailed) VMs failed"
|
||||
}
|
||||
|
||||
write-zipFile 0
|
||||
}
|
||||
|
||||
|
@ -20174,9 +20493,7 @@ try {
|
|||
if (($sourceSubUser -ne $targetSubUser) `
|
||||
-or ($sourceSubTenant -ne $targetSubTenant)) {
|
||||
|
||||
if (!$msInternalVersion) {
|
||||
$RemoteCopyNeeded = $True
|
||||
}
|
||||
$RemoteCopyNeeded = $True
|
||||
}
|
||||
|
||||
if (!$RemoteCopyNeeded) {
|
||||
|
|
|
@ -1,4 +1,13 @@
|
|||
## Version history
|
||||
#### RGCOPY 0.9.65 September 2024
|
||||
type|change
|
||||
:---|:---
|
||||
feature|Support for IP tags.
|
||||
feature|New value `false` for parameter `setVmZone` (and `setDiskSku`). When set, the existing zone of the VM (or SKU of the disk) is not changed by RGCOPY.
|
||||
experimental feature|Patching Windows VMs (not only Linux) in PATCH mode. Set RG owner in patch mode.
|
||||
feature|Fix compatibility issue with Az version 13: Breaking change of Get-AzAccessToken.
|
||||
feature|Allow BLOB copy for Microsoft internal version of RGCOPY (using storage account network rules and delegation keys).
|
||||
|
||||
#### RGCOPY 0.9.64 June 2024
|
||||
type|change
|
||||
:---|:---
|
||||
|
|
Загрузка…
Ссылка в новой задаче