AzureArcReOnboardingAssistant.psm1
function Write-ColorOutput { param ( [Parameter(Mandatory=$true)] [System.ConsoleColor]$ForegroundColor, [Parameter(Mandatory=$true)] [string]$Message ) Write-Host -ForegroundColor $ForegroundColor -Object $Message } function Get-ServerInfo { param ( [Parameter(Mandatory=$true)] [string]$ServerName, [Parameter(Mandatory=$true)] [string]$ResourceGroupName ) try { $server = Get-AzConnectedMachine -Name $ServerName -ResourceGroupName $ResourceGroupName -ErrorAction Stop $serverInfo = [ordered]@{ ServerName = $server.Name ResourceGroup = $server.ResourceGroupName SubscriptionId = (Get-AzContext).Subscription.Id Location = $server.Location Tags = @{} HasTags = $false Extensions = @() DataCollectionAssociations = @() AgentConfigurationConfigMode = $server.AgentConfigurationConfigMode.ToString() } # Handle Tags $tagProperty = if ($null -ne $server.Tags) { $server.Tags } else { $server.Tag } if ($null -ne $tagProperty -and $tagProperty.Count -gt 0) { $serverInfo.HasTags = $true foreach ($key in $tagProperty.Keys) { $serverInfo.Tags[$key] = $tagProperty[$key] } } # Get Extensions $extensions = Get-AzConnectedMachineExtension -ResourceGroupName $ResourceGroupName -MachineName $ServerName -ErrorAction Stop $serverInfo.Extensions = $extensions | Select-Object -Property Name, Publisher, ExtensionType, TypeHandlerVersion, Location # Get Data Collection Associations $dcas = Get-AzDataCollectionRuleAssociation -TargetResourceId $server.Id -ErrorAction Stop $serverInfo.DataCollectionAssociations = $dcas | Select-Object -Property Name, DataCollectionRuleId, DataCollectionEndpointId return $serverInfo } catch { Write-Error "Error getting server info: $($ERROR[0].Exception.Message)" return $null } } function Save-ServerInfo { param ( [Parameter(Mandatory=$true)] [hashtable]$ServerInfo, [Parameter(Mandatory=$true)] [string]$OutputLocation ) $Filename = Join-Path -Path $OutputLocation -ChildPath "$($ServerInfo.SubscriptionId)_$($ServerInfo.ServerName)_info.json" $MasterFilename = Join-Path -Path $OutputLocation -ChildPath "$($ServerInfo.SubscriptionId)_$($ServerInfo.ServerName)_info_master.json" $ServerInfo | ConvertTo-Json -Depth 10 | Out-File -FilePath $Filename -Encoding UTF8 Write-ColorOutput -ForegroundColor Green -Message "Server information saved to $Filename" if (-not (Test-Path -Path $MasterFilename)) { $ServerInfo | ConvertTo-Json -Depth 10 | Out-File -FilePath $MasterFilename -Encoding UTF8 Write-ColorOutput -ForegroundColor Green -Message "Master copy created at $MasterFilename" } else { Write-ColorOutput -ForegroundColor Yellow -Message "Master copy already exists at $MasterFilename (not overwritten)" } } function Wait-ForServerOffboarding { param ( [Parameter(Mandatory=$true)] [string]$ServerName, [Parameter(Mandatory=$true)] [string]$ResourceGroupName ) Write-ColorOutput -ForegroundColor Cyan -Message "Waiting for server to be offboarded..." while ($true) { try { Get-AzConnectedMachine -Name $ServerName -ResourceGroupName $ResourceGroupName -ErrorAction Stop | Out-Null Write-Host "." -NoNewline Start-Sleep -Seconds 10 } catch { Write-Host "" Write-ColorOutput -ForegroundColor Green -Message "Server has been offboarded." break } } } function Wait-ForServerReonboarding { param ( [Parameter(Mandatory=$true)] [string]$ServerName, [Parameter(Mandatory=$true)] [string]$ResourceGroupName ) Write-ColorOutput -ForegroundColor Cyan -Message "Waiting for server to be re-onboarded..." while ($true) { try { $server = Get-AzConnectedMachine -Name $ServerName -ResourceGroupName $ResourceGroupName -ErrorAction Stop if ($server.Status -eq "Connected") { Write-ColorOutput -ForegroundColor Green -Message "Server has been re-onboarded." return $server } } catch { Write-Host "." -NoNewline } Start-Sleep -Seconds 10 } } function Restore-ServerConfiguration { param ( [Parameter(Mandatory=$true)] [PSCustomObject]$StoredServerInfo, [Parameter(Mandatory=$true)] [PSObject]$Server ) if ($StoredServerInfo.HasTags) { try { $TagsHashtable = @{} foreach ($Tag in $StoredServerInfo.Tags.PSObject.Properties) { $TagsHashtable[$Tag.Name] = $Tag.Value } Update-AzConnectedMachine -Name $StoredServerInfo.ServerName -ResourceGroupName $StoredServerInfo.ResourceGroup -Tag $TagsHashtable -ErrorAction Stop | Out-Null Write-ColorOutput -ForegroundColor Green -Message "Tags restored successfully" } catch { Write-ColorOutput -ForegroundColor Red -Message "Error restoring tags: $($Error[0])" } } foreach ($dca in $StoredServerInfo.DataCollectionAssociations) { if ($dca.DataCollectionRuleId) { try { New-AzDataCollectionRuleAssociation -TargetResourceId $Server.Id -AssociationName $dca.Name -DataCollectionRuleId $dca.DataCollectionRuleId -ErrorAction Stop | Out-Null Write-ColorOutput -ForegroundColor Green -Message "Data Collection Rule Association '$($dca.Name)' restored" } catch { Write-ColorOutput -ForegroundColor Red -Message "Failed to restore Data Collection Rule Association '$($dca.Name)': $($Error[0])" } } elseif ($dca.DataCollectionEndpointId) { try { New-AzDataCollectionRuleAssociation -TargetResourceId $Server.Id -AssociationName $dca.Name -DataCollectionEndpointId $dca.DataCollectionEndpointId -ErrorAction Stop | Out-Null Write-ColorOutput -ForegroundColor Green -Message "Data Collection Endpoint Association '$($dca.Name)' restored" } catch { Write-ColorOutput -ForegroundColor Red -Message "Failed to restore Data Collection Endpoint Association '$($dca.Name)': $($Error[0])" } } } Write-ColorOutput -ForegroundColor Yellow -Message "Note: Stored AgentConfigurationConfigMode was $($StoredServerInfo.AgentConfigurationConfigMode). This setting cannot be automatically restored." } function Invoke-AzureArcNodeOffboarding { <# .SYNOPSIS Manages the offboarding and re-onboarding process for Azure Arc-enabled servers. .DESCRIPTION The Invoke-AzureArcNodeOffboarding function facilitates the process of offboarding an Azure Arc-enabled server and then re-onboarding it. It can also be used to restore a previously offboarded server's configuration. This function performs the following tasks: 1. Retrieves and stores the current server configuration. 2. Waits for the server to be manually offboarded. 3. Waits for the server to be re-onboarded. 4. Restores the server's original configuration (tags, data collection rules, etc.). .PARAMETER ServerName The name of the Azure Arc-enabled server to offboard and re-onboard. .PARAMETER ResourceGroupName The name of the resource group containing the Azure Arc-enabled server. .PARAMETER SubscriptionId The ID of the Azure subscription containing the Azure Arc-enabled server. .PARAMETER OutputLocation The file path where backup files of the server configuration will be stored. .PARAMETER RestoreOnly Switch parameter to indicate that only the restoration process should be performed, skipping the offboarding steps. .PARAMETER RestoreFile The file path of a specific backup file to use for restoration. Required when using the RestoreOnly switch. .EXAMPLE Invoke-AzureArcNodeOffboarding -ServerName SERVER1 -ResourceGroupName "RG-ARC-SRV" -SubscriptionId "XXXXXX-XXXX-XXXX-XXXX-XXXXXX" -OutputLocation "C:\AzureArcServerBackups" This example initiates the full offboarding and re-onboarding process for the server named SERVER1. .EXAMPLE Invoke-AzureArcNodeOffboarding -ServerName SERVER1 -ResourceGroupName "RG-ARC-SRV" -SubscriptionId "XXXXXX-XXXX-XXXX-XXXX-XXXXXX" -RestoreOnly -RestoreFile "C:\AzureArcServerBackups\XXXXXXXXXXXXXXXXXXXXXXXXX_SERVER1_info_master.json" -OutputLocation "C:\AzureArcServerBackups" This example performs only the restoration process for the server named SERVER1, using a specific backup file. .NOTES Author: Kaido Järvemets Website: KaidoJarvemets.com Version: 1.0.0 Requires: Azure PowerShell modules (Az.Accounts, Az.ConnectedMachine, Az.Monitor) Before running this function, ensure you have the necessary permissions and are connected to the correct Azure subscription. You may need to run Set-AzContext -SubscriptionId $SubscriptionId before invoking this function. .LINK https://kaidojarvemets.com/projects/azurearcreonboardingassistant #> param( [Parameter(Mandatory=$true)] [string]$ServerName, [Parameter(Mandatory=$true)] [string]$ResourceGroupName, [Parameter(Mandatory=$true)] [string]$SubscriptionId, [Parameter(Mandatory=$true)] [string]$OutputLocation, [Parameter(Mandatory=$false)] [switch]$RestoreOnly, [Parameter(Mandatory=$false)] [string]$RestoreFile ) if ($RestoreOnly -and -not $RestoreFile) { Write-ColorOutput -ForegroundColor Red -Message "RestoreFile parameter is required when using RestoreOnly switch." return } if (-not (Test-Path -Path $OutputLocation)) { New-Item -ItemType Directory -Path $OutputLocation -Force | Out-Null Write-ColorOutput -ForegroundColor Yellow -Message "Created output directory: $OutputLocation" } if (-not $RestoreOnly) { Write-ColorOutput -ForegroundColor Cyan -Message "Starting Azure Arc Node offboarding process for $ServerName" $serverInfo = Get-ServerInfo -ServerName $ServerName -ResourceGroupName $ResourceGroupName if ($null -eq $serverInfo) { return } Save-ServerInfo -ServerInfo $serverInfo -OutputLocation $OutputLocation Write-ColorOutput -ForegroundColor Yellow -Message "Please proceed with manual offboarding of the server." Wait-ForServerOffboarding -ServerName $ServerName -ResourceGroupName $ResourceGroupName } if ($RestoreOnly) { $fileToRestore = $RestoreFile Write-ColorOutput -ForegroundColor Yellow -Message "Restoring from specified file: $fileToRestore" } else { $masterFilename = Join-Path -Path $OutputLocation -ChildPath "$($SubscriptionId)_$($ServerName)_info_master.json" $latestFilename = Join-Path -Path $OutputLocation -ChildPath "$($SubscriptionId)_$($ServerName)_info.json" if (Test-Path -Path $masterFilename) { $fileToRestore = $masterFilename Write-ColorOutput -ForegroundColor Green -Message "Restoring from master copy: $fileToRestore" } elseif (Test-Path -Path $latestFilename) { $fileToRestore = $latestFilename Write-ColorOutput -ForegroundColor Yellow -Message "Master copy not found. Restoring from latest backup: $fileToRestore" } else { Write-ColorOutput -ForegroundColor Red -Message "No backup files found. Cannot proceed with restoration." return } $server = Wait-ForServerReonboarding -ServerName $ServerName -ResourceGroupName $ResourceGroupName } $storedServerInfo = Get-Content -Path $fileToRestore | ConvertFrom-Json if ($RestoreOnly) { try { $server = Get-AzConnectedMachine -Name $ServerName -ResourceGroupName $ResourceGroupName -ErrorAction Stop } catch { Write-ColorOutput -ForegroundColor Red -Message "Unable to find the server $ServerName. Ensure it's onboarded before restoration: $($Error[0])" return } } Write-ColorOutput -ForegroundColor Cyan -Message "Restoring server configuration..." Restore-ServerConfiguration -StoredServerInfo $storedServerInfo -Server $server Write-ColorOutput -ForegroundColor Green -Message "Azure Arc Node offboarding and restoration process completed successfully." } |