@@ -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<#
28793026Checks 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
0 commit comments