Skip to content

Commit 9453ae0

Browse files
Samhitha-MicrosoftCopilotwyunchi-ms
authored
Arc enable nodes before registration in latest Registration Flow (#28942)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Yunchi Wang <54880216+wyunchi-ms@users.noreply.github.com>
1 parent 6a1a65a commit 9453ae0

File tree

2 files changed

+205
-17
lines changed

2 files changed

+205
-17
lines changed

src/StackHCI/StackHCI.Autorest/custom/stackhci.ps1

Lines changed: 203 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -535,13 +535,22 @@ function Confirm-UserAcknowledgmentToUpgradeOS {
535535
[Microsoft.Azure.PowerShell.Cmdlets.StackHCI.DoNotExportAttribute()]
536536
[CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')]
537537
param(
538-
[Parameter(Mandatory=$true)]
538+
[Parameter(Mandatory=$false)]
539539
[System.Management.Automation.Runspaces.PSSession]
540540
$ClusterNodeSession
541541
)
542542

543543
$osVersionDetectoid = { $displayVersion = (Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion").DisplayVersion; $buildNumber = (Get-CimInstance -ClassName CIM_OperatingSystem).BuildNumber; New-Object -TypeName PSObject -Property @{'DisplayVersion'=$displayVersion; 'BuildNumber'=$buildNumber} }
544-
$osVersionInfo = Invoke-Command -Session $clusterNodeSession -ScriptBlock $osVersionDetectoid
544+
545+
if ($ClusterNodeSession)
546+
{
547+
$osVersionInfo = Invoke-Command -Session $ClusterNodeSession -ScriptBlock $osVersionDetectoid
548+
}
549+
else
550+
{
551+
$osVersionInfo = & $osVersionDetectoid
552+
}
553+
545554
$isOSVersion22H2 = ([Int]::Parse($osVersionInfo.BuildNumber) -le $22H2BuildNumber)
546555

547556
$doNotAbort = $true
@@ -2875,6 +2884,144 @@ function Test-ClusterMsiSupport {
28752884
return $result -eq $true
28762885
}
28772886

2887+
function Enable-ArcOnNodes {
2888+
param(
2889+
[Parameter(Mandatory=$true)]
2890+
[array]$ClusterNodes,
2891+
[Parameter(Mandatory=$false)]
2892+
[System.Management.Automation.PSCredential]$Credential,
2893+
[Parameter(Mandatory=$true)]
2894+
[string]$ClusterDNSSuffix,
2895+
[Parameter(Mandatory=$true)]
2896+
[string]$SubscriptionId,
2897+
[Parameter(Mandatory=$true)]
2898+
[string]$ResourceGroupName,
2899+
[Parameter(Mandatory=$true)]
2900+
[string]$TenantId,
2901+
[Parameter(Mandatory=$true)]
2902+
[string]$Location,
2903+
[Parameter(Mandatory=$true)]
2904+
[string]$EnvironmentName,
2905+
[Parameter(Mandatory=$true)]
2906+
[string]$AccessToken,
2907+
[Parameter(Mandatory=$false)]
2908+
[bool]$UseStableAgent = $false,
2909+
[Parameter(Mandatory=$false)]
2910+
[bool]$IsManagementNode = $false,
2911+
[Parameter(Mandatory=$false)]
2912+
[string]$ComputerName = [Environment]::MachineName
2913+
)
2914+
2915+
Write-VerboseLog "[Arc Enablement] Starting manual Arc enablement on nodes for environment: $EnvironmentName"
2916+
2917+
$cloudArgument = $EnvironmentName
2918+
if (($EnvironmentName -eq $AzureCanary) -or ($EnvironmentName -eq $AzurePPE)) {
2919+
$cloudArgument = $AzureCloud
2920+
}
2921+
2922+
foreach ($node in $ClusterNodes) {
2923+
$nodeName = $node.Name
2924+
$nodeFQDN = "$nodeName.$ClusterDNSSuffix"
2925+
2926+
Write-VerboseLog "[Arc Enablement] Processing node: $nodeName"
2927+
2928+
$session = $null
2929+
try {
2930+
if ($Credential) {
2931+
$session = New-PSSession -ComputerName $nodeFQDN -Credential $Credential
2932+
} else {
2933+
$session = New-PSSession -ComputerName $nodeFQDN
2934+
}
2935+
2936+
Invoke-Command -Session $session -ScriptBlock {
2937+
param($subId, $rg, $loc, $tenant, $token, $cloud, $UseStableAgent)
2938+
2939+
$agentPath = "${env:ProgramFiles}\AzureConnectedMachineAgent\azcmagent.exe"
2940+
2941+
# 1. Install Agent if missing
2942+
if (-not (Test-Path $agentPath)) {
2943+
Write-Verbose "Arc agent not found. Downloading and Installing..."
2944+
$installerPath = "$env:TEMP\AzureConnectedMachineAgent.msi"
2945+
2946+
$url = 'https://aka.ms/AzureConnectedMachineAgent'
2947+
if ($UseStableAgent) {
2948+
$url = 'https://aka.ms/hciarcagent'
2949+
}
2950+
2951+
try {
2952+
Invoke-WebRequest -Uri $url -OutFile $installerPath -ErrorAction Stop
2953+
}
2954+
catch {
2955+
throw "Failed to download Azure Connected Machine Agent from $url. Error: $($_.Exception.Message)"
2956+
}
2957+
2958+
$installArgs = "/i `"$installerPath`" /qn /l*v `"$env:TEMP\install.log`""
2959+
$process = Start-Process msiexec.exe -ArgumentList $installArgs -Wait -PassThru
2960+
2961+
if ($process.ExitCode -ne 0) {
2962+
throw "Agent installation failed with exit code $($process.ExitCode). Check $env:TEMP\install.log"
2963+
}
2964+
}
2965+
2966+
# 2. Check status
2967+
if (Test-Path $agentPath) {
2968+
$statusJson = & $agentPath show -j | ConvertFrom-Json
2969+
$isConnected = $statusJson.status -eq "Connected"
2970+
2971+
if ($isConnected -and
2972+
($statusJson.subscriptionId -eq $subId) -and
2973+
($statusJson.resourceGroup -eq $rg) -and
2974+
($statusJson.tenantId -eq $tenant)) {
2975+
Write-Verbose "Node is already connected to correct Subscription/RG."
2976+
return
2977+
}
2978+
}
2979+
2980+
# 3. Connect using the passed token
2981+
$connectArgs = @("connect",
2982+
"--subscription-id", $subId,
2983+
"--resource-group", $rg,
2984+
"--tenant-id", $tenant,
2985+
"--location", $loc,
2986+
"--access-token", $token,
2987+
"--cloud", $cloud,
2988+
"--correlation-id", $(New-Guid).ToString())
2989+
2990+
Write-Verbose "Executing azcmagent connect..."
2991+
2992+
# Use call operator & to run command and capture stdout/stderr combined
2993+
$output = & $agentPath $connectArgs 2>&1 | Out-String
2994+
2995+
if ($LASTEXITCODE -ne 0) {
2996+
if ($output -match "Forbidden" -or $output -match "AuthorizationFailed" -or $output -match "access is denied") {
2997+
$errorMsg = "PERMISSIONS ERROR: Failed to onboard node '$env:COMPUTERNAME' to Azure Arc.`n" +
2998+
"REASON: Your account lacks the required permissions on Resource Group '$rg'.`n" +
2999+
"ACTION: Ensure your account has ONE of the following roles:`n" +
3000+
" - 'Owner'`n" +
3001+
" - 'Contributor'`n" +
3002+
" - 'Azure Connected Machine Resource Administrator' "
3003+
throw $errorMsg
3004+
}
3005+
throw "azcmagent connect failed with exit code $LASTEXITCODE. Details: $output"
3006+
}
3007+
3008+
} -ArgumentList $SubscriptionId, $ResourceGroupName, $Location, $TenantId, $AccessToken, $cloudArgument, $UseStableAgent
3009+
}
3010+
catch {
3011+
$errorMsg = "Failed to enable Arc on node ${nodeName}: $($_.Exception.Message)"
3012+
Write-ErrorLog $errorMsg
3013+
Write-NodeEventLog -Message $errorMsg -EventID 9150 -IsManagementNode $IsManagementNode -Credentials $Credential -ComputerName $ComputerName -Level Error
3014+
throw
3015+
}
3016+
finally {
3017+
if ($session) { Remove-PSSession $session }
3018+
}
3019+
}
3020+
$successMsg = "[Arc Enablement] Completed successfully on all nodes."
3021+
Write-VerboseLog $successMsg
3022+
Write-NodeEventLog -Message $successMsg -EventID 9151 -IsManagementNode $IsManagementNode -Credentials $Credential -ComputerName $ComputerName -Level Information
3023+
}
3024+
28783025
<#
28793026
Checks whether all nodes in the given cluster are Arc-enabled.
28803027
[bool] Returns $true if all nodes are Arc-enabled, $false otherwise.
@@ -3052,18 +3199,57 @@ function Invoke-MSIFlow {
30523199
[Parameter(Mandatory=$false)]
30533200
[string]$ArcServerResourceGroupName,
30543201
[Parameter(Mandatory=$false)]
3055-
[string]$EnvironmentName = $AzureCloud
3202+
[string]$EnvironmentName = $AzureCloud,
3203+
[Parameter(Mandatory=$false)]
3204+
[bool]$UseStableAgent = $false
30563205
)
30573206

30583207
try {
30593208
Write-VerboseLog "[MSI Flow] Starting MSI-based cluster registration."
30603209
Write-NodeEventLog -Message "[MSI Flow] Starting MSI-based cluster registration." -EventID 9133 -IsManagementNode $IsManagementNode -credentials $Credential -ComputerName $ComputerName
30613210
$resource = Get-AzResource -ResourceId $ResourceId -ApiVersion $RPAPIVersion -ErrorAction Ignore
30623211

3063-
#Confirm Arc is enabled on all nodes
3212+
# Check if nodes are already Arc enabled
30643213
$allArcEnabled = Test-ClusterArcEnabled -ClusterNodes $ClusterNodes -Credential $Credential -ClusterDNSSuffix $ClusterDNSSuffix -SubscriptionId $SubscriptionId -ArcResourceGroupName $ArcServerResourceGroupName
3214+
30653215
if (-not $allArcEnabled) {
3066-
throw [System.InvalidOperationException]::new("Not all cluster nodes are Arc-enabled. Aborting MSI registration.")
3216+
Write-VerboseLog "[MSI Flow] Not all nodes are Arc-enabled. Attempting to enable Arc on nodes manually..."
3217+
3218+
# 2. Retrieve Token Locally
3219+
try {
3220+
Write-VerboseLog "[MSI Flow] Requesting Access Token for Tenant '$TenantId'"
3221+
3222+
$azToken = Get-AzAccessToken -TenantId $TenantId -ErrorAction Stop
3223+
$tokenString = $azToken.Token
3224+
3225+
if ($tokenString -is [System.Security.SecureString]) {
3226+
$tokenString = [System.Net.NetworkCredential]::new("", $tokenString).Password
3227+
}
3228+
}
3229+
catch {
3230+
throw "Failed to retrieve Azure Access Token for Arc enablement. Error: $($_.Exception.Message)"
3231+
}
3232+
3233+
# 3. Call the manual enablement function with the PlainText token
3234+
Enable-ArcOnNodes -ClusterNodes $ClusterNodes `
3235+
-Credential $Credential `
3236+
-ClusterDNSSuffix $ClusterDNSSuffix `
3237+
-SubscriptionId $SubscriptionId `
3238+
-ResourceGroupName $ArcServerResourceGroupName `
3239+
-TenantId $TenantId `
3240+
-Location $Region `
3241+
-EnvironmentName $EnvironmentName `
3242+
-AccessToken $tokenString `
3243+
-UseStableAgent $UseStableAgent `
3244+
-IsManagementNode $IsManagementNode `
3245+
-ComputerName $ComputerName
3246+
3247+
# Re-verify enablement
3248+
$allArcEnabled = Test-ClusterArcEnabled -ClusterNodes $ClusterNodes -Credential $Credential -ClusterDNSSuffix $ClusterDNSSuffix -SubscriptionId $SubscriptionId -ArcResourceGroupName $ArcServerResourceGroupName
3249+
3250+
if (-not $allArcEnabled) {
3251+
throw [System.InvalidOperationException]::new("Failed to enable Arc on all cluster nodes. Aborting registration.")
3252+
}
30673253
}
30683254

30693255
if($Null -eq $resource.Properties.ResourceProviderObjectId)
@@ -4118,7 +4304,7 @@ Set-WacOutputProperty -IsWAC $IsWAC -PropertyName $OutputPropertyWacErrorCode -
41184304
if ($isRegistrationWithArcMsiCapable -eq $true)
41194305
{
41204306
Write-VerboseLog "[Registration] Entered MSI registration path"
4121-
Invoke-MSIFlow -ClusterNodes $clusterNodes -Credential $Credential -ClusterDNSSuffix $clusterDNSSuffix -ResourceId $resourceId -RPAPIVersion $RPAPIVersion -TenantId $TenantId -Region $Region -ResourceGroupName $ResourceGroupName -Tag $Tag -ResourceName $ResourceName -SubscriptionId $SubscriptionId -registrationOutput $registrationOutput -ClusterNodeSession $clusterNodeSession -IsManagementNode $IsManagementNode -ComputerName $ComputerName -IsWAC:$IsWAC -ArcServerResourceGroupName $ArcServerResourceGroupName -EnvironmentName $EnvironmentName
4307+
Invoke-MSIFlow -ClusterNodes $clusterNodes -Credential $Credential -ClusterDNSSuffix $clusterDNSSuffix -ResourceId $resourceId -RPAPIVersion $RPAPIVersion -TenantId $TenantId -Region $Region -ResourceGroupName $ResourceGroupName -Tag $Tag -ResourceName $ResourceName -SubscriptionId $SubscriptionId -registrationOutput $registrationOutput -ClusterNodeSession $clusterNodeSession -IsManagementNode $IsManagementNode -ComputerName $ComputerName -IsWAC:$IsWAC -ArcServerResourceGroupName $ArcServerResourceGroupName -EnvironmentName $EnvironmentName -UseStableAgent $useStableAgentVersion
41224308
$operationStatus = [OperationStatus]::Success
41234309
}
41244310
else
@@ -6934,7 +7120,7 @@ param(
69347120
try
69357121
{
69367122

6937-
$_, $_, $clusterNodeSession, $_ = Get-SetupLoggingDetails
7123+
$_, $_, $clusterNodeSession, $_ = Get-SetupLoggingDetails -newSession $false
69387124
Confirm-UserAcknowledgmentToUpgradeOS -ClusterNodeSession $clusterNodeSession
69397125

69407126
$LogFilePrefix = "AddAzStackHCIVMAttestation"
@@ -7130,7 +7316,7 @@ param(
71307316

71317317
try
71327318
{
7133-
$_, $_, $clusterNodeSession, $_ = Get-SetupLoggingDetails
7319+
$_, $_, $clusterNodeSession, $_ = Get-SetupLoggingDetails -newSession $false
71347320
Confirm-UserAcknowledgmentToUpgradeOS -ClusterNodeSession $clusterNodeSession
71357321

71367322
$LogFilePrefix = "RemoveAzStackHCIVMAttestation"
@@ -7258,7 +7444,7 @@ param(
72587444
{
72597445
try
72607446
{
7261-
$_, $_, $clusterNodeSession, $_ = Get-SetupLoggingDetails
7447+
$_, $_, $clusterNodeSession, $_ = Get-SetupLoggingDetails -newSession $false
72627448
Confirm-UserAcknowledgmentToUpgradeOS -ClusterNodeSession $clusterNodeSession
72637449

72647450
$getImdsOutputList = [System.Collections.ArrayList]::new()
@@ -7425,7 +7611,7 @@ function Invoke-DeploymentModuleDownload{
74257611
$retryCount = 3
74267612
try
74277613
{
7428-
$_, $IsClusterRegistered, $clusterNodeSession, $_ = Get-SetupLoggingDetails
7614+
$_, $IsClusterRegistered, $clusterNodeSession, $_ = Get-SetupLoggingDetails -newSession $false
74297615
Setup-Logging -LogFilePrefix "AzStackHCIRemoteSupport" -DebugEnabled ($DebugPreference -ne "SilentlyContinue") -ClusterNodeSession $clusterNodeSession -IsClusterRegistered $IsClusterRegistered | Out-Null
74307616

74317617
if ($Null -ne $clusterNodeSession)
@@ -7464,7 +7650,7 @@ function Install-DeployModule {
74647650
$ModuleName
74657651
)
74667652

7467-
$_, $IsClusterRegistered, $clusterNodeSession, $_ = Get-SetupLoggingDetails
7653+
$_, $IsClusterRegistered, $clusterNodeSession, $_ = Get-SetupLoggingDetails -newSession $false
74687654
Setup-Logging -LogFilePrefix "AzStackHCIRemoteSupportInstallModule" -DebugEnabled ($DebugPreference -ne "SilentlyContinue") -ClusterNodeSession $clusterNodeSession -IsClusterRegistered $IsClusterRegistered | Out-Null
74697655

74707656
if ($Null -ne $clusterNodeSession)
@@ -7504,7 +7690,7 @@ function Install-AzStackHCIRemoteSupport{
75047690
[OutputType([Boolean])]
75057691
param()
75067692

7507-
$_, $IsClusterRegistered, $clusterNodeSession, $_ = Get-SetupLoggingDetails
7693+
$_, $IsClusterRegistered, $clusterNodeSession, $_ = Get-SetupLoggingDetails -newSession $false
75087694

75097695
Confirm-UserAcknowledgmentToUpgradeOS -ClusterNodeSession $clusterNodeSession
75107696

@@ -7544,7 +7730,7 @@ function Remove-AzStackHCIRemoteSupport{
75447730
[OutputType([Boolean])]
75457731
param()
75467732

7547-
$_, $IsClusterRegistered, $clusterNodeSession, $_ = Get-SetupLoggingDetails
7733+
$_, $IsClusterRegistered, $clusterNodeSession, $_ = Get-SetupLoggingDetails -newSession $false
75487734
Confirm-UserAcknowledgmentToUpgradeOS -ClusterNodeSession $clusterNodeSession
75497735
Setup-Logging -LogFilePrefix "AzStackHCIRemoteSupportRemove" -DebugEnabled ($DebugPreference -ne "SilentlyContinue") -ClusterNodeSession $clusterNodeSession -IsClusterRegistered $IsClusterRegistered | Out-Null
75507736
if ($Null -ne $clusterNodeSession)
@@ -7612,7 +7798,7 @@ function Enable-AzStackHCIRemoteSupport{
76127798
$AgreeToRemoteSupportConsent
76137799
)
76147800

7615-
$_, $_, $clusterNodeSession, $_ = Get-SetupLoggingDetails
7801+
$_, $_, $clusterNodeSession, $_ = Get-SetupLoggingDetails -newSession $false
76167802
Confirm-UserAcknowledgmentToUpgradeOS -ClusterNodeSession $clusterNodeSession
76177803

76187804
if ($AgreeToRemoteSupportConsent -ne $true)
@@ -7659,7 +7845,7 @@ function Disable-AzStackHCIRemoteSupport{
76597845
[OutputType([Boolean])]
76607846
param()
76617847

7662-
$_, $_, $clusterNodeSession, $_ = Get-SetupLoggingDetails
7848+
$_, $_, $clusterNodeSession, $_ = Get-SetupLoggingDetails -newSession $false
76637849
Confirm-UserAcknowledgmentToUpgradeOS -ClusterNodeSession $clusterNodeSession
76647850

76657851
$agentInstallType = (Get-ItemProperty -Path "HKLM:\SYSTEM\Software\Microsoft\AzureStack\Observability\RemoteSupport" -ErrorAction SilentlyContinue).InstallType
@@ -7707,7 +7893,7 @@ function Get-AzStackHCIRemoteSupportAccess{
77077893
$IncludeExpired
77087894
)
77097895

7710-
$_, $_, $clusterNodeSession, $_ = Get-SetupLoggingDetails
7896+
$_, $_, $clusterNodeSession, $_ = Get-SetupLoggingDetails -newSession $false
77117897
Confirm-UserAcknowledgmentToUpgradeOS -ClusterNodeSession $clusterNodeSession
77127898

77137899
$agentInstallType = (Get-ItemProperty -Path "HKLM:\SYSTEM\Software\Microsoft\AzureStack\Observability\RemoteSupport" -ErrorAction SilentlyContinue).InstallType
@@ -7809,7 +7995,7 @@ function Get-AzStackHCIRemoteSupportSessionHistory{
78097995
$FromDate = (Get-Date).AddDays(-7)
78107996
)
78117997

7812-
$_, $_, $clusterNodeSession, $_ = Get-SetupLoggingDetails
7998+
$_, $_, $clusterNodeSession, $_ = Get-SetupLoggingDetails -newSession $false
78137999
Confirm-UserAcknowledgmentToUpgradeOS -ClusterNodeSession $clusterNodeSession
78148000

78158001
$agentInstallType = (Get-ItemProperty -Path "HKLM:\SYSTEM\Software\Microsoft\AzureStack\Observability\RemoteSupport" -ErrorAction SilentlyContinue).InstallType

src/StackHCI/StackHCI/ChangeLog.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
- Additional information about change #1
1919
-->
2020
## Upcoming Release
21+
* ARC Enablement of Nodes Before Triggering Registration in New Registration Flow.
22+
* Resolved double hop authentication issue.
2123

2224
## Version 2.6.4
2325
* Fixed bug: Buse boolean in comparision

0 commit comments

Comments
 (0)