293 строки
12 KiB
PowerShell
293 строки
12 KiB
PowerShell
[CmdletBinding()]
|
|
Param(
|
|
[ValidateScript({
|
|
if(-Not ($_ | Test-Path) ){
|
|
throw "File or folder does not exist"
|
|
}
|
|
if(-Not ($_ | Test-Path -PathType Leaf) ){
|
|
throw "The Path argument must be a file. Folder paths are not allowed."
|
|
}
|
|
if($_ -notmatch "(\.yaml)"){
|
|
throw "The config file must be of type 'yaml'"
|
|
}
|
|
return $true
|
|
})]
|
|
[System.IO.FileInfo]$config,
|
|
[string]$location,
|
|
[string]$applianceSubscriptionId,
|
|
[string]$applianceResourceGroupName,
|
|
[string]$applianceName,
|
|
[string]$customLocationSubscriptionId,
|
|
[string]$customLocationResourceGroupName,
|
|
[string]$customLocationName,
|
|
[string]$vCenterSubscriptionId,
|
|
[string]$vCenterResourceGroupName,
|
|
[string]$vCenterName
|
|
)
|
|
|
|
# Start Region: Set user inputs
|
|
|
|
if (!$location)
|
|
{$location = Read-Host -Prompt 'Enter region of your current Arc resource bridge'}
|
|
|
|
if (!$applianceSubscriptionId)
|
|
{$applianceSubscriptionId = Read-Host -Prompt 'Enter the Subscription ID of your Arc resource bridge'}
|
|
|
|
if (!$applianceResourceGroupName)
|
|
{$applianceResourceGroupName = Read-Host -Prompt 'Enter the Resource Group name of your Arc resource bridge'}
|
|
|
|
if (!$applianceName)
|
|
{$applianceName = Read-Host -Prompt 'Enter the original name in Azure of your current Arc resource bridge'}
|
|
|
|
if (!$customLocationSubscriptionId)
|
|
{$customLocationSubscriptionId = Read-Host -Prompt 'Enter the Subscription ID of your custom location'}
|
|
|
|
if (!$customLocationResourceGroupName)
|
|
{$customLocationResourceGroupName = Read-Host -Prompt 'Enter the Resource Group name of your custom location'}
|
|
|
|
# TODO: Allow for multiple custom location inputs
|
|
if (!$customLocationName)
|
|
{$customLocationName = Read-Host -Prompt 'Enter the current name in Azure of your custom location'}
|
|
|
|
if (!$vCenterSubscriptionId)
|
|
{$vCenterSubscriptionId = Read-Host -Prompt 'Enter the Subscription ID of your vCenter resource in Azure'}
|
|
|
|
if (!$vCenterResourceGroupName)
|
|
{$vCenterResourceGroupName = Read-Host -Prompt 'Enter the Resource Group name of your vCenter resource in Azure'}
|
|
|
|
if (!$vCenterName)
|
|
{$vCenterName = Read-Host -Prompt 'Enter the current name of your vCenter resource in Azure'}
|
|
|
|
# End Region: Set user inputs
|
|
|
|
function confirmationPrompt($msg) {
|
|
Write-Host $msg
|
|
while ($true) {
|
|
$inp = Read-Host "Yes(y)/No(n)?"
|
|
$inp = $inp.ToLower()
|
|
if ($inp -eq 'y' -or $inp -eq 'yes') {
|
|
return $true
|
|
}
|
|
elseif ($inp -eq 'n' -or $inp -eq 'no') {
|
|
return $false
|
|
}
|
|
}
|
|
}
|
|
|
|
$logFile = "arcvmware-output.log"
|
|
|
|
function logH1($msg) {
|
|
$pattern = '0-' * 40
|
|
$spaces = ' ' * (40 - $msg.length / 2)
|
|
$nl = [Environment]::NewLine
|
|
$msgFull = "$nl $nl $pattern $nl $spaces $msg $nl $pattern $nl"
|
|
Write-Host -ForegroundColor Green $msgFull
|
|
Write-Output $msgFull >> $logFile
|
|
}
|
|
|
|
function logH2($msg) {
|
|
$msgFull = "==> $msg"
|
|
Write-Host -ForegroundColor Magenta $msgFull
|
|
Write-Output $msgFull >> $logFile
|
|
}
|
|
|
|
function logText($msg) {
|
|
Write-Host "$msg"
|
|
Write-Output "$msg" >> $logFile
|
|
}
|
|
|
|
function createRG($subscriptionId, $rgName) {
|
|
$group = (az group show --subscription $subscriptionId -n $rgName)
|
|
if (!$group) {
|
|
$Error[0] | Out-String >> $logFile
|
|
throw "Resource Group $rgName does not exist in subscription $subscriptionId."
|
|
|
|
# TODO: Figure out if we can get the rg name from the appliance ARM ID
|
|
}
|
|
}
|
|
|
|
|
|
logH1 "Step 1/5: Setting up the current workstation"
|
|
|
|
if (!$UseProxy -and (confirmationPrompt -msg "Is the current workstation behind a proxy?")) {
|
|
$UseProxy = $true
|
|
}
|
|
|
|
Write-Host "Setting the TLS Protocol for the current session to TLS 1.2."
|
|
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
|
|
|
|
$proxyCA = ""
|
|
|
|
if ($UseProxy) {
|
|
logH2 "Provide proxy details"
|
|
$proxyURL = Read-Host "Proxy URL"
|
|
if ($proxyURL.StartsWith("http") -ne $true) {
|
|
$proxyURL = "http://$proxyURL"
|
|
}
|
|
|
|
$noProxy = Read-Host "No Proxy (comma separated)"
|
|
|
|
$env:http_proxy = $proxyURL
|
|
$env:HTTP_PROXY = $proxyURL
|
|
$env:https_proxy = $proxyURL
|
|
$env:HTTPS_PROXY = $proxyURL
|
|
$env:no_proxy = $noProxy
|
|
$env:NO_PROXY = $noProxy
|
|
|
|
$proxyCA = Read-Host "Proxy CA cert path (Press enter to skip)"
|
|
if ($proxyCA -ne "") {
|
|
$proxyCA = Resolve-Path -Path $proxyCA
|
|
}
|
|
|
|
$credential = $null
|
|
$proxyAddr = $proxyURL
|
|
|
|
if ($proxyURL.Contains("@")) {
|
|
$x = $proxyURL.Split("//")
|
|
$proto = $x[0]
|
|
$x = $x[2].Split("@")
|
|
$userPass = $x[0]
|
|
$proxyAddr = $proto + "//" + $x[1]
|
|
$x = $userPass.Split(":")
|
|
$proxyUsername = $x[0]
|
|
$proxyPassword = $x[1]
|
|
$password = ConvertTo-SecureString -String $proxyPassword -AsPlainText -Force
|
|
$credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $proxyUsername, $password
|
|
}
|
|
|
|
[system.net.webrequest]::defaultwebproxy = new-object system.net.webproxy($proxyAddr)
|
|
[system.net.webrequest]::defaultwebproxy.credentials = $credential
|
|
[system.net.webrequest]::defaultwebproxy.BypassProxyOnLocal = $true
|
|
}
|
|
|
|
$ProgressPreference = 'Continue'
|
|
|
|
try {
|
|
if ($proxyCA -ne "") {
|
|
$env:REQUESTS_CA_BUNDLE = $proxyCA
|
|
}
|
|
|
|
logH2 "Logging into azure"
|
|
|
|
$azLoginMsg = "Please login to Azure CLI.`n" +
|
|
"`t* If you're running the script for the first time, select yes.`n" +
|
|
"`t* If you've recently logged in to az while running the script, you can select no.`n" +
|
|
"Confirm login to azure cli?"
|
|
if (confirmationPrompt -msg $azLoginMsg) {
|
|
az login --use-device-code -o table
|
|
}
|
|
|
|
az account set -s $applianceSubscriptionId
|
|
if ($LASTEXITCODE) {
|
|
$Error[0] | Out-String >> $logFile
|
|
throw "The default subscription for the az cli context could not be set."
|
|
}
|
|
|
|
# Add necessary extensions
|
|
az extension add --upgrade --name arcappliance
|
|
az extension add --upgrade --name k8s-extension
|
|
az extension add --upgrade --name customlocation
|
|
az extension add --upgrade --name connectedvmware
|
|
|
|
logH1 "Completed Step 1/5: Workstation configured and all extensions successfully installed"
|
|
|
|
createRG "$applianceSubscriptionId" "$applianceResourceGroupName"
|
|
|
|
logH1 "Step 2/5: Deleting and recreating a healthy Arc resource bridge"
|
|
logH2 "Provide vCenter details to deploy Arc resource bridge VM. The credentials will be used by Arc resource bridge to update and scale itself."
|
|
|
|
# TODO: Fetch necessary information for all related ARM resources
|
|
# Get appliance name, cluster extension name, custom location names, vCenter name
|
|
|
|
# Delete existing resource bridge VM and ARM resource
|
|
logH2 "Cleaning up existing Arc Resource Bridge"
|
|
# Ask if they're sure about deleting
|
|
$deleteWarningMsg = "This will delete the existing Arc resource bridge and deploy a new one with the existing VM template.`n" +
|
|
"`t*Confirm deletion and recreation of Arc resource bridge?"
|
|
if (!(confirmationPrompt -msg $deleteWarningMsg)) {
|
|
$Error[0] | Out-String >> $logFile
|
|
throw "Arc resource bridge recovery process canceled."
|
|
}
|
|
# TODO: Catch exit code
|
|
az arcappliance delete vmware --debug --config-file $config
|
|
|
|
# Redeploying new resource bridge VM
|
|
logH2 "Deploying new Arc Resource Bridge"
|
|
az arcappliance deploy vmware --debug --config-file $config
|
|
|
|
# Recreating resource bridge ARM resource and connecting new resource bridge VM and Azure
|
|
logH2 "Connecting new Arc Resource Bridge to Azure"
|
|
az arcappliance create vmware --debug --config-file $config --kubeconfig .\kubeconfig
|
|
|
|
$applianceId = (az arcappliance show --subscription $applianceSubscriptionId --resource-group $applianceResourceGroupName --name $applianceName --query id -o tsv 2>> $logFile)
|
|
if (!$applianceId) {
|
|
throw "Appliance recreation has failed."
|
|
}
|
|
# TODO: Get every 30 seconds, timeout after 5 minutes
|
|
$applianceStatus = (az resource show --debug --ids "$applianceId" --query 'properties.status' -o tsv 2>> $logFile)
|
|
if ($applianceStatus -ne "Running") {
|
|
throw "Appliance is not in running state. Current state: $applianceStatus."
|
|
}
|
|
|
|
logH1 "Completed Step 2/5: Arc resource bridge is back up and running"
|
|
logH1 "Step 3/5: Reinstalling cluster extension"
|
|
|
|
az k8s-extension create --debug --subscription $applianceSubscriptionId --resource-group $applianceResourceGroupName --name azure-vmwareoperator --extension-type 'Microsoft.vmware' --scope cluster --cluster-type appliances --cluster-name $applianceName --config Microsoft.CustomLocation.ServiceAccount=azure-vmwareoperator 2>> $logFile
|
|
|
|
$clusterExtensionId = (az k8s-extension show --subscription $applianceSubscriptionId --resource-group $applianceResourceGroupName --name azure-vmwareoperator --cluster-type appliances --cluster-name $applianceName --query id -o tsv 2>> $logFile)
|
|
if (!$clusterExtensionId) {
|
|
throw "Cluster extension reinstallation failed."
|
|
}
|
|
$clusterExtensionState = (az resource show --debug --ids "$clusterExtensionId" --query 'properties.provisioningState' -o tsv 2>> $logFile)
|
|
if ($clusterExtensionState -ne "Succeeded") {
|
|
throw "Provisioning State of cluster extension is not succeeded. Current state: $clusterExtensionState."
|
|
}
|
|
|
|
logH1 "Completed Step 3/5: Cluster extension reinstalled successfully"
|
|
logH1 "Step 4/5: Reconnecting custom location"
|
|
|
|
createRG "$customLocationSubscriptionId" "$customLocationResourceGroupName"
|
|
|
|
$customLocationNamespace = ("$customLocationName".ToLower() -replace '[^a-z0-9-]', '')
|
|
az customlocation create --debug --tags "" --subscription $customLocationSubscriptionId --resource-group $customLocationResourceGroupName --name $customLocationName --location $location --namespace $customLocationNamespace --host-resource-id $applianceId --cluster-extension-ids $clusterExtensionId 2>> $logFile
|
|
|
|
$customLocationId = (az customlocation show --subscription $customLocationSubscriptionId --resource-group $customLocationResourceGroupName --name $customLocationName --query id -o tsv 2>> $logFile)
|
|
if (!$customLocationId) {
|
|
throw "Custom location reconnection failed."
|
|
}
|
|
$customLocationState = (az resource show --debug --ids $customLocationId --query 'properties.provisioningState' -o tsv 2>> $logFile)
|
|
if ($customLocationState -ne "Succeeded") {
|
|
throw "Provisioning State of custom location is not succeeded. Current state: $customLocationState."
|
|
}
|
|
|
|
logH1 "Completed Step 4/5: Custom location reconnected successfully"
|
|
logH1 "Step 5/5: Reconnecting to vCenter"
|
|
|
|
createRG "$vCenterSubscriptionId" "$vCenterResourceGroupName"
|
|
|
|
logH2 "Provide vCenter details"
|
|
logText "`t* These credentials will be used when you perform vCenter operations through Azure."
|
|
logText "`t* You can provide the same credentials that you provided for Arc resource bridge earlier."
|
|
|
|
az connectedvmware vcenter connect --debug --tags "" --subscription $vCenterSubscriptionId --resource-group $vCenterResourceGroupName --name $vCenterName --custom-location $customLocationId --location $location --port 443
|
|
|
|
$vcenterId = (az connectedvmware vcenter show --subscription $vCenterSubscriptionId --resource-group $vCenterResourceGroupName --name $vCenterName --query id -o tsv 2>> $logFile)
|
|
if (!$vcenterId) {
|
|
throw "Reconnecting to vCenter failed."
|
|
}
|
|
$vcenterState = (az resource show --debug --ids "$vcenterId" --query 'properties.provisioningState' -o tsv 2>> $logFile)
|
|
if ($vcenterState -ne "Succeeded") {
|
|
throw "Provisioning State of vCenter is not succeeded. Current state: $vcenterState."
|
|
}
|
|
|
|
logH1 "Completed Step 5/5: vCenter was reconnected successfully"
|
|
logH1 "Your vCenter has been successfully reonboarded to Azure Arc and recovery is completed!"
|
|
}
|
|
catch {
|
|
$err = $_.Exception | Out-String
|
|
logText -ForegroundColor Red ("Script execution failed: " + $err)
|
|
}
|
|
finally {
|
|
deactivate
|
|
}
|