tools/modules/AksEdgeDeploy/AksEdgeDeploy-AES.ps1 (408 lines of code) (raw):
<#
.DESCRIPTION
This module contains the Arc functions to use on Edge Essentials platforms (AksEdgeDeploy-Arc)
#>
#Requires -RunAsAdministrator
if (! [Environment]::Is64BitProcess) {
Write-Host "Error: Run this in 64bit Powershell session" -ForegroundColor Red
exit -1
}
New-Variable -Option Constant -ErrorAction SilentlyContinue -Name azcmagentexe -Value "$env:ProgramW6432\AzureConnectedMachineAgent\azcmagent.exe"
New-Variable -Option Constant -ErrorAction SilentlyContinue -Name arciotEnvConfig -Value @{
"ArcIotSchema" = @("SubscriptionName", "SubscriptionId", "TenantId", "ResourceGroupName", "Location", "Auth")
}
New-Variable -option Constant -ErrorAction SilentlyContinue -Name arcLocations -Value @(
"westeurope", "eastus", "westcentralus", "southcentralus", "southeastasia", "uksouth",
"eastus2", "westus2", "australiaeast", "northeurope", "francecentral", "centralus",
"westus", "northcentralus", "koreacentral", "japaneast", "eastasia", "westus3",
"canadacentral", "eastus2euap"
)
function Get-AideArcUserConfig {
return (Get-AideUserConfig).Azure
}
function Test-AideArcUserConfig {
$retval = $true
$aicfg = Get-AideArcUserConfig
if (! $aicfg) {
Write-Host "Error: UserConfig not set. Use Set-AideUserConfig to set." -ForegroundColor Red
return $false
}
foreach ($key in $arciotEnvConfig.ArcIotSchema) {
if ($aicfg.$key) {
Write-Verbose "- $key ok"
} else {
Write-Host "Error: $key not specified" -ForegroundColor Red
$retval = $false
}
}
if ((-not $aicfg.Auth.ServicePrincipalId) -and (-not $aicfg.Auth.Password)) {
Write-Host "Error: Specify Auth parameters" -ForegroundColor Red
$retval = $false
}
if ($arcLocations -inotcontains $($aicfg.Location)){
Write-Host "Error: Location $($aicfg.Location) is not supported for Azure Arc" -ForegroundColor Red
Write-Host "Supported Locations : $arcLocations"
$retval = $false
}
return $retval
}
function Get-AideArcAzureCreds {
$cred = $null
$aicfg = Get-AideArcUserConfig
if ($aicfg.Auth) {
$aiauth = $aicfg.Auth
if ($aiauth.Password -and $aiauth.ServicePrincipalId) {
$cred = @{
"Username" = $aiauth.ServicePrincipalId
"Password" = $aiauth.Password
}
} else {
Write-Host "Error: ServicePrincipalId/Password not specified." -ForegroundColor Red
$cred = $null
}
}
return $cred
}
function Install-AideArcServer {
<#
.SYNOPSIS
Checks and installs connected machine agent.
.DESCRIPTION
This command tests if the connected machine agent is installed and installs using script from "https://aka.ms/azcmagent-windows".
This also sets up for auto update via Microsoft Update.
.OUTPUTS
Boolean
True when the install is successful
.EXAMPLE
Install-AideArcServer
#>
if (Test-IsAzureVM) { return $false }
if (Test-Path -Path $azcmagentexe -PathType Leaf) {
Write-Host "> ACMA is already installed" -ForegroundColor Green
& $azcmagentexe version
return $true
}
Write-Host "> Installing ACMA..."
Push-Location $env:TEMP
try {
# Download the installation package
Invoke-WebRequest -Uri "https://aka.ms/azcmagent-windows" -TimeoutSec 300 -OutFile "$env:TEMP\install_windows_azcmagent.ps1" -UseBasicParsing
# Install the hybrid agent
& "$env:TEMP\install_windows_azcmagent.ps1"
if ($LASTEXITCODE -ne 0) {
Write-Host "Error: Failed to install the ACMA agent : $LASTEXITCODE" -ForegroundColor Red
} else {
Write-Host "Setting up auto update via Microsoft Update"
$ServiceManager = (New-Object -com "Microsoft.Update.ServiceManager")
$ServiceID = "7971f918-a847-4430-9279-4a52d1efe18d"
$ServiceManager.AddService2($ServiceId, 7, "") | Out-Null
}
Remove-Item .\AzureConnectedMachineAgent.msi
if (Test-Path -Path $azcmagentexe -PathType Leaf) {
Write-Host "> ACMA is installed successfully" -ForegroundColor Green
& $azcmagentexe version
$retval = $true
} else {
Write-Host "Error: Install failed." -ForegroundColor Red
$retval = $false
}
} catch {
Write-Host "Error: Install failed." -ForegroundColor Red
$retval = $false
}
Pop-Location
return $retval
}
function Get-AideACMAStatus {
[cmdletbinding()]
param()
<#
.SYNOPSIS
Returns the status of the Azure Connected Machine Agent (acma).
.DESCRIPTION
This command tests if the connected machine agent is installed and connected to Azure Arc-enabled server.
.OUTPUTS
String
[Connected/Disconnected/NotInstalled]
.EXAMPLE
Get-AideACMAStatus
#>
$retval = "Disconnected"
if (!(Test-Path -Path $azcmagentexe -PathType Leaf)) {
Write-Verbose "ACMA is not installed"
$retval = "NotInstalled"
} else{
# Check if the machine is already connected
$agentstatus = (& $azcmagentexe show -j) | ConvertFrom-Json
$retval = $agentstatus.status
Write-Verbose "ACMA is $retval"
}
return $retval
}
function Test-AideArcServer {
[cmdletbinding()]
param()
<#
.SYNOPSIS
Tests if the connected machine agent is installed and connected to Azure Arc-enabled server.
.DESCRIPTION
This command tests if the connected machine agent is installed and connected to Azure Arc-enabled server.
The inputs required are consumed from the aide-userconfig.json file.
.OUTPUTS
Boolean
True when connected.
.EXAMPLE
Test-AideArcServer
#>
$retval = $false
$status = Get-AideACMAStatus
if ($status -eq "Connected") {
$retval = $true
}
return $retval
}
function Get-AideArcServerInfo {
<#
.SYNOPSIS
Returns Arc connection information for Arc-enabled server instance.
.DESCRIPTION
This command returns Arc connection information for Arc-enabled server instance from the local IMDS endpoint.
.OUTPUTS
Hashtable
Hashtable with the following keys :Status, Name,ResourceGroupName,SubscriptionId,Location.
.EXAMPLE
Get-AideArcServerInfo
#>
$vmInfo = @{}
$status = Get-AideACMAStatus
$vmInfo.Add("Status", $status)
if ($status -eq "Connected") {
$apiVersion = "2020-06-01"
$imdsEndpoint = [System.Environment]::GetEnvironmentVariable("IMDS_ENDPOINT","Machine")
$InstanceUri = $imdsEndpoint + "/metadata/instance?api-version=$apiVersion"
$Proxy = New-Object System.Net.WebProxy
$WebSession = New-Object Microsoft.PowerShell.Commands.WebRequestSession
$WebSession.Proxy = $Proxy
$response = (Invoke-RestMethod -Headers @{"Metadata" = "true"} -Method GET -Uri $InstanceUri -WebSession $WebSession)
$vmInfo.Add("Name", $response.compute.name)
$vmInfo.Add("ResourceGroupName", $response.compute.resourceGroupName)
$vmInfo.Add("SubscriptionId", $response.compute.subscriptionId)
$vmInfo.Add("Location", $response.compute.location)
}
return $vmInfo
}
function Connect-AideArcServer {
<#
.SYNOPSIS
Connects the machine to Azure Arc.
.DESCRIPTION
This command installs and connects Azure Arc Connected machine agent to Arc-enabled Server.
The inputs required are consumed from the aide-userconfig.json file.
.OUTPUTS
Boolean
True if the connection is successful.
.EXAMPLE
Connect-AideArcServer
#>
if (Test-IsAzureVM) {
Write-Host "Disabling WindowsAzureGuestAgent"
Disable-WindowsAzureGuestAgent
}
$status = Get-AideACMAStatus
if ($status -eq "Connected") {
Write-Host "ACMA is already connected." -ForegroundColor Green
return $true
}
if ($status -eq "NotInstalled" ) {
$retval = Install-AideArcServer
if (!$retval) { return $retval }
}
Write-Host "Connecting ConnectedMachine Agent now..."
$aicfg = Get-AideArcUserConfig
$creds = Get-AideArcAzureCreds
if (!$creds) {
Write-Host "Error: No valid credentials found. Connect not attempted." -ForegroundColor Red
return $false
}
$connectargs = @( "--resource-group", "$($aicfg.ResourceGroupName)",
"--tenant-id", "$($aicfg.TenantId)",
"--location", "$($aicfg.Location)",
"--subscription-id", "$($aicfg.SubscriptionId)",
"--cloud", "AzureCloud",
"--service-principal-id", "$($creds.Username)",
"--service-principal-secret", "$($creds.Password)"
)
$tags = @("--tags","SKU=AksEdgeEssentials")
if (Test-AksEdgeArcConnection) {
$clustername = Get-AideArcClusterName
$tags += @("--tags","AKSEE=$clustername")
}
$connectargs += $tags
if ($aicfg.ConnectedMachineName) {
$connectargs += @("--resource-name","$($aicfg.ConnectedMachineName)")
}
$hostSettings = Get-ItemProperty -Path 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings' | Select-Object ProxyServer, ProxyEnable
if ($hostSettings.ProxyEnable) {
& $azcmagentexe config set proxy.url $($hostSettings.ProxyServer)
}
& $azcmagentexe connect @connectargs
if ($LastExitCode -eq 0) {
Write-Host "ACMA connected."
} else {
Write-Host "Error in connecting to Azure: $LastExitCode" -ForegroundColor Red
return $false
}
return $true
}
function Disconnect-AideArcServer {
<#
.SYNOPSIS
Disconnects the machine from Azure Arc.
.DESCRIPTION
This command disconnects from Arc-enabled Server, if connected.
The inputs required are consumed from the aide-userconfig.json file.
.OUTPUTS
Boolean
True if the disconnection is successful.
.EXAMPLE
Disconnect-AideArcServer
#>
$status = Get-AideACMAStatus
if ($status -ne "Connected") {
Write-Host "ACMA is $status. Nothing to disconnect." -ForegroundColor Green
return $true
}
#check and unregister extensions
Remove-AideArcServerExtension
# disconnect
Write-Host "ACMA state is connected. Disconnecting now..."
# Get creds
$creds = Get-AideArcAzureCreds
if ($creds) {
$disconnectargs = @(
"--service-principal-id", "$($creds.Username)",
"--service-principal-secret", "$($creds.Password)"
)
& $azcmagentexe disconnect @disconnectargs
if ($LastExitCode -eq 0) {
Write-Host "ACMA disconnected."
return $true
} else {
Write-Host -ForegroundColor red "Error in disconnecting from Azure: $LastExitCode"
}
} else {
Write-Host "Error: No valid credentials found. Disconnect not attempted." -ForegroundColor Red
}
return $false
}
function Set-AideArcCmProxy {
<#
.SYNOPSIS
Sets the proxy server settings for the connected machine agent.
.DESCRIPTION
Sets the proxy server settings for the connected machine agent, if the agent is installed.
.OUTPUTS
None
.PARAMETER proxyUrl
This proxy url to set.
.EXAMPLE
Set-AideArcCmProxy -proxyUrl "https://myproxy:8080"
#>
Param(
[Parameter(Position = 0, Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[String]$proxyUrl
)
if (!(Test-Path -Path $azcmagentexe -PathType Leaf)) {
Write-Host "ACMA is not installed" -ForegroundColor Gray
return
}
& $azcmagentexe config set proxy.url $proxyUrl
}
function Get-AideArcServerSMI {
<#
.SYNOPSIS
Returns the System Managed Identity access token for the Arc-enabled Server instance.
.DESCRIPTION
This command the System Managed Identity access token for the Arc-enabled Server instance, queried from the local IMDS end point.
.OUTPUTS
String
.EXAMPLE
Get-AideArcServerSMI
#>
if (!(Test-AideArcServer)) { return $null }
$token = $null
$apiVersion = "2020-06-01"
$resource = "https://management.azure.com/"
$idEndpoint = [System.Environment]::GetEnvironmentVariable("IDENTITY_ENDPOINT","Machine")
$endpoint = "{0}?resource={1}&api-version={2}" -f $idEndpoint, $resource, $apiVersion
$secretFile = ""
try {
Invoke-WebRequest -Method GET -Uri $endpoint -Headers @{Metadata = 'True' } -UseBasicParsing
} catch {
$wwwAuthHeader = $_.Exception.Response.Headers["WWW-Authenticate"]
if ($wwwAuthHeader -match "Basic realm=.+") {
$secretFile = ($wwwAuthHeader -split "Basic realm=")[1]
}
}
Write-Verbose "Secret file path: " $secretFile`n
$secret = Get-Content -Raw $secretFile
$response = Invoke-WebRequest -Method GET -Uri $endpoint -Headers @{Metadata = 'True'; Authorization = "Basic $secret" } -UseBasicParsing
if ($response) {
$token = (ConvertFrom-Json -InputObject $response.Content).access_token
Write-Verbose "Access token: " $token
}
return $token
}
function New-AideArcServerExtension {
if (!(Test-AideArcServer)) { return }
$aicfg = Get-AideArcUserConfig
$protectedsettings = @'
{
"fileUris": [ filename.ps1 ]
"commandToExecute":"powershell.exe -ExecutionPolicy Unrestricted -File filename.ps1 "
}
'@
$arciotMachineName = hostname
$cmebaseargs = @(
"--machine-name", "$($arciotMachineName)",
"--name", "CustomScriptExtension",
"--resource-group", "$($aicfg.ResourceGroupName)"
)
$cmeargs = @(
"--location", "$($aicfg.Location)",
"--type", "CustomScriptExtension",
"--publisher", "Microsoft.HybridCompute",
"--protected-settings", "$($protectedsettings)",
"--type-handler-version", "1.10",
"--tags", "owner=ArcIot"
)
az connectedmachine extension create @cmebaseargs @cmeargs --no-wait
Write-Host "CustomScriptExtension creation in progress. Waiting..."
az connectedmachine extension wait @cmebaseargs --created
Write-Host "CustomScriptExtension created successfully"
}
function Remove-AideArcServerExtension {
if (!(Test-AideArcServer)) { return }
$aicfg = Get-AideArcUserConfig
$arciotMachineName = hostname
$cmeargs = @(
"--machine-name", "$($arciotMachineName)",
"--resource-group", "$($aicfg.ResourceGroupName)"
)
$extlist = (az connectedmachine extension list @cmeargs --query [].name) | ConvertFrom-Json -ErrorAction SilentlyContinue
if ($extlist) {
$extensions = [String]::Join(",", $extlist)
Write-Host "Found : $($extensions)"
# remove each extension
foreach ($ext in $extlist) {
Write-Host "Removing $ext extension"
az connectedmachine extension delete @cmeargs --name $ext --no-wait --yes
}
Write-Host "Waiting..."
az connectedmachine extension wait @cmebaseargs --deleted
Write-Host "Extension removal completed successfully"
} else {
Write-Host "No extensions to remove."
}
}