Skip to content

Instantly share code, notes, and snippets.

@gdbarron
Last active March 12, 2019 01:22
Show Gist options
  • Save gdbarron/40a05af81a12f936aaf6f68ade57f329 to your computer and use it in GitHub Desktop.
Save gdbarron/40a05af81a12f936aaf6f68ade57f329 to your computer and use it in GitHub Desktop.
Perform validation prior to moving Azure resources between resource groups or subscriptions
<#
.SYNOPSIS
Test if moving Azure resources will be successful
.DESCRIPTION
Test if moving Azure resources will be successful. Returns a boolean, but will also display an error if encountered.
.PARAMETER SourceSubscriptionId
Subscription ID where the resources are to move from
.PARAMETER SourceResourceGroup
Resource group name where the resources are to move from
.PARAMETER SourceResourceIds
Resource IDs to be moved. The resource id can typically be found on the Properties page in the portal
.PARAMETER TargetSubscriptionId
Subscription ID where the resources are to move to. If moving within the same subscription, this does not need to be provided.
.PARAMETER TargetResourceGroup
Resource group name where the resources are to move to
.EXAMPLE
.Test-AzureResourceMove.ps1 -SourceSubscriptionId '5a9601f8-83d2-4d2f-a655-24518e89e766' -SourceResourceGroup 'my_source_rg' -TargetResourceGroup 'my_target_rg' -SourceResourceIds '/subscriptions/5a9601f8-83d2-4d2f-a655-24518e89e766/resourceGroups/my_source_rg/providers/Microsoft.Compute/virtualMachines/myVM'
Test resource moves within the same subscription
.INPUTS
None
.OUTPUTS
Boolean
.NOTES
Author: Greg Brownstein
.LINK
https://gallery.technet.microsoft.com/scriptcenter/Easily-obtain-AccessToken-3ba6e593
#>
param(
[Parameter(Mandatory)]
[string] $SourceSubscriptionId,
[Parameter(Mandatory)]
[string] $SourceResourceGroup,
[Parameter(Mandatory)]
[string[]] $SourceResourceIds,
[Parameter()]
[string] $TargetSubscriptionId,
[Parameter(Mandatory)]
[string] $TargetResourceGroup
)
function Get-AzureRmCachedAccessToken() {
$ErrorActionPreference = 'Stop'
if (-not (Get-Module AzureRm.Profile)) {
Import-Module AzureRm.Profile
}
$azureRmProfileModuleVersion = (Get-Module AzureRm.Profile).Version
# refactoring performed in AzureRm.Profile v3.0 or later
if ($azureRmProfileModuleVersion.Major -ge 3) {
$azureRmProfile = [Microsoft.Azure.Commands.Common.Authentication.Abstractions.AzureRmProfileProvider]::Instance.Profile
if (-not $azureRmProfile.Accounts.Count) {
Write-Error "Ensure you have logged in before calling this function."
}
}
else {
# AzureRm.Profile < v3.0
$azureRmProfile = [Microsoft.WindowsAzure.Commands.Common.AzureRmProfileProvider]::Instance.Profile
if (-not $azureRmProfile.Context.Account.Count) {
Write-Error "Ensure you have logged in before calling this function."
}
}
$currentAzureContext = Get-AzureRmContext
$profileClient = New-Object Microsoft.Azure.Commands.ResourceManager.Common.RMProfileClient($azureRmProfile)
Write-Debug ("Getting access token for tenant" + $currentAzureContext.Subscription.TenantId)
$token = $profileClient.AcquireAccessToken($currentAzureContext.Subscription.TenantId)
$token.AccessToken
}
function Get-AzureRmBearerToken() {
$ErrorActionPreference = 'Stop'
('Bearer {0}' -f (Get-AzureRmCachedAccessToken))
}
function Get-AzCachedAccessToken() {
$ErrorActionPreference = 'Stop'
if (-not (Get-Module Az.Profile)) {
Import-Module Az.Profile
}
$azureRmProfileModuleVersion = (Get-Module Az.Profile).Version
$azureRmProfile = [Microsoft.Azure.Commands.Common.Authentication.Abstractions.AzureRmProfileProvider]::Instance.Profile
if (-not $azureRmProfile.Accounts.Count) {
Write-Error "Ensure you have logged in before calling this function."
}
$currentAzureContext = Get-AzContext
$profileClient = New-Object Microsoft.Azure.Commands.ResourceManager.Common.RMProfileClient($azureRmProfile)
Write-Debug ("Getting access token for tenant" + $currentAzureContext.Tenant.TenantId)
$token = $profileClient.AcquireAccessToken($currentAzureContext.Tenant.TenantId)
$token.AccessToken
}
function Get-AzBearerToken() {
$ErrorActionPreference = 'Stop'
('Bearer {0}' -f (Get-AzCachedAccessToken))
}
Write-Warning "Be patient, this will take some time..."
if ( -not $PSBoundParameters.ContainsKey('TargetSubscriptionId') ) {
$TargetSubscriptionId = $SourceSubscriptionId
}
Write-Verbose "Getting token"
$authHeader = Get-AzureRmBearerToken
$body = @{
'resources' = $SourceResourceIds
'targetResourceGroup' = "/subscriptions/$TargetSubscriptionId/resourceGroups/$TargetResourceGroup"
}
$params = @{
Method = 'Post'
Uri = "https://management.azure.com/subscriptions/$SourceSubscriptionId/resourceGroups/$SourceResourceGroup/validateMoveResources?api-version=2018-02-01"
Headers = @{'Authorization' = $authHeader}
ContentType = 'application/json'
Body = ($body | ConvertTo-Json)
}
$initialRequest = Invoke-WebRequest @params
$retryInSecs = $initialRequest.headers.'Retry-After'
$retryLocation = $initialRequest.headers.location
$performRetry = $false
$testPassed = $false
if ( $retryInSecs -and $retryLocation ) {
$performRetry = $true
Write-Verbose "Successfully created validation request, waiting for response from Azure"
}
$params = @{
Method = 'Get'
Uri = $initialRequest.headers.location
Headers = @{'Authorization' = $authHeader}
}
while ($performRetry) {
Write-Verbose "Sleeping $retryInSecs seconds"
Start-Sleep -Seconds $retryInSecs
try {
$response = Invoke-WebRequest @params
switch ($response.StatusCode) {
202 {
# operation still in progress
$retryInSecs = $response.headers.'Retry-After'
}
204 {
# success
$testPassed = $true
$performRetry = $false
}
Default {
# something we didn't expect
$performRetry = $false
Write-Error ($response | Out-String)
$testPassed = $false
}
}
}
catch {
$errDetails = $_.ErrorDetails.message | ConvertFrom-Json
Write-Error ('Validation failed with code {0} and message {1}' -f $errDetails.error.code, $errDetails.error.message)
$performRetry = $false
$testPassed = $false
}
}
$testPassed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment