parts/windows/kuberneteswindowssetup.ps1 (411 lines of code) (raw):

<# .SYNOPSIS Provisions VM as a Kubernetes agent. .DESCRIPTION Provisions VM as a Kubernetes agent. The parameters passed in are required, and will vary per-deployment. Notes on modifying this file: - This file extension is PS1, but it is actually used as a template from pkg/engine/template_generator.go - All of the lines that have braces in them will be modified. Please do not change them here, change them in the Go sources - Single quotes are forbidden, they are reserved to delineate the different members for the ARM template concat() call - windowscsehelper.ps1 contains basic util functions. It will be compressed to a zip file and then be converted to base64 encoding string and stored in $zippedFiles. Reason: This script is a template and has some limitations. - All other scripts will be packaged and published in a single package. It will be downloaded in provisioning VM. Reason: CustomData has length limitation 87380. - ProvisioningScriptsPackage contains scripts to start kubelet, kubeproxy, etc. The source is https://github.com/Azure/aks-engine/tree/master/staging/provisioning/windows #> [CmdletBinding(DefaultParameterSetName="Standard")] param( [parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] $AgentKey, [parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] $AADClientSecret, # base64 # C:\AzureData\provision.complete # MUST keep generating this file when CSE is done and do not change the name # - It is used to avoid running CSE multiple times # - Some customers use this file to check if CSE is done [parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] $CSEResultFilePath ) # In an ideal world, all these values would be passed to this script in parameters. However, we don't live in an ideal world. # https://learn.microsoft.com/en-gb/troubleshoot/windows-client/shell-experience/command-line-string-limitation $MasterIP = "{{ GetKubernetesEndpoint }}" $KubeDnsServiceIp="{{ GetParameter "kubeDNSServiceIP" }}" $MasterFQDNPrefix="{{ GetParameter "masterEndpointDNSNamePrefix" }}" $Location="{{ GetVariable "location" }}" {{if UserAssignedIDEnabled}} $UserAssignedClientID="{{ GetVariable "userAssignedIdentityID" }}" {{ end }} $TargetEnvironment="{{ GetTargetEnvironment }}" $AADClientId="{{ GetParameter "servicePrincipalClientId" }}" $NetworkAPIVersion="2018-08-01" # Do not parse the start time from $LogFile to simplify the logic $StartTime=Get-Date $global:ExitCode=0 $global:ErrorMessage="" # These globals will not change between nodes in the same cluster, so they are not # passed as powershell parameters ## SSH public keys to add to authorized_keys $global:SSHKeys = @( {{ GetSshPublicKeysPowerShell }} ) ## Certificates generated by aks-engine $global:CACertificate = "{{GetParameter "caCertificate"}}" $global:AgentCertificate = "{{GetParameter "clientCertificate"}}" ## Download sources provided by aks-engine $global:KubeBinariesPackageSASURL = "{{GetParameter "kubeBinariesSASURL"}}" $global:WindowsKubeBinariesURL = "{{GetParameter "windowsKubeBinariesURL"}}" $global:KubeBinariesVersion = "{{GetParameter "kubeBinariesVersion"}}" $global:ContainerdUrl = "{{GetParameter "windowsContainerdURL"}}" $global:ContainerdSdnPluginUrl = "{{GetParameter "windowsSdnPluginURL"}}" ## Docker Version $global:DockerVersion = "{{GetParameter "windowsDockerVersion"}}" ## ContainerD Usage $global:DefaultContainerdWindowsSandboxIsolation = "{{GetParameter "defaultContainerdWindowsSandboxIsolation"}}" $global:ContainerdWindowsRuntimeHandlers = "{{GetParameter "containerdWindowsRuntimeHandlers"}}" ## VM configuration passed by Azure $global:WindowsTelemetryGUID = "{{GetParameter "windowsTelemetryGUID"}}" {{if eq GetIdentitySystem "adfs"}} $global:TenantId = "adfs" {{else}} $global:TenantId = "{{GetVariable "tenantID"}}" {{end}} $global:SubscriptionId = "{{GetVariable "subscriptionId"}}" $global:ResourceGroup = "{{GetVariable "resourceGroup"}}" $global:VmType = "{{GetVariable "vmType"}}" $global:SubnetName = "{{GetVariable "subnetName"}}" # NOTE: MasterSubnet is still referenced by `kubeletstart.ps1` and `windowsnodereset.ps1` # for case of Kubenet $global:MasterSubnet = "" $global:SecurityGroupName = "{{GetVariable "nsgName"}}" $global:VNetName = "{{GetVariable "virtualNetworkName"}}" $global:RouteTableName = "{{GetVariable "routeTableName"}}" $global:PrimaryAvailabilitySetName = "{{GetVariable "primaryAvailabilitySetName"}}" $global:PrimaryScaleSetName = "{{GetVariable "primaryScaleSetName"}}" $global:KubeClusterCIDR = "{{GetParameter "kubeClusterCidr"}}" $global:KubeServiceCIDR = "{{GetParameter "kubeServiceCidr"}}" $global:VNetCIDR = "{{GetParameter "vnetCidr"}}" {{if IsKubernetesVersionGe "1.16.0"}} $global:KubeletNodeLabels = "{{GetAgentKubernetesLabels . }}" {{else}} $global:KubeletNodeLabels = "{{GetAgentKubernetesLabelsDeprecated . }}" {{end}} $global:KubeletConfigArgs = @( {{GetKubeletConfigKeyValsPsh}} ) $global:KubeproxyConfigArgs = @( {{GetKubeproxyConfigKeyValsPsh}} ) $global:KubeproxyFeatureGates = @( {{GetKubeProxyFeatureGatesPsh}} ) $global:UseManagedIdentityExtension = "{{GetVariable "useManagedIdentityExtension"}}" $global:UseInstanceMetadata = "{{GetVariable "useInstanceMetadata"}}" $global:LoadBalancerSku = "{{GetVariable "loadBalancerSku"}}" $global:ExcludeMasterFromStandardLB = "{{GetVariable "excludeMasterFromStandardLB"}}" $global:PrivateEgressProxyAddress = "{{GetPrivateEgressProxyAddress}}" # Windows defaults, not changed by aks-engine $global:CacheDir = "c:\akse-cache" $global:KubeDir = "c:\k" $global:HNSModule = [Io.path]::Combine("$global:KubeDir", "hns.v2.psm1") $global:KubeDnsSearchPath = "svc.cluster.local" $global:CNIPath = [Io.path]::Combine("$global:KubeDir", "cni") $global:NetworkMode = "L2Bridge" $global:CNIConfig = [Io.path]::Combine($global:CNIPath, "config", "`$global:NetworkMode.conf") $global:CNIConfigPath = [Io.path]::Combine("$global:CNIPath", "config") $global:AzureCNIDir = [Io.path]::Combine("$global:KubeDir", "azurecni") $global:AzureCNIBinDir = [Io.path]::Combine("$global:AzureCNIDir", "bin") $global:AzureCNIConfDir = [Io.path]::Combine("$global:AzureCNIDir", "netconf") # Azure cni configuration # $global:NetworkPolicy = "{{GetParameter "networkPolicy"}}" # BUG: unused $global:NetworkPlugin = "{{GetParameter "networkPlugin"}}" $global:VNetCNIPluginsURL = "{{GetParameter "vnetCniWindowsPluginsURL"}}" $global:IsDualStackEnabled = {{if IsIPv6DualStackFeatureEnabled}}$true{{else}}$false{{end}} $global:IsAzureCNIOverlayEnabled = {{if IsAzureCNIOverlayFeatureEnabled}}$true{{else}}$false{{end}} $global:CiliumDataplaneEnabled = {{if CiliumDataplaneEnabled}}$true{{else}}$false{{end}} # Kubelet credential provider $global:CredentialProviderURL = "{{GetParameter "windowsCredentialProviderURL"}}" # CSI Proxy settings $global:EnableCsiProxy = [System.Convert]::ToBoolean("{{GetVariable "windowsEnableCSIProxy" }}"); $global:CsiProxyUrl = "{{GetVariable "windowsCSIProxyURL" }}"; # Hosts Config Agent settings $global:EnableHostsConfigAgent = [System.Convert]::ToBoolean("{{ EnableHostsConfigAgent }}"); # These scripts are used by cse $global:CSEScriptsPackageUrl = "{{GetVariable "windowsCSEScriptsPackageURL" }}"; # The windows nvidia gpu driver related url is used by windows cse $global:GpuDriverURL = "{{GetVariable "windowsGpuDriverURL" }}"; # PauseImage $global:WindowsPauseImageURL = "{{GetVariable "windowsPauseImageURL" }}"; $global:AlwaysPullWindowsPauseImage = [System.Convert]::ToBoolean("{{GetVariable "alwaysPullWindowsPauseImage" }}"); # Calico $global:WindowsCalicoPackageURL = "{{GetVariable "windowsCalicoPackageURL" }}"; ## GPU install $global:ConfigGPUDriverIfNeeded = [System.Convert]::ToBoolean("{{GetVariable "configGPUDriverIfNeeded" }}"); # GMSA $global:WindowsGmsaPackageUrl = "{{GetVariable "windowsGmsaPackageUrl" }}"; # TLS Bootstrap Token $global:TLSBootstrapToken = "{{GetTLSBootstrapTokenForKubeConfig}}" # Disable OutBoundNAT in Azure CNI configuration $global:IsDisableWindowsOutboundNat = [System.Convert]::ToBoolean("{{GetVariable "isDisableWindowsOutboundNat" }}"); # Base64 representation of ZIP archive $zippedFiles = "{{ GetKubernetesWindowsAgentFunctions }}" $global:KubeClusterConfigPath = "c:\k\kubeclusterconfig.json" $fipsEnabled = [System.Convert]::ToBoolean("{{ FIPSEnabled }}") # HNS remediator $global:HNSRemediatorIntervalInMinutes = [System.Convert]::ToUInt32("{{GetHnsRemediatorIntervalInMinutes}}"); # Log generator $global:LogGeneratorIntervalInMinutes = [System.Convert]::ToUInt32("{{GetLogGeneratorIntervalInMinutes}}"); $global:EnableIncreaseDynamicPortRange = $false $global:RebootNeeded = $false $global:IsSkipCleanupNetwork = [System.Convert]::ToBoolean("{{GetVariable "isSkipCleanupNetwork" }}"); $global:EnableKubeletServingCertificateRotation = [System.Convert]::ToBoolean("{{EnableKubeletServingCertificateRotation}}") # Extract cse helper script from ZIP [io.file]::WriteAllBytes("scripts.zip", [System.Convert]::FromBase64String($zippedFiles)) Expand-Archive scripts.zip -DestinationPath "C:\\AzureData\\" -Force # Dot-source windowscsehelper.ps1 with functions that are called in this script . c:\AzureData\windows\windowscsehelper.ps1 # util functions only can be used after this line, for example, Write-Log $global:OperationId = New-Guid try { Logs-To-Event -TaskName "AKS.WindowsCSE.ExecuteCustomDataSetupScript" -TaskMessage ".\CustomDataSetupScript.ps1 -MasterIP $MasterIP -KubeDnsServiceIp $KubeDnsServiceIp -MasterFQDNPrefix $MasterFQDNPrefix -Location $Location -AADClientId $AADClientId -NetworkAPIVersion $NetworkAPIVersion -TargetEnvironment $TargetEnvironment -CSEResultFilePath $CSEResultFilePath" # Exit early if the script has been executed if (Test-Path -Path $CSEResultFilePath -PathType Leaf) { Write-Log "The script has been executed before, will exit without doing anything." return } # This involes using proxy, log the config before fetching packages Write-Log "private egress proxy address is '$global:PrivateEgressProxyAddress'" # TODO update to use proxy $WindowsCSEScriptsPackage = "aks-windows-cse-scripts-v0.0.52.zip" Write-Log "CSEScriptsPackageUrl is $global:CSEScriptsPackageUrl" Write-Log "WindowsCSEScriptsPackage is $WindowsCSEScriptsPackage" # Old AKS RP sets the full URL (https://acs-mirror.azureedge.net/aks/windows/cse/aks-windows-cse-scripts-v0.0.11.zip) in CSEScriptsPackageUrl # but it is better to set the CSE package version in Windows CSE in AgentBaker # since most changes in CSE package also need the change in Windows CSE in AgentBaker # In future, AKS RP only sets the endpoint with the pacakge name, for example, https://acs-mirror.azureedge.net/aks/windows/cse/ if ($global:CSEScriptsPackageUrl.EndsWith("/")) { $global:CSEScriptsPackageUrl = $global:CSEScriptsPackageUrl + $WindowsCSEScriptsPackage Write-Log "CSEScriptsPackageUrl is set to $global:CSEScriptsPackageUrl" } # Download CSE function scripts Logs-To-Event -TaskName "AKS.WindowsCSE.DownloadAndExpandCSEScriptPackageUrl" -TaskMessage "Start to get CSE scripts. CSEScriptsPackageUrl: $global:CSEScriptsPackageUrl" $tempfile = 'c:\csescripts.zip' DownloadFileOverHttp -Url $global:CSEScriptsPackageUrl -DestinationPath $tempfile -ExitCode $global:WINDOWS_CSE_ERROR_DOWNLOAD_CSE_PACKAGE Expand-Archive $tempfile -DestinationPath "C:\\AzureData\\windows" -Force Remove-Item -Path $tempfile -Force # Dot-source cse scripts with functions that are called in this script . c:\AzureData\windows\azurecnifunc.ps1 . c:\AzureData\windows\calicofunc.ps1 . c:\AzureData\windows\configfunc.ps1 . c:\AzureData\windows\containerdfunc.ps1 . c:\AzureData\windows\kubeletfunc.ps1 . c:\AzureData\windows\kubernetesfunc.ps1 . c:\AzureData\windows\nvidiagpudriverfunc.ps1 # Install OpenSSH if SSH enabled $sshEnabled = [System.Convert]::ToBoolean("{{ WindowsSSHEnabled }}") if ( $sshEnabled ) { Install-OpenSSH -SSHKeys $SSHKeys } Set-TelemetrySetting -WindowsTelemetryGUID $global:WindowsTelemetryGUID Resize-OSDrive Initialize-DataDisks Initialize-DataDirectories Logs-To-Event -TaskName "AKS.WindowsCSE.GetProvisioningAndLogCollectionScripts" -TaskMessage "Start to get provisioning scripts and log collection scripts" Create-Directory -FullPath "c:\k" Write-Log "Remove `"NT AUTHORITY\Authenticated Users`" write permissions on files in c:\k" icacls.exe "c:\k" /inheritance:r icacls.exe "c:\k" /grant:r SYSTEM:`(OI`)`(CI`)`(F`) icacls.exe "c:\k" /grant:r BUILTIN\Administrators:`(OI`)`(CI`)`(F`) icacls.exe "c:\k" /grant:r BUILTIN\Users:`(OI`)`(CI`)`(RX`) Write-Log "c:\k permissions: " icacls.exe "c:\k" Get-ProvisioningScripts Get-LogCollectionScripts # NOTE: this function MUST be called before Write-KubeClusterConfig since it has the potential # to mutate both kubelet config args and kubelet node labels. Configure-KubeletServingCertificateRotation Write-KubeClusterConfig -MasterIP $MasterIP -KubeDnsServiceIp $KubeDnsServiceIp Install-CredentialProvider -KubeDir $global:KubeDir -CustomCloudContainerRegistryDNSSuffix {{if IsAKSCustomCloud}}"{{ AKSCustomCloudContainerRegistryDNSSuffix }}"{{else}}""{{end}} Get-KubePackage -KubeBinariesSASURL $global:KubeBinariesPackageSASURL $cniBinPath = $global:AzureCNIBinDir $cniConfigPath = $global:AzureCNIConfDir if ($global:NetworkPlugin -eq "kubenet") { $cniBinPath = $global:CNIPath $cniConfigPath = $global:CNIConfigPath } Install-Containerd-Based-On-Kubernetes-Version -ContainerdUrl $global:ContainerdUrl -CNIBinDir $cniBinPath -CNIConfDir $cniConfigPath -KubeDir $global:KubeDir -KubernetesVersion $global:KubeBinariesVersion Retag-ImagesForAzureChinaCloud -TargetEnvironment $TargetEnvironment # For AKSClustomCloud, TargetEnvironment must be set to AzureStackCloud Write-AzureConfig ` -KubeDir $global:KubeDir ` -AADClientId $AADClientId ` -AADClientSecret $([System.Text.Encoding]::ASCII.GetString([System.Convert]::FromBase64String($AADClientSecret))) ` -TenantId $global:TenantId ` -SubscriptionId $global:SubscriptionId ` -ResourceGroup $global:ResourceGroup ` -Location $Location ` -VmType $global:VmType ` -SubnetName $global:SubnetName ` -SecurityGroupName $global:SecurityGroupName ` -VNetName $global:VNetName ` -RouteTableName $global:RouteTableName ` -PrimaryAvailabilitySetName $global:PrimaryAvailabilitySetName ` -PrimaryScaleSetName $global:PrimaryScaleSetName ` -UseManagedIdentityExtension $global:UseManagedIdentityExtension ` -UserAssignedClientID $UserAssignedClientID ` -UseInstanceMetadata $global:UseInstanceMetadata ` -LoadBalancerSku $global:LoadBalancerSku ` -ExcludeMasterFromStandardLB $global:ExcludeMasterFromStandardLB ` -TargetEnvironment {{if IsAKSCustomCloud}}"AzureStackCloud"{{else}}$TargetEnvironment{{end}} # we borrow the logic of AzureStackCloud to achieve AKSCustomCloud. # In case of AKSCustomCloud, customer cloud env will be loaded from azurestackcloud.json {{if IsAKSCustomCloud}} $azureStackConfigFile = [io.path]::Combine($global:KubeDir, "azurestackcloud.json") $envJSON = "{{ GetBase64EncodedEnvironmentJSON }}" [io.file]::WriteAllBytes($azureStackConfigFile, [System.Convert]::FromBase64String($envJSON)) Get-CACertificates {{end}} Write-CACert -CACertificate $global:CACertificate ` -KubeDir $global:KubeDir if ($global:EnableCsiProxy) { New-CsiProxyService -CsiProxyPackageUrl $global:CsiProxyUrl -KubeDir $global:KubeDir } if ($global:TLSBootstrapToken) { Write-BootstrapKubeConfig -CACertificate $global:CACertificate ` -KubeDir $global:KubeDir ` -MasterFQDNPrefix $MasterFQDNPrefix ` -MasterIP $MasterIP ` -TLSBootstrapToken $global:TLSBootstrapToken # NOTE: we need kubeconfig to setup calico even if TLS bootstrapping is enabled # This kubeconfig will deleted after calico installation. # TODO(hbc): once TLS bootstrap is fully enabled, remove this if block Write-Log "Write temporary kube config" } else { Write-Log "Write kube config" } Write-KubeConfig -CACertificate $global:CACertificate ` -KubeDir $global:KubeDir ` -MasterFQDNPrefix $MasterFQDNPrefix ` -MasterIP $MasterIP ` -AgentKey $AgentKey ` -AgentCertificate $global:AgentCertificate if ($global:EnableHostsConfigAgent) { New-HostsConfigService } Write-Log "Configuring networking with NetworkPlugin:$global:NetworkPlugin" # Configure network policy. Get-HnsPsm1 -HNSModule $global:HNSModule Import-Module $global:HNSModule Install-VnetPlugins -AzureCNIConfDir $global:AzureCNIConfDir ` -AzureCNIBinDir $global:AzureCNIBinDir ` -VNetCNIPluginsURL $global:VNetCNIPluginsURL Set-AzureCNIConfig -AzureCNIConfDir $global:AzureCNIConfDir ` -KubeDnsSearchPath $global:KubeDnsSearchPath ` -KubeClusterCIDR $global:KubeClusterCIDR ` -KubeServiceCIDR $global:KubeServiceCIDR ` -VNetCIDR $global:VNetCIDR ` -IsDualStackEnabled $global:IsDualStackEnabled ` -IsAzureCNIOverlayEnabled $global:IsAzureCNIOverlayEnabled if ($TargetEnvironment -ieq "AzureStackCloud") { GenerateAzureStackCNIConfig ` -TenantId $global:TenantId ` -SubscriptionId $global:SubscriptionId ` -ResourceGroup $global:ResourceGroup ` -AADClientId $AADClientId ` -KubeDir $global:KubeDir ` -AADClientSecret $([System.Text.Encoding]::ASCII.GetString([System.Convert]::FromBase64String($AADClientSecret))) ` -NetworkAPIVersion $NetworkAPIVersion ` -AzureEnvironmentFilePath $([io.path]::Combine($global:KubeDir, "azurestackcloud.json")) ` -IdentitySystem "{{ GetIdentitySystem }}" } New-ExternalHnsNetwork -IsDualStackEnabled $global:IsDualStackEnabled Install-KubernetesServices ` -KubeDir $global:KubeDir Set-Explorer Adjust-PageFileSize Logs-To-Event -TaskName "AKS.WindowsCSE.PreprovisionExtension" -TaskMessage "Start preProvisioning script" PREPROVISION_EXTENSION Update-ServiceFailureActions Adjust-DynamicPortRange Register-LogsCleanupScriptTask Register-NodeResetScriptTask Update-DefenderPreferences $windowsVersion = Get-WindowsVersion if ($windowsVersion -ne "1809") { Logs-To-Event -TaskName "AKS.WindowsCSE.EnableSecureTLS" -TaskMessage "Skip secure TLS protocols for Windows version: $windowsVersion" } else { Logs-To-Event -TaskName "AKS.WindowsCSE.EnableSecureTLS" -TaskMessage "Start to enable secure TLS protocols" try { . C:\k\windowssecuretls.ps1 Enable-SecureTls } catch { Set-ExitCode -ExitCode $global:WINDOWS_CSE_ERROR_ENABLE_SECURE_TLS -ErrorMessage $_ } } Enable-FIPSMode -FipsEnabled $fipsEnabled if ($global:WindowsGmsaPackageUrl) { Install-GmsaPlugin -GmsaPackageUrl $global:WindowsGmsaPackageUrl } Check-APIServerConnectivity -MasterIP $MasterIP if ($global:WindowsCalicoPackageURL) { Start-InstallCalico -RootDir "c:\" -KubeServiceCIDR $global:KubeServiceCIDR -KubeDnsServiceIp $KubeDnsServiceIp } Start-InstallGPUDriver -EnableInstall $global:ConfigGPUDriverIfNeeded -GpuDriverURL $global:GpuDriverURL if (Test-Path $CacheDir) { Write-Log "Removing aks cache directory" Remove-Item $CacheDir -Recurse -Force } if ($global:TLSBootstrapToken) { Write-Log "Removing temporary kube config" $kubeConfigFile = [io.path]::Combine($KubeDir, "config") Remove-Item $kubeConfigFile } Enable-GuestVMLogs -IntervalInMinutes $global:LogGeneratorIntervalInMinutes if ($global:RebootNeeded) { Logs-To-Event -TaskName "AKS.WindowsCSE.RestartComputer" -TaskMessage "Setup Complete, calling Postpone-RestartComputer with reboot" Postpone-RestartComputer } else { Logs-To-Event -TaskName "AKS.WindowsCSE.StartScheduledTask" -TaskMessage "Setup Complete, start NodeResetScriptTask to register Windows node without reboot" Start-ScheduledTask -TaskName "k8s-restart-job" $timeout = 180 ## seconds $timer = [Diagnostics.Stopwatch]::StartNew() while ((Get-ScheduledTask -TaskName 'k8s-restart-job').State -ne 'Ready') { # The task `k8s-restart-job` needs ~8 seconds. if ($timer.Elapsed.TotalSeconds -gt $timeout) { Set-ExitCode -ExitCode $global:WINDOWS_CSE_ERROR_START_NODE_RESET_SCRIPT_TASK -ErrorMessage "NodeResetScriptTask is not finished after [$($timer.Elapsed.TotalSeconds)] seconds" } Write-Log -Message "Waiting on NodeResetScriptTask..." Start-Sleep -Seconds 3 } $timer.Stop() Write-Log -Message "We waited [$($timer.Elapsed.TotalSeconds)] seconds on NodeResetScriptTask" } } catch { # Set-ExitCode will exit with the specified ExitCode immediately and not be caught by this catch block # Ideally all exceptions will be handled and no exception will be thrown. Set-ExitCode -ExitCode $global:WINDOWS_CSE_ERROR_UNKNOWN -ErrorMessage $_ } finally { # Generate CSE result so it can be returned as the CSE response in csecmd.ps1 $ExecutionDuration=$(New-Timespan -Start $StartTime -End $(Get-Date)) Write-Log "CSE ExecutionDuration: $ExecutionDuration. ExitCode: $global:ExitCode" Logs-To-Event -TaskName "AKS.WindowsCSE.cse_main" -TaskMessage "ExitCode: $global:ExitCode. ErrorMessage: $global:ErrorMessage." # $CSEResultFilePath is used to avoid running CSE multiple times if ($global:ExitCode -ne 0) { # $JsonString = "ExitCode: |{0}|, Output: |{1}|, Error: |{2}|" # Max length of the full error message returned by Windows CSE is ~256. We use 240 to be safe. $errorMessageLength = "ExitCode: |$global:ExitCode|, Output: |$($global:ErrorCodeNames[$global:ExitCode])|, Error: ||".Length $turncatedErrorMessage = $global:ErrorMessage.Substring(0, [Math]::Min(240 - $errorMessageLength, $global:ErrorMessage.Length)) Set-Content -Path $CSEResultFilePath -Value "ExitCode: |$global:ExitCode|, Output: |$($global:ErrorCodeNames[$global:ExitCode])|, Error: |$turncatedErrorMessage|" } else { Set-Content -Path $CSEResultFilePath -Value $global:ExitCode -Force } if ($global:ExitCode -eq $global:WINDOWS_CSE_ERROR_DOWNLOAD_CSE_PACKAGE) { Write-Log "Do not call Upload-GuestVMLogs because there is no cse script package downloaded" } else { Upload-GuestVMLogs -ExitCode $global:ExitCode } }