From 9dfd03969226276803b748630abb56df2ec7ea88 Mon Sep 17 00:00:00 2001 From: Ab0907 <109592962+Ab0907@users.noreply.github.com> Date: Wed, 14 Sep 2022 11:20:20 +0530 Subject: [PATCH 1/7] Create AccountMigrationScriptOneRegionToAnother.ps1 --- ...countMigrationScriptOneRegionToAnother.ps1 | 589 ++++++++++++++++++ 1 file changed, 589 insertions(+) create mode 100644 Utility/Migration/AccountMigrationScriptOneRegionToAnother.ps1 diff --git a/Utility/Migration/AccountMigrationScriptOneRegionToAnother.ps1 b/Utility/Migration/AccountMigrationScriptOneRegionToAnother.ps1 new file mode 100644 index 0000000..5449b34 --- /dev/null +++ b/Utility/Migration/AccountMigrationScriptOneRegionToAnother.ps1 @@ -0,0 +1,589 @@ +# +.SYNOPSIS + This Script is for migration of assets from one azure automation account to another + Prerequisites: + 1. The Account to where the assets are to be migrated should exist + 2. System managed Identities is enabled in the source account involved in the migration process + 3. Source account's managed Identity has contributor access with read and write privileges to the destination account(https://docs.microsoft.com/en-us/azure/role-based-access-control/quickstart-assign-role-user-portal) + 4. For proper migration this script has to be run as a runbook on the source automation account as it requires access to assets + +.PARAMETER SourceAutomationAccountName + [Mandatory] Name of automation account from where assets need to be migrated (Source Account) + +.PARAMETER DestinationAutomationAccountName + [Mandatory] Name of automation account to where assets need to be migrated (Destination Account) + +.PARAMETER SourceResourceGroup + [Mandatory] Resource group to which the automation account from where assets need to be migrated belongs + +.PARAMETER DestinationResourceGroup + [Mandatory] Resource group to which the automation account to where assets need to be migrated belongs + +.PARAMETER SourceSubscriptionId + [Mandatory] Id of the Subscription to which the automation account from where assets need to be migrated belongs + +.PARAMETER DestinationSubscriptionId + [Mandatory] Id of the Subscription to which the automation account to where assets need to be migrated belongs + +.PARAMETER Type[] + [Mandatory] Array consisting of all the types of assets that need to be migrated, possible values are: Certificates, Connections, Credentials, Modules, Runbooks, Variables + +.NOTES + 1. Script for Migrations from-> Source account to Destination Account (will have to be created for now) + 2. Please do the following for the execution of script if source account's managed identity does not have read write access control of the destination account: + • Get into the destination account and grant access of destination account to your source account's managed identity using this guide Tutorial: https://docs.microsoft.com/en-us/azure/role-based-access-control/quickstart-assign-role-user-portal + +.AUTHOR Microsoft + +.VERSION 1.0 +#> +#Requires -module @{ ModuleName="Az.Accounts"; ModuleVersion=" 2.8.0" },@{ ModuleName="Az.Resources"; ModuleVersion=" 6.0.0" },@{ ModuleName="Az.Automation"; ModuleVersion="1.7.3" },@{ ModuleName="Az.Storage"; ModuleVersion="4.6.0" } +#Requires -psedition Core + + +$Version="1.0" +Write-Output $Version + + +try +{ + "Logging in to Azure..." + Connect-AzAccount -Identity +} +catch { + Write-Error -Message $_.Exception + throw $_.Exception +} + +$SourceAutomationAccountName +$DestinationAutomationAccountName +$SourceResourceGroup +$DestinationResourceGroup +$SourceSubscriptionId +$DestinationSubscriptionId +$SourceAutomationAcccountResourceId="/subscriptions/430eaafe-fb8f-4014-8deb-1b174430a299/resourceGroups/abhishek/providers/Microsoft.Automation/automationAccounts/MigStart" +$DestinationAutomationAcccountResourceId= "/subscriptions/1e5e7c02-d552-41bc-95fc-bdf8e8478fcf/resourceGroups/abhishek1/providers/Microsoft.Automation/automationAccounts/MigEndPoint" +$Types= @("Certificates", "Connections", "Credentials", "Modules", "Runbooks", "Variables") + +Function ParseReourceID($resourceID) +{ + $array = $resourceID.Split('/') + $indexRG = 0..($array.Length -1) | where {$array[$_] -eq 'resourcegroups'} + $indexSub = 0..($array.Length -1) | where {$array[$_] -eq 'subscriptions'} + $indexAA =0..($array.Length -1) | where {$array[$_] -eq 'automationAccounts'} + $result = $array.get($indexRG+1),$array.get($indexSub+1),$array.get($indexAA+1) + return $result +} + +Function CheckifInputIsValid($In) +{ + if ([string]::IsNullOrWhiteSpace($In)) + { + return $False + } + return $True +} + +Function Test-IsGuid +{ + [OutputType([bool])] + param + ( + [Parameter(Mandatory = $true)] + [string]$StringGuid + ) + + $ObjectGuid = [System.Guid]::empty + return [System.Guid]::TryParse($StringGuid,[System.Management.Automation.PSReference]$ObjectGuid) # Returns True if successfully parsed +} + +#Get bearer token for authentication +Function Get-AzCachedAccessToken() +{ + $token=Get-AzAccessToken + return [String]$token.Token +} + + +#Module transfer helper functions + +Function StoreModules($Modules_Custom) +{ + + Foreach($Module in $Modules_Custom) + { + + + $ModuleName = $Module + $ModulePath="C:\Modules\User\"+$ModuleName + $ModuleZipPath="C:\"+ $tempFolder+"\"+$ModuleName+".zip" + try + { + Compress-Archive -LiteralPath $ModulePath -DestinationPath $ModuleZipPath + } + catch + { + Write-Error -Message "Unable to store custom modules, error while accessing the temprary memory. Error Message: $($Error[0].Exception.Message)" + } + } + +} + + +Function CreateStorageAcc($StorageAccountName, $storageAccountRG) +{ + New-AzStorageAccount -ResourceGroupName $storageAccountRG -Name $StorageAccountName -Location westus -SkuName Standard_LRS + $storageAccountKey = (Get-AzStorageAccountKey -ResourceGroupName $storageAccountRG -AccountName $storageAccountName).Value[0] + $secureStorageAccountKey=ConvertTo-SecureString [String]$storageAccountKey -AsPlainText -Force + return $secureStorageAccountKey + +} + +Function CreateContainer($Context,$storageContainerName) +{ + New-AzStorageContainer -Name $storageContainerName -Context $Context -Permission Container +} + + +Function SendToBlob($Modules) +{ + + # Set AzStorageContext + [String]$storageAccountKey = CreateStorageAcc $StorageAccountName $storageAccountRG + if($null -ne $storageAccountKey) + { + $Context = New-AzStorageContext -StorageAccountName $storageAccountName -StorageAccountKey $storageAccountKey + try + { + CreateContainer $Context $storageContainerName + Foreach($Module in $Modules) + { + $ModuleName = $Module + $ModuleZipPath="C:\"+$tempFolder+"\"+$ModuleName+".zip" + "Retrieving files from module: $ModuleName to save to container: $storageContainerName" + Set-AzStorageBlobContent -File $ModuleZipPath -Container $storageContainerName -Context $Context + } + } + catch + { + Write-Error -Message "Unable to store custom modules, error while creating and transfering modules to temprory storage account. Error Message: $($Error[0].Exception.Message)" + } + + } + else + { + Write-Error "Unable to create a new temprory storage account for transfer of modules, please ensure you have appropriate permissions for the subscription- $SourceSubscriptionId " + } + + + +} + +Function RemoveStorageAcc($StorageAccountName, $StorageAccountRG, $SubscriptionId) +{ + Set-Context($SubscriptionId) + Remove-AzStorageAccount -ResourceGroupName $StorageAccountRG -Name $StorageAccountName +} + +#setting context +Function Set-Context($SubscriptionId) +{ + try + { + Set-AzContext -SubscriptionId $SubscriptionId + } + catch + { + Write-Error -Message $_.Exception + throw $_.Exception + } + Set-AzContext -SubscriptionId $SubscriptionId +} + +#----------------------------------------------------------------------------------------------------------------------- +# Import Asset functions + + +Function Import-RunbooksFromOldAccount{ + $Runbooks = Get-AzAutomationRunbook -ResourceGroupName $SourceResourceGroup -AutomationAccountName $SourceAutomationAccountName + if(!$?) + { + Write-Error "Failed to retrieve runbooks from automation account \' $SourceAutomationAccountName \'" + } + # $Runbooks | Export-AzAutomationRunbook -OutputFolder $LocalStoragePath -Force ; + return $Runbooks +} + +Function Import-VariablesFromOldAccount{ + $Variables=Get-AzAutomationVariable -AutomationAccountName $SourceAutomationAccountName -ResourceGroupName $SourceResourceGroup + if(!$?) + { + Write-Error "Failed to retrieve variables from automation account \' $SourceAutomationAccountName \'" + } + return $Variables +} + +Function Import-CredentialsFromOldAccount{ + $Credentials=Get-AzAutomationCredential -ResourceGroupName $SourceResourceGroup -AutomationAccountName $SourceAutomationAccountName + if(!$?) + { + Write-Error "Failed to retrieve credentials from automation account \' $SourceAutomationAccountName \'" + } + return $Credentials +} + +Function Import-CertificatesFromOldAccount +{ + $Certificates=Get-AzAutomationCertificate -ResourceGroupName $SourceResourceGroup -AutomationAccountName $SourceAutomationAccountName + if(!$?) + { + Write-Error "Failed to retrieve certificates from automation account \' $SourceAutomationAccountName \'" + } + return $Certificates +} + +Function Import-ConnectionsFromOldAccount{ + $Connections=Get-AzAutomationConnection -ResourceGroupName $SourceResourceGroup -AutomationAccountName $SourceAutomationAccountName + if(!$?) + { + Write-Error "Failed to retrieve connections from automation account \' $SourceAutomationAccountName \'" + } + return $Connections +} + +Function Import-PwshModulesFromOldAccount +{ + $AllModules= Get-AzAutomationModule -AutomationAccountName $SourceAutomationAccountName -ResourceGroupName $SourceResourceGroup + if(!$?) + { + Write-Error "Failed to retrieve modules from automation account ' $SourceAutomationAccountName '" + } + # $AllModules.name | Import-Module + $ModulesRequired = $AllModules.name + $Modules_Custom = Get-ChildItem -Path "C:\Modules\User\" | ?{$_.Attributes -eq "Directory"} | where Name -match $($ModulesRequired -join '|') + return $Modules_Custom + +} + +#------------------------------------------------------------------------------------------------------------------------------------------- + + +#Export Assets functions +Function Export-RunbooksToNewAccount($Runbooks) +{ + foreach($Runbook in $Runbooks) + { + [string]$TempName=$Runbook.Name+".*" + $CurrentFilePaths=Get-ChildItem -Path $LocalStoragePath -Filter $TempName -Recurse | %{$_.FullName} + [string]$CurrentRunbookType=$Runbook.RunbookType + if($CurrentFilePaths -ne 0) + { + if($CurrentRunbookType[0]-eq'G') + { + if($CurrentRunbookType -eq "GraphPowerShell") {$CurrentRunbookType="GraphicalPowerShell"} + else { $CurrentRunbookType="GraphicalPowerShellWorkflow"} + } + if($CurrentRunbookType -eq "PowerShell7") + { + $CurrentRunbookType="PowerShell" + } + Import-AzAutomationRunbook -Path $CurrentFilePaths -ResourceGroupName $DestinationResourceGroup -AutomationAccountName $DestinationAutomationAccountName -Type $CurrentRunbookType -Tags $Runbook.Tags -erroraction 'silentlycontinue'; + + } + else + { + Write-Error "Unable to find runbook named $Runbook.Name on the temporary storage - Reason: Issue with import of runbook $Runbook.Name from automation account $SourceAutomationAccountName" + } + + # Publish-AzAutomationRunbook -AutomationAccountName $DestinationAutomationAccountName -Name $Runbook.Name -ResourceGroupName $DestinationResourceGroup; + } +} + +Function Export-VariablesToNewAccount($Variables) +{ + foreach($Variable in $Variables) + { + [string]$VariableName=$Variable.Name + $VariableEncryption=$Variable.Encrypted + $VariableValue=Get-AutomationVariable -Name $VariableName + New-AzAutomationVariable -AutomationAccountName $DestinationAutomationAccountName -Name $VariableName -Value $VariableValue -ResourceGroupName $DestinationResourceGroup -Encrypted $VariableEncryption + + } +} + +Function Export-CredentialsToNewAccount($Credentials) +{ + foreach($Credential in $Credentials) + { + $getCredential = Get-AutomationPSCredential -Name $Credential.Name + New-AzAutomationCredential -AutomationAccountName $DestinationAutomationAccountName -Name $Credential.Name -Value $getCredential -ResourceGroupName $DestinationResourceGroup + } +} + +Function Export-ConnectionsToNewAccount($Connections) +{ + foreach($Connection in $Connections) + { + $ConnectionType=$Connection.ConnectionTypeName + $ConnectionFieldValues + $getConnection= Get-AutomationConnection $Connection.Name + if($ConnectionType -eq "AzureClassicCertificate") + { + $SubscriptionName = $getConnection.SubscriptionName + $SubscriptionId = $getConnection.SubscriptionId + $ClassicRunAsAccountCertifcateAssetName = $getConnection.CertificateAssetName + $ConnectionFieldValues = @{"SubscriptionName" = $SubscriptionName; "SubscriptionId" = $SubscriptionId; "CertificateAssetName" = $ClassicRunAsAccountCertifcateAssetName} + } + if($ConnectionType -eq "AzureServicePrincipal") + { + + $Thumbprint = $getConnection.CertificateThumbprint + $TenantId = $getConnection.TenantId + $ApplicationId = $getConnection.ApplicationId + $SubscriptionId = $getConnection.SubscriptionId + $ConnectionFieldValues = @{"ApplicationId" = $ApplicationId; "TenantId" = $TenantId; "CertificateThumbprint" = $Thumbprint; "SubscriptionId" = $SubscriptionId} + + } + + if($ConnectionType -eq "Azure") + { + $ConnectionFieldValues = @{"AutomationCertificateName"=$getConnection.AutomationCertificateName;"SubscriptionID"=$getConnection.SubscriptionId} + } + + New-AzAutomationConnection -Name $Connection.Name -ConnectionTypeName $ConnectionType -ConnectionFieldValues $ConnectionFieldValues -ResourceGroupName $DestinationResourceGroup -AutomationAccountName $DestinationAutomationAccountName + } +} + +Function Export-CertificatesToNewAccount($Certificates) +{ + foreach($Certificate in $Certificates) + { + $CertificateName=$Certificate.Name + $getCertificate=Get-AutomationCertificate -Name $CertificateName + $ASNFormatCertificate=$getCertificate.GetRawCertData() + [string]$Base64Certificate =[Convert]::ToBase64String($ASNFormatCertificate) + $bearerToken = Get-AzCachedAccessToken + if($null -ne $bearerToken) + { + $Headers = @{ + "Authorization" = "Bearer $bearerToken" + } + + $url="https://management.azure.com/subscriptions/"+$DestinationSubscriptionId+"/resourceGroups/"+$DestinationResourceGroup+"/providers/Microsoft.Automation/automationAccounts/"+$DestinationAutomationAccountName+"/certificates/"+$CertificateName+"?api-version=2019-06-01" + $properties= @{ + "base64Value"= $Base64Certificate; + "description"= $Certificate.description; + "thumbprint"= $getCertificate.Thumbprint; + "isExportable"= $Certificate.Exportable; + } + $Body = @{ + "name"= $CertificateName; + "properties"= $properties + } + $bodyjson=($Body| COnvertTo-Json) + try + { + Invoke-RestMethod -Method "PUT" -Uri "$url" -Body $bodyjson -ContentType "application/json" -Headers $Headers + } + catch{ + Write-Error -Message "Unable to import cerficate ' $CertificateName ' to account $DestinationAutomationAccountName. Error Message: $($Error[0].Exception.Message)" + } + + } + else{ + Write-Error "Unable to retrieve the authentication token for the account $DestinationAutomationAccountName" + } + } + +} + +Function Export-PwshModulesToNewAccount($Modules) +{ + Foreach($Module in $Modules) + { + $ModuleName = $Module + $BlobURL="https://"+$StorageAccountName+".blob.core.windows.net/"+$storageContainerName+"/"+$ModuleName+".zip" + New-AzAutomationModule -AutomationAccountName $DestinationAutomationAccountName -Name $ModuleName -ContentLink $BlobURL -ResourceGroupName $DestinationResourceGroup + } +} + +#--------------------------------------------------------------------------------------------------------------------------------------------------- +#Transfer function + +Function TransferRunbooks +{ + Set-Context $SourceSubscriptionId + $Runbooks = Import-RunbooksFromOldAccount + Write-Output $Runbooks + if($null -ne $Runbooks) + { + $Runbooks | Export-AzAutomationRunbook -OutputFolder $LocalStoragePath -Force + Set-Context $DestinationSubscriptionId + Export-RunbooksToNewAccount $Runbooks + } + else + { + Write-Error "Unable to find any runbooks associated with the account name $SourceAutomationAccountName" + } +} + + +Function TransferVariables +{ + Set-Context $SourceSubscriptionId + $Variables = Import-VariablesFromOldAccount + if($null -ne $Variables) + { + Set-Context $DestinationSubscriptionId + Export-VariablesToNewAccount $Variables + } + else + { + Write-Error "Unable to find any variables associated with the account name $SourceAutomationAccountName" + } +} + +Function TransferCredentials +{ + Set-Context $SourceSubscriptionId + $Credentials= Import-CredentialsFromOldAccount + if($null -ne $Credentials) + { + Set-Context $DestinationSubscriptionId + Export-CredentialsToNewAccount $Credentials + } + else + { + Write-Error "Unable to find any credentials associated with the account name $SourceAutomationAccountName" + } +} + +Function TransferConnections +{ + Set-Context $SourceSubscriptionId + $Connections=Import-ConnectionsFromOldAccount + if($null -ne $Connections) + { + Set-Context $DestinationSubscriptionId + Export-ConnectionsToNewAccount $Connections + } + else + { + Write-Error "Unable to find any connections associated with the account name $SourceAutomationAccountName" + } + +} + +Function TransferCertificates +{ + Set-Context $SourceSubscriptionId + $Certificates=Import-CertificatesFromOldAccount + if($null -ne $Certificates) + { + Set-Context $DestinationSubscriptionId + Export-CertificatesToNewAccount $Certificates + } + else + { + Write-Error "Unable to find any certificates associated with the account name $SourceAutomationAccountName" + } +} + +Function TransferModules +{ + Set-AzContext -SubscriptionId $SourceSubscriptionId + New-Item -Path "C:\$tempFolder" -ItemType Directory + $modules=Import-PwshModulesFromOldAccount + if($null -ne $modules) + { + StoreModules $modules + SendToBlob $modules + Set-AzContext -SubscriptionId $DestinationSubscriptionId + try + { + Export-PwshModulesToNewAccount $modules + } + catch + { + Write-Error -Message "Unable to transfer modules to account $DestinationAutomationAccountName. Error Message: $($Error[0].Exception.Message)" + } + RemoveStorageAcc $storageAccountName $storageAccountRG $subscriptionId + } + else + { + Write-Error "Unable to find any powershell modules associated with the account name $SourceAutomationAccountName" + } +} + +# Start point for the script +if($SourceAutomationAcccountResourceId -ne $null) +{ + $parsedResourceID=ParseReourceID $SourceAutomationAcccountResourceId + $SourceResourceGroup=$parsedResourceID[0] + $SourceSubscriptionId=$parsedResourceID[1] + $SourceAutomationAccountName=$parsedResourceID[2] +} + +if($DestinationAutomationAcccountResourceId -ne $null) +{ + $parsedResourceID=ParseReourceID $DestinationAutomationAcccountResourceId + $DestinationResourceGroup=$parsedResourceID[0] + $DestinationSubscriptionId=$parsedResourceID[1] + $DestinationAutomationAccountName=$parsedResourceID[2] +} + +$LocalStoragePath= ".\" +$subscriptionId = $SourceSubscriptionId +$storageAccountRG = $SourceResourceGroup +$storageAccountName = "migrationacctemp1" +$storageContainerName = "migrationcontainertemp1" +$tempFolder="LocalTempFolder1" + +if(CheckifInputIsValid($SourceAutomationAccountName) -and CheckifInputIsValid($SourceResourceGroup) -and CheckifInputIsValid($SourceSubscriptionId) -and CheckifInputIsValid($DestinationAutomationAccountName) -and CheckifInputIsValid($DestinationResourceGroup) -and CheckifInputIsValid($DestinationSubscriptionId)) +{ + if((Test-IsGuid $SourceSubscriptionId) -and (Test-IsGuid $DestinationSubscriptionId)) + { + foreach($assestType in $Types) + { + if($assestType -eq "Runbooks") + { + TransferRunbooks + } + elseif($assestType -eq "Variables") + { + TransferVariables + } + elseif($assestType -eq "Connections") + { + TransferConnections + } + elseif($assestType -eq "Credentials") + { + TransferCredentials + } + elseif($assestType -eq "Certificates") + { + TransferCertificates + } + elseif($assestType -eq "Modules") + { + TransferModules + } + else{ + Write-Error "Please enter a valid type as $assestType is not a valid option, acceptable options are: Certificates, Connections, Credentials, Modules, Runbooks, Variables" + } + + } + + } + else + { + Write-Error "Please enter valid Source and Destination subscription IDs" + } +} +else +{ + Write-Error "Please enter valid Inputs(either Source and Destination Resource IDs or Source and Destination Subscription IDs, Resource Group names and Automation account names)" +} + + From 8f03cccf30b91b14d865667697d81f2d61cb2000 Mon Sep 17 00:00:00 2001 From: Ab0907 <109592962+Ab0907@users.noreply.github.com> Date: Fri, 16 Sep 2022 18:10:39 +0530 Subject: [PATCH 2/7] Updated documentation --- ...countMigrationScriptOneRegionToAnother.ps1 | 32 ++++++++----------- 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/Utility/Migration/AccountMigrationScriptOneRegionToAnother.ps1 b/Utility/Migration/AccountMigrationScriptOneRegionToAnother.ps1 index 5449b34..0ece6a0 100644 --- a/Utility/Migration/AccountMigrationScriptOneRegionToAnother.ps1 +++ b/Utility/Migration/AccountMigrationScriptOneRegionToAnother.ps1 @@ -1,37 +1,33 @@ # .SYNOPSIS - This Script is for migration of assets from one azure automation account to another - Prerequisites: - 1. The Account to where the assets are to be migrated should exist - 2. System managed Identities is enabled in the source account involved in the migration process - 3. Source account's managed Identity has contributor access with read and write privileges to the destination account(https://docs.microsoft.com/en-us/azure/role-based-access-control/quickstart-assign-role-user-portal) - 4. For proper migration this script has to be run as a runbook on the source automation account as it requires access to assets + This PowerShell script is for migration of Automation account assets from the account in primary region to the account in secondary region. This script migrates only Runbooks, Modules, Connections, Credentials, Certificates and Variables. + + Prerequisites: + 1.Ensure that the Automation account in the secondary region is created and available so that assets from primary region can be migrated to it. + 2.System Managed Identities should be enabled in the Automation account in the primary region. + 3.Ensure that Primary Automation account's Managed Identity has Contributor access with read and write permissions to the Automation account in secondary region. You can enable it by providing the necessary permissions in Secondary Automation account’s managed identities. Learn more + 4.This script requires access to Automation account assets in primary region. Hence, it should be executed as a runbook in that Automation account for successful migration. .PARAMETER SourceAutomationAccountName - [Mandatory] Name of automation account from where assets need to be migrated (Source Account) + Name of automation account from where assets need to be migrated (Source Account) .PARAMETER DestinationAutomationAccountName - [Mandatory] Name of automation account to where assets need to be migrated (Destination Account) + Name of automation account to where assets need to be migrated (Destination Account) .PARAMETER SourceResourceGroup - [Mandatory] Resource group to which the automation account from where assets need to be migrated belongs + Resource group to which the automation account from where assets need to be migrated belongs .PARAMETER DestinationResourceGroup - [Mandatory] Resource group to which the automation account to where assets need to be migrated belongs + Resource group to which the automation account to where assets need to be migrated belongs .PARAMETER SourceSubscriptionId - [Mandatory] Id of the Subscription to which the automation account from where assets need to be migrated belongs + Id of the Subscription to which the automation account from where assets need to be migrated belongs .PARAMETER DestinationSubscriptionId - [Mandatory] Id of the Subscription to which the automation account to where assets need to be migrated belongs + Id of the Subscription to which the automation account to where assets need to be migrated belongs .PARAMETER Type[] - [Mandatory] Array consisting of all the types of assets that need to be migrated, possible values are: Certificates, Connections, Credentials, Modules, Runbooks, Variables - -.NOTES - 1. Script for Migrations from-> Source account to Destination Account (will have to be created for now) - 2. Please do the following for the execution of script if source account's managed identity does not have read write access control of the destination account: - • Get into the destination account and grant access of destination account to your source account's managed identity using this guide Tutorial: https://docs.microsoft.com/en-us/azure/role-based-access-control/quickstart-assign-role-user-portal + Array consisting of all the types of assets that need to be migrated, possible values are: Certificates, Connections, Credentials, Modules, Runbooks, Variables .AUTHOR Microsoft From 9e0921a060fd346d88a4378ac3e20766368f66fa Mon Sep 17 00:00:00 2001 From: Ab0907 <109592962+Ab0907@users.noreply.github.com> Date: Fri, 16 Sep 2022 18:13:11 +0530 Subject: [PATCH 3/7] added params --- ...countMigrationScriptOneRegionToAnother.ps1 | 37 ++++++++----------- 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/Utility/Migration/AccountMigrationScriptOneRegionToAnother.ps1 b/Utility/Migration/AccountMigrationScriptOneRegionToAnother.ps1 index 0ece6a0..acb53c2 100644 --- a/Utility/Migration/AccountMigrationScriptOneRegionToAnother.ps1 +++ b/Utility/Migration/AccountMigrationScriptOneRegionToAnother.ps1 @@ -8,27 +8,22 @@ 3.Ensure that Primary Automation account's Managed Identity has Contributor access with read and write permissions to the Automation account in secondary region. You can enable it by providing the necessary permissions in Secondary Automation account’s managed identities. Learn more 4.This script requires access to Automation account assets in primary region. Hence, it should be executed as a runbook in that Automation account for successful migration. -.PARAMETER SourceAutomationAccountName - Name of automation account from where assets need to be migrated (Source Account) - -.PARAMETER DestinationAutomationAccountName - Name of automation account to where assets need to be migrated (Destination Account) - -.PARAMETER SourceResourceGroup - Resource group to which the automation account from where assets need to be migrated belongs - -.PARAMETER DestinationResourceGroup - Resource group to which the automation account to where assets need to be migrated belongs - -.PARAMETER SourceSubscriptionId - Id of the Subscription to which the automation account from where assets need to be migrated belongs - -.PARAMETER DestinationSubscriptionId - Id of the Subscription to which the automation account to where assets need to be migrated belongs - -.PARAMETER Type[] - Array consisting of all the types of assets that need to be migrated, possible values are: Certificates, Connections, Credentials, Modules, Runbooks, Variables - + .PARAMETER SourceAutomationAccountName + [Optional] Name of automation account from where assets need to be migrated (Source Account) + .PARAMETER DestinationAutomationAccountName + [Optional] Name of automation account to where assets need to be migrated (Destination Account) + .PARAMETER SourceResourceGroup + [Optional] Resource group to which the automation account from where assets need to be migrated + .PARAMETER DestinationResourceGroup + [Optional] Resource group to which the automation account to where assets need to be migrated + .PARAMETER SourceSubscriptionId + [Optional] Id of the Subscription to which the automation account from where assets need to be migrated + .PARAMETER DestinationSubscriptionId + [Optional] Id of the Subscription to which the automation account to where assets need to be migrated + .PARAMETER SourceAutomationAccountResourceId + [Optional] Resource Id of the automation account from where assets need to be migrated + .PARAMETER DestinationAutomationAccountResourceId + [Optional] Resource Id of the automation account to where assets need to be migrated .AUTHOR Microsoft .VERSION 1.0 From 98e0fa184c5aa9a7172b99caca2f744761d7ceab Mon Sep 17 00:00:00 2001 From: Ab0907 <109592962+Ab0907@users.noreply.github.com> Date: Fri, 16 Sep 2022 18:16:57 +0530 Subject: [PATCH 4/7] adding type --- Utility/Migration/AccountMigrationScriptOneRegionToAnother.ps1 | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Utility/Migration/AccountMigrationScriptOneRegionToAnother.ps1 b/Utility/Migration/AccountMigrationScriptOneRegionToAnother.ps1 index acb53c2..addef6e 100644 --- a/Utility/Migration/AccountMigrationScriptOneRegionToAnother.ps1 +++ b/Utility/Migration/AccountMigrationScriptOneRegionToAnother.ps1 @@ -24,6 +24,9 @@ [Optional] Resource Id of the automation account from where assets need to be migrated .PARAMETER DestinationAutomationAccountResourceId [Optional] Resource Id of the automation account to where assets need to be migrated + .PARAMETER Type[] + [Mandatory] Array consisting of all the types of assets that need to be migrated, possible values are Certificates, Connections, Credentials, Modules, Runbooks, Variables + .AUTHOR Microsoft .VERSION 1.0 From 7f25495b68457fadd6a8b57739facd33d4344333 Mon Sep 17 00:00:00 2001 From: Ab0907 <109592962+Ab0907@users.noreply.github.com> Date: Tue, 20 Sep 2022 12:35:35 +0530 Subject: [PATCH 5/7] Update AccountMigrationScriptOneRegionToAnother.ps1 --- ...countMigrationScriptOneRegionToAnother.ps1 | 220 +++++++++++------- 1 file changed, 131 insertions(+), 89 deletions(-) diff --git a/Utility/Migration/AccountMigrationScriptOneRegionToAnother.ps1 b/Utility/Migration/AccountMigrationScriptOneRegionToAnother.ps1 index addef6e..9c32c07 100644 --- a/Utility/Migration/AccountMigrationScriptOneRegionToAnother.ps1 +++ b/Utility/Migration/AccountMigrationScriptOneRegionToAnother.ps1 @@ -1,31 +1,37 @@ -# +<# .SYNOPSIS - This PowerShell script is for migration of Automation account assets from the account in primary region to the account in secondary region. This script migrates only Runbooks, Modules, Connections, Credentials, Certificates and Variables. - - Prerequisites: - 1.Ensure that the Automation account in the secondary region is created and available so that assets from primary region can be migrated to it. - 2.System Managed Identities should be enabled in the Automation account in the primary region. - 3.Ensure that Primary Automation account's Managed Identity has Contributor access with read and write permissions to the Automation account in secondary region. You can enable it by providing the necessary permissions in Secondary Automation account’s managed identities. Learn more - 4.This script requires access to Automation account assets in primary region. Hence, it should be executed as a runbook in that Automation account for successful migration. - - .PARAMETER SourceAutomationAccountName - [Optional] Name of automation account from where assets need to be migrated (Source Account) - .PARAMETER DestinationAutomationAccountName - [Optional] Name of automation account to where assets need to be migrated (Destination Account) - .PARAMETER SourceResourceGroup - [Optional] Resource group to which the automation account from where assets need to be migrated - .PARAMETER DestinationResourceGroup - [Optional] Resource group to which the automation account to where assets need to be migrated - .PARAMETER SourceSubscriptionId - [Optional] Id of the Subscription to which the automation account from where assets need to be migrated - .PARAMETER DestinationSubscriptionId - [Optional] Id of the Subscription to which the automation account to where assets need to be migrated - .PARAMETER SourceAutomationAccountResourceId - [Optional] Resource Id of the automation account from where assets need to be migrated - .PARAMETER DestinationAutomationAccountResourceId - [Optional] Resource Id of the automation account to where assets need to be migrated - .PARAMETER Type[] - [Mandatory] Array consisting of all the types of assets that need to be migrated, possible values are Certificates, Connections, Credentials, Modules, Runbooks, Variables + This Script is for migration of assets from one azure automation account to another + Prerequisites: + 1. The Account to where the assets are to be migrated should exist + 2. System managed Identities is enabled in the source account involved in the migration process + 3. Source account's managed Identity has contributor access with read and write privileges to the destination account(https://docs.microsoft.com/en-us/azure/role-based-access-control/quickstart-assign-role-user-portal) + 4. For proper migration this script has to be run as a runbook on the source automation account as it requires access to assets + +.PARAMETER SourceAutomationAccountName + [Mandatory] Name of automation account from where assets need to be migrated (Source Account) + +.PARAMETER DestinationAutomationAccountName + [Mandatory] Name of automation account to where assets need to be migrated (Destination Account) + +.PARAMETER SourceResourceGroup + [Mandatory] Resource group to which the automation account from where assets need to be migrated belongs + +.PARAMETER DestinationResourceGroup + [Mandatory] Resource group to which the automation account to where assets need to be migrated belongs + +.PARAMETER SourceSubscriptionId + [Mandatory] Id of the Subscription to which the automation account from where assets need to be migrated belongs + +.PARAMETER DestinationSubscriptionId + [Mandatory] Id of the Subscription to which the automation account to where assets need to be migrated belongs + +.PARAMETER Type[] + [Mandatory] Array consisting of all the types of assets that need to be migrated, possible values are: Certificates, Connections, Credentials, Modules, Runbooks, Variables + +.NOTES + 1. Script for Migrations from-> Source account to Destination Account (will have to be created for now) + 2. Please do the following for the execution of script if source account's managed identity does not have read write access control of the destination account: + • Get into the destination account and grant access of destination account to your source account's managed identity using this guide Tutorial: https://docs.microsoft.com/en-us/azure/role-based-access-control/quickstart-assign-role-user-portal .AUTHOR Microsoft @@ -49,16 +55,18 @@ catch { throw $_.Exception } -$SourceAutomationAccountName -$DestinationAutomationAccountName -$SourceResourceGroup -$DestinationResourceGroup -$SourceSubscriptionId -$DestinationSubscriptionId -$SourceAutomationAcccountResourceId="/subscriptions/430eaafe-fb8f-4014-8deb-1b174430a299/resourceGroups/abhishek/providers/Microsoft.Automation/automationAccounts/MigStart" -$DestinationAutomationAcccountResourceId= "/subscriptions/1e5e7c02-d552-41bc-95fc-bdf8e8478fcf/resourceGroups/abhishek1/providers/Microsoft.Automation/automationAccounts/MigEndPoint" +$SourceAutomationAccountName= +$DestinationAutomationAccountName= +$SourceResourceGroup= +$DestinationResourceGroup= +$SourceSubscriptionId= +$DestinationSubscriptionId= +$SourceAutomationAcccountResourceId= +$DestinationAutomationAcccountResourceId= + $Types= @("Certificates", "Connections", "Credentials", "Modules", "Runbooks", "Variables") + Function ParseReourceID($resourceID) { $array = $resourceID.Split('/') @@ -69,33 +77,50 @@ Function ParseReourceID($resourceID) return $result } +Function RandomStringProducer +{ + $TokenSet = @{ + L = [Char[]]'abcdefghijklmnopqrstuvwxyz' + N = [Char[]]'0123456789' + } + + + $Lower = Get-Random -Count 10 -InputObject $TokenSet.L + $Number = Get-Random -Count 10 -InputObject $TokenSet.N + + + $StringSet = $Lower + $Number + $RandomString=(Get-Random -Count 15 -InputObject $StringSet) -join '' + return $RandomString +} + Function CheckifInputIsValid($In) { if ([string]::IsNullOrWhiteSpace($In)) - { - return $False - } + { + return $False + } return $True } Function Test-IsGuid { - [OutputType([bool])] - param - ( - [Parameter(Mandatory = $true)] - [string]$StringGuid - ) - - $ObjectGuid = [System.Guid]::empty - return [System.Guid]::TryParse($StringGuid,[System.Management.Automation.PSReference]$ObjectGuid) # Returns True if successfully parsed + [OutputType([bool])] + param + ( + [Parameter(Mandatory = $true)] + [string]$StringGuid + ) + +$ObjectGuid = [System.Guid]::empty +return [System.Guid]::TryParse($StringGuid,[System.Management.Automation.PSReference]$ObjectGuid) # Returns True if successfully parsed } #Get bearer token for authentication Function Get-AzCachedAccessToken() { $token=Get-AzAccessToken - return [String]$token.Token + return [String]$token.Token } @@ -123,13 +148,26 @@ Function StoreModules($Modules_Custom) } +Function ValidateDestinationSubId($SubscriptionId) +{ + $SubscriptionsFullDetails=Get-AzSubscription + $SubIds=$SubscriptionsFullDetails.SubscriptionId + foreach($sub in $SubIds ) + { + if($sub -eq $SubscriptionId) + { + return $true + } + } + return $false + +} Function CreateStorageAcc($StorageAccountName, $storageAccountRG) { New-AzStorageAccount -ResourceGroupName $storageAccountRG -Name $StorageAccountName -Location westus -SkuName Standard_LRS $storageAccountKey = (Get-AzStorageAccountKey -ResourceGroupName $storageAccountRG -AccountName $storageAccountName).Value[0] - $secureStorageAccountKey=ConvertTo-SecureString [String]$storageAccountKey -AsPlainText -Force - return $secureStorageAccountKey + return $storageAccountKey } @@ -176,7 +214,7 @@ Function SendToBlob($Modules) Function RemoveStorageAcc($StorageAccountName, $StorageAccountRG, $SubscriptionId) { Set-Context($SubscriptionId) - Remove-AzStorageAccount -ResourceGroupName $StorageAccountRG -Name $StorageAccountName + Remove-AzStorageAccount -ResourceGroupName $StorageAccountRG -Name $StorageAccountName -Force } #setting context @@ -204,7 +242,6 @@ Function Import-RunbooksFromOldAccount{ { Write-Error "Failed to retrieve runbooks from automation account \' $SourceAutomationAccountName \'" } - # $Runbooks | Export-AzAutomationRunbook -OutputFolder $LocalStoragePath -Force ; return $Runbooks } @@ -408,7 +445,6 @@ Function TransferRunbooks { Set-Context $SourceSubscriptionId $Runbooks = Import-RunbooksFromOldAccount - Write-Output $Runbooks if($null -ne $Runbooks) { $Runbooks | Export-AzAutomationRunbook -OutputFolder $LocalStoragePath -Force @@ -509,18 +545,17 @@ Function TransferModules } } -# Start point for the script -if($SourceAutomationAcccountResourceId -ne $null) +if($SourceAutomationAccountResourceId.Length -ne 0) { - $parsedResourceID=ParseReourceID $SourceAutomationAcccountResourceId + $parsedResourceID=ParseReourceID $SourceAutomationAccountResourceId $SourceResourceGroup=$parsedResourceID[0] $SourceSubscriptionId=$parsedResourceID[1] $SourceAutomationAccountName=$parsedResourceID[2] } -if($DestinationAutomationAcccountResourceId -ne $null) +if($DestinationAutomationAccountResourceId.Length -ne 0) { - $parsedResourceID=ParseReourceID $DestinationAutomationAcccountResourceId + $parsedResourceID=ParseReourceID $DestinationAutomationAccountResourceId $DestinationResourceGroup=$parsedResourceID[0] $DestinationSubscriptionId=$parsedResourceID[1] $DestinationAutomationAccountName=$parsedResourceID[2] @@ -529,44 +564,52 @@ if($DestinationAutomationAcccountResourceId -ne $null) $LocalStoragePath= ".\" $subscriptionId = $SourceSubscriptionId $storageAccountRG = $SourceResourceGroup -$storageAccountName = "migrationacctemp1" -$storageContainerName = "migrationcontainertemp1" -$tempFolder="LocalTempFolder1" +$storageAccountName = RandomStringProducer +$storageContainerName = "migrationcontainer" +$tempFolder=RandomStringProducer + +$Access=0 if(CheckifInputIsValid($SourceAutomationAccountName) -and CheckifInputIsValid($SourceResourceGroup) -and CheckifInputIsValid($SourceSubscriptionId) -and CheckifInputIsValid($DestinationAutomationAccountName) -and CheckifInputIsValid($DestinationResourceGroup) -and CheckifInputIsValid($DestinationSubscriptionId)) { if((Test-IsGuid $SourceSubscriptionId) -and (Test-IsGuid $DestinationSubscriptionId)) { - foreach($assestType in $Types) + if(ValidateDestinationSubId $DestinationSubscriptionId) { - if($assestType -eq "Runbooks") - { - TransferRunbooks - } - elseif($assestType -eq "Variables") - { - TransferVariables - } - elseif($assestType -eq "Connections") - { - TransferConnections - } - elseif($assestType -eq "Credentials") - { - TransferCredentials - } - elseif($assestType -eq "Certificates") + foreach($assestType in $Types) { - TransferCertificates - } - elseif($assestType -eq "Modules") - { - TransferModules - } - else{ - Write-Error "Please enter a valid type as $assestType is not a valid option, acceptable options are: Certificates, Connections, Credentials, Modules, Runbooks, Variables" - } + if($assestType -eq "Runbooks") + { + TransferRunbooks + } + elseif($assestType -eq "Variables") + { + TransferVariables + } + elseif($assestType -eq "Connections") + { + TransferConnections + } + elseif($assestType -eq "Credentials") + { + TransferCredentials + } + elseif($assestType -eq "Certificates") + { + TransferCertificates + } + elseif($assestType -eq "Modules") + { + TransferModules + } + else{ + Write-Error "Please enter a valid type as $assestType is not a valid option, acceptable options are: Certificates, Connections, Credentials, Modules, Runbooks, Variables" + } + } + } + else{ + Write-Error "Please ensure source account's managed Identity has contributor access with read and write privileges to the destination account(https://docs.microsoft.com/en-us/azure/role-based-access-control/quickstart-assign-role-user-portal)" } } @@ -580,4 +623,3 @@ else Write-Error "Please enter valid Inputs(either Source and Destination Resource IDs or Source and Destination Subscription IDs, Resource Group names and Automation account names)" } - From 59c366e6ff81335fe94b01f18906cc215f7b7d83 Mon Sep 17 00:00:00 2001 From: Ab0907 <109592962+Ab0907@users.noreply.github.com> Date: Tue, 20 Sep 2022 12:35:57 +0530 Subject: [PATCH 6/7] Update AccountMigrationScriptOneRegionToAnother.ps1 --- Utility/Migration/AccountMigrationScriptOneRegionToAnother.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Utility/Migration/AccountMigrationScriptOneRegionToAnother.ps1 b/Utility/Migration/AccountMigrationScriptOneRegionToAnother.ps1 index 9c32c07..b329fc8 100644 --- a/Utility/Migration/AccountMigrationScriptOneRegionToAnother.ps1 +++ b/Utility/Migration/AccountMigrationScriptOneRegionToAnother.ps1 @@ -1,4 +1,4 @@ -<# + <# .SYNOPSIS This Script is for migration of assets from one azure automation account to another Prerequisites: From 2093859c96c7f441babbd5c03abf129478c6440c Mon Sep 17 00:00:00 2001 From: Ab0907 <109592962+Ab0907@users.noreply.github.com> Date: Tue, 20 Sep 2022 12:57:03 +0530 Subject: [PATCH 7/7] Update AccountMigrationScriptOneRegionToAnother.ps1 --- ...countMigrationScriptOneRegionToAnother.ps1 | 34 ++++++++++++------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/Utility/Migration/AccountMigrationScriptOneRegionToAnother.ps1 b/Utility/Migration/AccountMigrationScriptOneRegionToAnother.ps1 index b329fc8..6fbd324 100644 --- a/Utility/Migration/AccountMigrationScriptOneRegionToAnother.ps1 +++ b/Utility/Migration/AccountMigrationScriptOneRegionToAnother.ps1 @@ -1,35 +1,43 @@ <# .SYNOPSIS - This Script is for migration of assets from one azure automation account to another - Prerequisites: - 1. The Account to where the assets are to be migrated should exist - 2. System managed Identities is enabled in the source account involved in the migration process - 3. Source account's managed Identity has contributor access with read and write privileges to the destination account(https://docs.microsoft.com/en-us/azure/role-based-access-control/quickstart-assign-role-user-portal) - 4. For proper migration this script has to be run as a runbook on the source automation account as it requires access to assets + This PowerShell script is for migration of Automation account assets from the account in primary region to the account in secondary region. This script migrates only Runbooks, Modules, Connections, Credentials, Certificates and Variables. + + Prerequisites: + 1.Ensure that the Automation account in the secondary region is created and available so that assets from primary region can be migrated to it. + 2.System Managed Identities should be enabled in the Automation account in the primary region. + 3.Ensure that Primary Automation account's Managed Identity has Contributor access with read and write permissions to the Automation account in secondary region. You can enable it by providing the necessary permissions in Secondary Automation account’s managed identities. Learn more + 4.This script requires access to Automation account assets in primary region. Hence, it should be executed as a runbook in that Automation account for successful migration. + .PARAMETER SourceAutomationAccountName - [Mandatory] Name of automation account from where assets need to be migrated (Source Account) + [Optional] Name of automation account from where assets need to be migrated (Source Account) .PARAMETER DestinationAutomationAccountName - [Mandatory] Name of automation account to where assets need to be migrated (Destination Account) + [Optional] Name of automation account to where assets need to be migrated (Destination Account) .PARAMETER SourceResourceGroup - [Mandatory] Resource group to which the automation account from where assets need to be migrated belongs + [Optional] Resource group to which the automation account from where assets need to be migrated .PARAMETER DestinationResourceGroup - [Mandatory] Resource group to which the automation account to where assets need to be migrated belongs + [Optional] Resource group to which the automation account to where assets need to be migrated .PARAMETER SourceSubscriptionId - [Mandatory] Id of the Subscription to which the automation account from where assets need to be migrated belongs + [Optional] Id of the Subscription to which the automation account from where assets need to be migrated .PARAMETER DestinationSubscriptionId - [Mandatory] Id of the Subscription to which the automation account to where assets need to be migrated belongs + [Optional] Id of the Subscription to which the automation account to where assets need to be migrated + +.PARAMETER SourceAutomationAccountResourceId + [Optional] Resource Id of the automation account from where assets need to be migrated + +.PARAMETER DestinationAutomationAccountResourceId + [Optional] Resource Id of the automation account to where assets need to be migrated .PARAMETER Type[] [Mandatory] Array consisting of all the types of assets that need to be migrated, possible values are: Certificates, Connections, Credentials, Modules, Runbooks, Variables .NOTES - 1. Script for Migrations from-> Source account to Destination Account (will have to be created for now) + 1. Script for Migrations from-> Source account to Destination Account (will have to be created for now) 2. Please do the following for the execution of script if source account's managed identity does not have read write access control of the destination account: • Get into the destination account and grant access of destination account to your source account's managed identity using this guide Tutorial: https://docs.microsoft.com/en-us/azure/role-based-access-control/quickstart-assign-role-user-portal