mirror of
https://github.com/actions/runner-images-sangeeth.git
synced 2025-12-10 11:41:32 +00:00
Remove obsolete ADO pipelines and scripts (#11640)
This commit is contained in:
@@ -1,14 +0,0 @@
|
|||||||
param(
|
|
||||||
[String] [Parameter (Mandatory=$true)] $RepoUrl,
|
|
||||||
[String] [Parameter (Mandatory=$true)] $RepoBranch
|
|
||||||
)
|
|
||||||
|
|
||||||
Write-Host "Clean up default repository"
|
|
||||||
Remove-Item -path './*' -Recurse -Force
|
|
||||||
|
|
||||||
Write-Host "Download ${RepoBranch} branch from ${RepoUrl}"
|
|
||||||
$env:GIT_REDIRECT_STDERR = '2>&1'
|
|
||||||
git clone $RepoUrl . -b $RepoBranch --single-branch --depth 1
|
|
||||||
|
|
||||||
Write-Host "Latest commit:"
|
|
||||||
git --no-pager log --pretty=format:"Date: %cd; Commit: %H - %s; Author: %an <%ae>" -1
|
|
||||||
@@ -1,181 +0,0 @@
|
|||||||
# Ideally we would use GitHub Actions for this, but since we use self-hosted machines to run image builds
|
|
||||||
# we need the following features to use GitHub Actions for Images CI:
|
|
||||||
# - https://github.community/t5/GitHub-Actions/Make-secrets-available-to-builds-of-forks/m-p/30678#M508
|
|
||||||
# - https://github.community/t5/GitHub-Actions/GitHub-Actions-Manual-Trigger-Approvals/td-p/31504
|
|
||||||
# - https://github.community/t5/GitHub-Actions/Protecting-github-workflows/td-p/30290
|
|
||||||
|
|
||||||
parameters:
|
|
||||||
- name: job_id
|
|
||||||
type: string
|
|
||||||
default: 'generate_image'
|
|
||||||
|
|
||||||
- name: image_type
|
|
||||||
type: string
|
|
||||||
|
|
||||||
- name: image_template_name
|
|
||||||
type: string
|
|
||||||
|
|
||||||
- name: image_readme_name
|
|
||||||
type: string
|
|
||||||
|
|
||||||
- name: agent_pool
|
|
||||||
type: object
|
|
||||||
default:
|
|
||||||
name: 'ci-agent-pool'
|
|
||||||
|
|
||||||
- name: variable_group_name
|
|
||||||
type: string
|
|
||||||
default: 'Image Generation Variables'
|
|
||||||
|
|
||||||
- name: create_release
|
|
||||||
type: boolean
|
|
||||||
default: true
|
|
||||||
|
|
||||||
- name: repository_ref
|
|
||||||
type: string
|
|
||||||
default: 'self'
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
- job: ${{ parameters.job_id }}
|
|
||||||
displayName: Image Generation (${{ parameters.image_type }})
|
|
||||||
timeoutInMinutes: 600
|
|
||||||
cancelTimeoutInMinutes: 30
|
|
||||||
pool: ${{ parameters.agent_pool }}
|
|
||||||
variables:
|
|
||||||
- group: ${{ parameters.variable_group_name }}
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- checkout: ${{ parameters.repository_ref }}
|
|
||||||
clean: true
|
|
||||||
fetchDepth: 0
|
|
||||||
fetchTags: false
|
|
||||||
|
|
||||||
- task: PowerShell@2
|
|
||||||
displayName: 'Download custom repository'
|
|
||||||
condition: and(ne(variables['CUSTOM_REPOSITORY_URL'], ''), ne(variables['CUSTOM_REPOSITORY_BRANCH'], ''))
|
|
||||||
inputs:
|
|
||||||
targetType: 'filePath'
|
|
||||||
filePath: ./images.CI/download-repo.ps1
|
|
||||||
arguments: -RepoUrl $(CUSTOM_REPOSITORY_URL) `
|
|
||||||
-RepoBranch $(CUSTOM_REPOSITORY_BRANCH)
|
|
||||||
|
|
||||||
- task: AzureCLI@2
|
|
||||||
displayName: 'Set variables'
|
|
||||||
inputs:
|
|
||||||
azureSubscription: 'spn-hosted-runners'
|
|
||||||
scriptType: 'pscore'
|
|
||||||
scriptLocation: 'inlineScript'
|
|
||||||
inlineScript: |
|
|
||||||
$ImageType = "${{ parameters.image_type }}"
|
|
||||||
$TemplateDirectoryName = if ($ImageType.StartsWith("ubuntu")) { "ubuntu/templates" } else { "windows/templates" }
|
|
||||||
$TemplateDirectoryPath = Join-Path "images" $TemplateDirectoryName | Resolve-Path
|
|
||||||
|
|
||||||
$TemplateFileName = "${{ parameters.image_template_name }}"
|
|
||||||
$TemplatePath = Join-Path $TemplateDirectoryPath $TemplateFileName
|
|
||||||
Write-Host "##vso[task.setvariable variable=TemplateDirectoryPath;]$TemplateDirectoryPath"
|
|
||||||
Write-Host "##vso[task.setvariable variable=TemplatePath;]$TemplatePath"
|
|
||||||
|
|
||||||
$ManagedImageName = "${{ parameters.image_type }}-$(Build.BuildId)"
|
|
||||||
Write-Host "##vso[task.setvariable variable=ManagedImageName;]$ManagedImageName"
|
|
||||||
|
|
||||||
$TempResourceGroupName = "packer-temp-$ManagedImageName"
|
|
||||||
Write-Host "##vso[task.setvariable variable=TempResourceGroupName;]$TempResourceGroupName"
|
|
||||||
|
|
||||||
$clientSecret = $(az keyvault secret show --name "spnhostedrunners" --vault-name "gh-imagegeneration" --query value -o tsv)
|
|
||||||
Write-Host "##vso[task.setvariable variable=ClientSecret;issecret=true]$clientSecret"
|
|
||||||
|
|
||||||
- task: PowerShell@2
|
|
||||||
displayName: 'Build VM'
|
|
||||||
inputs:
|
|
||||||
targetType: filePath
|
|
||||||
filePath: ./images.CI/linux-and-win/build-image.ps1
|
|
||||||
arguments: -ClientId $(CLIENT_ID) `
|
|
||||||
-ClientSecret "$(ClientSecret)" `
|
|
||||||
-TemplatePath $(TemplatePath) `
|
|
||||||
-ImageName "$(ManagedImageName)" `
|
|
||||||
-ImageResourceGroupName $(AZURE_RESOURCE_GROUP) `
|
|
||||||
-TempResourceGroupName "$(TempResourceGroupName)" `
|
|
||||||
-SubscriptionId $(AZURE_SUBSCRIPTION) `
|
|
||||||
-TenantId $(AZURE_TENANT) `
|
|
||||||
-Location $(AZURE_LOCATION) `
|
|
||||||
-VirtualNetworkName $(BUILD_AGENT_VNET_NAME) `
|
|
||||||
-VirtualNetworkRG $(BUILD_AGENT_VNET_RESOURCE_GROUP) `
|
|
||||||
-VirtualNetworkSubnet $(BUILD_AGENT_SUBNET_NAME)
|
|
||||||
|
|
||||||
env:
|
|
||||||
PACKER_LOG: 1
|
|
||||||
PACKER_LOG_PATH: "$(Agent.TempDirectory)/packer-log.txt"
|
|
||||||
|
|
||||||
- task: PowerShell@2
|
|
||||||
displayName: 'Copy image artifacts to the separate directory'
|
|
||||||
inputs:
|
|
||||||
targetType: 'inline'
|
|
||||||
script: |
|
|
||||||
$ImageType = "${{ parameters.image_type }}"
|
|
||||||
$rootDirectoryName = if ($ImageType.StartsWith("ubuntu")) { "ubuntu" } else { "windows" }
|
|
||||||
$rootDirectoryPath = Join-Path "images" $rootDirectoryName | Resolve-Path
|
|
||||||
|
|
||||||
$readmePath = Join-Path $rootDirectoryPath "${{ parameters.image_readme_name }}"
|
|
||||||
$softwareReportPath = Join-Path $rootDirectoryPath "software-report.json"
|
|
||||||
|
|
||||||
Copy-Item -Path $readmePath -Destination "$(Build.ArtifactStagingDirectory)/"
|
|
||||||
if (Test-Path $softwareReportPath) {
|
|
||||||
Copy-Item -Path $softwareReportPath -Destination "$(Build.ArtifactStagingDirectory)/"
|
|
||||||
}
|
|
||||||
|
|
||||||
- task: PowerShell@2
|
|
||||||
displayName: 'Print markdown software report'
|
|
||||||
inputs:
|
|
||||||
targetType: 'inline'
|
|
||||||
script: |
|
|
||||||
Get-Content -Path "$(Build.ArtifactStagingDirectory)/${{ parameters.image_readme_name }}"
|
|
||||||
|
|
||||||
- task: PowerShell@2
|
|
||||||
displayName: 'Print json software report'
|
|
||||||
inputs:
|
|
||||||
targetType: 'inline'
|
|
||||||
script: |
|
|
||||||
$softwareReportPath = "$(Build.ArtifactStagingDirectory)/software-report.json"
|
|
||||||
if (Test-Path $softwareReportPath) {
|
|
||||||
Get-Content -Path $softwareReportPath
|
|
||||||
}
|
|
||||||
|
|
||||||
- task: PublishBuildArtifacts@1
|
|
||||||
inputs:
|
|
||||||
ArtifactName: 'Built_VM_Artifacts'
|
|
||||||
displayName: Publish Artifacts
|
|
||||||
|
|
||||||
- task: PowerShell@2
|
|
||||||
displayName: 'Print provisioners duration'
|
|
||||||
inputs:
|
|
||||||
targetType: 'filePath'
|
|
||||||
filePath: ./images.CI/measure-provisioners-duration.ps1
|
|
||||||
arguments: -PackerLogPath "$(Agent.TempDirectory)/packer-log.txt" `
|
|
||||||
-PrefixToPathTrim "$(TemplateDirectoryPath)" `
|
|
||||||
-PrintTopNLongest 25
|
|
||||||
|
|
||||||
- ${{ if eq(parameters.create_release, true) }}:
|
|
||||||
- task: PowerShell@2
|
|
||||||
displayName: 'Create release for VM deployment'
|
|
||||||
inputs:
|
|
||||||
targetType: filePath
|
|
||||||
filePath: ./images.CI/linux-and-win/create-release.ps1
|
|
||||||
arguments: -BuildId $(Build.BuildId) `
|
|
||||||
-Organization $(RELEASE_TARGET_ORGANIZATION) `
|
|
||||||
-DefinitionId $(RELEASE_TARGET_DEFINITION_ID) `
|
|
||||||
-Project $(RELEASE_TARGET_PROJECT) `
|
|
||||||
-ImageType "${{ parameters.image_type }}" `
|
|
||||||
-ManagedImageName "$(ManagedImageName)" `
|
|
||||||
-AccessToken $(RELEASE_TARGET_TOKEN)
|
|
||||||
|
|
||||||
- task: PowerShell@2
|
|
||||||
displayName: 'Clean up resources'
|
|
||||||
condition: always()
|
|
||||||
inputs:
|
|
||||||
targetType: filePath
|
|
||||||
filePath: ./images.CI/linux-and-win/cleanup.ps1
|
|
||||||
arguments: -TempResourceGroupName "$(TempResourceGroupName)" `
|
|
||||||
-SubscriptionId $(AZURE_SUBSCRIPTION) `
|
|
||||||
-ClientId $(CLIENT_ID) `
|
|
||||||
-ClientSecret "$(ClientSecret)" `
|
|
||||||
-TenantId $(AZURE_TENANT)
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
schedules:
|
|
||||||
- cron: "0 0 * * *"
|
|
||||||
displayName: Daily
|
|
||||||
branches:
|
|
||||||
include:
|
|
||||||
- main
|
|
||||||
always: true
|
|
||||||
|
|
||||||
trigger: none
|
|
||||||
pr:
|
|
||||||
autoCancel: true
|
|
||||||
branches:
|
|
||||||
include:
|
|
||||||
- main
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
- template: image-generation.yml
|
|
||||||
parameters:
|
|
||||||
image_type: ubuntu2004
|
|
||||||
image_readme_name: Ubuntu2004-Readme.md
|
|
||||||
image_template_name: ubuntu-20.04.pkr.hcl
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
schedules:
|
|
||||||
- cron: "0 0 * * *"
|
|
||||||
displayName: Daily
|
|
||||||
branches:
|
|
||||||
include:
|
|
||||||
- main
|
|
||||||
always: true
|
|
||||||
|
|
||||||
trigger: none
|
|
||||||
pr:
|
|
||||||
autoCancel: true
|
|
||||||
branches:
|
|
||||||
include:
|
|
||||||
- main
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
- template: image-generation.yml
|
|
||||||
parameters:
|
|
||||||
image_type: ubuntu2204
|
|
||||||
image_readme_name: Ubuntu2204-Readme.md
|
|
||||||
image_template_name: ubuntu-22.04.pkr.hcl
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
schedules:
|
|
||||||
- cron: "0 0 * * *"
|
|
||||||
displayName: Daily
|
|
||||||
branches:
|
|
||||||
include:
|
|
||||||
- main
|
|
||||||
always: true
|
|
||||||
|
|
||||||
trigger: none
|
|
||||||
pr:
|
|
||||||
autoCancel: true
|
|
||||||
branches:
|
|
||||||
include:
|
|
||||||
- main
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
- template: image-generation.yml
|
|
||||||
parameters:
|
|
||||||
image_type: ubuntu2404
|
|
||||||
image_readme_name: Ubuntu2404-Readme.md
|
|
||||||
image_template_name: ubuntu-24.04.pkr.hcl
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
schedules:
|
|
||||||
- cron: "0 0 * * *"
|
|
||||||
displayName: Daily
|
|
||||||
branches:
|
|
||||||
include:
|
|
||||||
- main
|
|
||||||
always: true
|
|
||||||
|
|
||||||
trigger: none
|
|
||||||
pr:
|
|
||||||
autoCancel: true
|
|
||||||
branches:
|
|
||||||
include:
|
|
||||||
- main
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
- template: image-generation.yml
|
|
||||||
parameters:
|
|
||||||
image_type: windows2019
|
|
||||||
image_readme_name: Windows2019-Readme.md
|
|
||||||
image_template_name: windows-2019.pkr.hcl
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
schedules:
|
|
||||||
- cron: "0 0 * * *"
|
|
||||||
displayName: Daily
|
|
||||||
branches:
|
|
||||||
include:
|
|
||||||
- main
|
|
||||||
always: true
|
|
||||||
|
|
||||||
trigger: none
|
|
||||||
pr:
|
|
||||||
autoCancel: true
|
|
||||||
branches:
|
|
||||||
include:
|
|
||||||
- main
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
- template: image-generation.yml
|
|
||||||
parameters:
|
|
||||||
image_type: windows2022
|
|
||||||
image_readme_name: Windows2022-Readme.md
|
|
||||||
image_template_name: windows-2022.pkr.hcl
|
|
||||||
@@ -1,248 +0,0 @@
|
|||||||
function Push-AnkaTemplateToRegistry {
|
|
||||||
param (
|
|
||||||
[Parameter(Mandatory)]
|
|
||||||
[ValidateNotNullOrEmpty()]
|
|
||||||
[string] $RegistryUrl,
|
|
||||||
|
|
||||||
[Parameter(Mandatory)]
|
|
||||||
[ValidateNotNullOrEmpty()]
|
|
||||||
[string] $TagName,
|
|
||||||
|
|
||||||
[Parameter(Mandatory)]
|
|
||||||
[ValidateNotNullOrEmpty()]
|
|
||||||
[string] $TemplateName
|
|
||||||
)
|
|
||||||
|
|
||||||
# if registry uuid doesn't match then delete an image in registry
|
|
||||||
$AnkaCaCrtPath="$HOME/.config/anka/certs/anka-ca-crt.pem"
|
|
||||||
$images = anka --machine-readable registry --cacert $AnkaCaCrtPath --registry-path $RegistryUrl list | ConvertFrom-Json | ForEach-Object body
|
|
||||||
$images | Where-Object name -eq $TemplateName | ForEach-Object {
|
|
||||||
$id = $_.uuid
|
|
||||||
Show-StringWithFormat "Deleting '$TemplateName[$id]' VM and '$TagName' tag"
|
|
||||||
$curlCommand='curl -s -X DELETE -k "{0}/registry/vm?id={1}"' -f $RegistryUrl, $id
|
|
||||||
Invoke-AnkaCommand -Command $curlCommand
|
|
||||||
}
|
|
||||||
|
|
||||||
$command = "anka registry --cacert $AnkaCaCrtPath --registry-path $RegistryUrl push --force --tag $TagName $TemplateName"
|
|
||||||
Invoke-AnkaCommand -Command $command
|
|
||||||
}
|
|
||||||
|
|
||||||
function Get-AnkaVM {
|
|
||||||
param(
|
|
||||||
[string] $VMName
|
|
||||||
)
|
|
||||||
|
|
||||||
$command = "anka --machine-readable list"
|
|
||||||
if (-not [string]::IsNullOrEmpty($VMName)) {
|
|
||||||
$command = "anka --machine-readable show $VMName"
|
|
||||||
}
|
|
||||||
Invoke-AnkaCommand -Command $command | ConvertFrom-Json | Foreach-Object body
|
|
||||||
}
|
|
||||||
|
|
||||||
function Get-AnkaVMStatus {
|
|
||||||
param(
|
|
||||||
[Parameter(Mandatory)]
|
|
||||||
[ValidateNotNullOrEmpty()]
|
|
||||||
[string] $VMName
|
|
||||||
)
|
|
||||||
|
|
||||||
$command = "anka --machine-readable list $VMName"
|
|
||||||
Invoke-AnkaCommand -Command $command | ConvertFrom-Json | Foreach-Object { $_.body.status }
|
|
||||||
}
|
|
||||||
|
|
||||||
function Get-AnkaVMIPAddress {
|
|
||||||
param(
|
|
||||||
[Parameter(Mandatory)]
|
|
||||||
[ValidateNotNullOrEmpty()]
|
|
||||||
[string] $VMName
|
|
||||||
)
|
|
||||||
|
|
||||||
Get-AnkaVM -VMName $VMName | Foreach-Object ip
|
|
||||||
}
|
|
||||||
|
|
||||||
function Invoke-AnkaCommand {
|
|
||||||
param (
|
|
||||||
[Parameter(Mandatory)]
|
|
||||||
[ValidateNotNullOrEmpty()]
|
|
||||||
[string] $Command
|
|
||||||
)
|
|
||||||
|
|
||||||
$result = bash -c "$Command 2>&1"
|
|
||||||
if ($LASTEXITCODE -ne 0) {
|
|
||||||
Write-Error "There is an error during command execution:`n$result"
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
$result
|
|
||||||
}
|
|
||||||
|
|
||||||
function New-AnkaVMTemplate {
|
|
||||||
param (
|
|
||||||
[Parameter(Mandatory)]
|
|
||||||
[ValidateNotNullOrEmpty()]
|
|
||||||
[string] $InstallerPath,
|
|
||||||
|
|
||||||
[Parameter(Mandatory)]
|
|
||||||
[ValidateNotNullOrEmpty()]
|
|
||||||
[string] $TemplateName,
|
|
||||||
|
|
||||||
[Parameter(Mandatory)]
|
|
||||||
[ValidateNotNullOrEmpty()]
|
|
||||||
[string] $TemplateUsername,
|
|
||||||
|
|
||||||
[Parameter(Mandatory)]
|
|
||||||
[ValidateNotNullOrEmpty()]
|
|
||||||
[string] $TemplatePassword,
|
|
||||||
|
|
||||||
[Parameter(Mandatory)]
|
|
||||||
[int] $CPUCount,
|
|
||||||
|
|
||||||
[Parameter(Mandatory)]
|
|
||||||
[int] $RamSizeGb,
|
|
||||||
|
|
||||||
[Parameter(Mandatory)]
|
|
||||||
[int] $DiskSizeGb
|
|
||||||
)
|
|
||||||
|
|
||||||
$env:ANKA_DEFAULT_USER = $TemplateUsername
|
|
||||||
$env:ANKA_DEFAULT_PASSWD = $TemplatePassword
|
|
||||||
$env:ANKA_CREATE_SUSPEND = 0
|
|
||||||
$command = "anka create --cpu-count '$CPUCount' --ram-size '${RamSizeGb}G' --disk-size '${DiskSizeGb}G' --app '$InstallerPath' $TemplateName"
|
|
||||||
Invoke-AnkaCommand -Command $command
|
|
||||||
}
|
|
||||||
|
|
||||||
function Remove-AnkaVM {
|
|
||||||
param(
|
|
||||||
[Parameter(Mandatory)]
|
|
||||||
[ValidateNotNullOrEmpty()]
|
|
||||||
[string] $VMName
|
|
||||||
)
|
|
||||||
|
|
||||||
$command = "anka delete $VMName --yes"
|
|
||||||
$isTemplateExists = Get-AnkaVM | Where-Object name -eq $VMName
|
|
||||||
if ($isTemplateExists) {
|
|
||||||
$null = Invoke-AnkaCommand -Command $command
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function Set-AnkaVMVideoController {
|
|
||||||
param(
|
|
||||||
[Parameter(Mandatory)]
|
|
||||||
[ValidateNotNullOrEmpty()]
|
|
||||||
[string] $VMName,
|
|
||||||
|
|
||||||
[Parameter(Mandatory)]
|
|
||||||
[ValidateNotNullOrEmpty()]
|
|
||||||
[string] $ShortMacOSVersion,
|
|
||||||
|
|
||||||
[ValidateSet("fbuf", "pg")]
|
|
||||||
[string] $Controller = "pg"
|
|
||||||
)
|
|
||||||
|
|
||||||
$command = "anka modify $VMName set display -c $Controller"
|
|
||||||
$null = Invoke-AnkaCommand -Command $command
|
|
||||||
}
|
|
||||||
|
|
||||||
function Set-AnkaVMDisplayResolution {
|
|
||||||
param (
|
|
||||||
[Parameter(Mandatory)]
|
|
||||||
[ValidateNotNullOrEmpty()]
|
|
||||||
[string] $VMName,
|
|
||||||
|
|
||||||
[Parameter(Mandatory)]
|
|
||||||
[ValidateNotNullOrEmpty()]
|
|
||||||
[string] $DisplayResolution
|
|
||||||
)
|
|
||||||
|
|
||||||
$command = "anka modify $VMName set display -r $DisplayResolution"
|
|
||||||
$null = Invoke-AnkaCommand -Command $command
|
|
||||||
}
|
|
||||||
|
|
||||||
function Start-AnkaVM {
|
|
||||||
param(
|
|
||||||
[Parameter(Mandatory)]
|
|
||||||
[ValidateNotNullOrEmpty()]
|
|
||||||
[string] $VMName
|
|
||||||
)
|
|
||||||
|
|
||||||
$command = "anka start $VMName"
|
|
||||||
$vmStatus = Get-AnkaVMStatus -VMName $VMName
|
|
||||||
if ($vmStatus -eq "stopped") {
|
|
||||||
$null = Invoke-AnkaCommand -Command $command
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function Stop-AnkaVM {
|
|
||||||
param(
|
|
||||||
[Parameter(Mandatory)]
|
|
||||||
[ValidateNotNullOrEmpty()]
|
|
||||||
[string] $VMName
|
|
||||||
)
|
|
||||||
|
|
||||||
$command = "anka stop $VMName"
|
|
||||||
$vmStatus = Get-AnkaVMStatus -VMName $VMName
|
|
||||||
if ($vmStatus -eq "running") {
|
|
||||||
$null = Invoke-AnkaCommand -Command $command
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function Wait-AnkaVMIPAddress {
|
|
||||||
param(
|
|
||||||
[Parameter(Mandatory)]
|
|
||||||
[ValidateNotNullOrEmpty()]
|
|
||||||
[string] $VMName,
|
|
||||||
|
|
||||||
[int] $RetryCount = 20,
|
|
||||||
[int] $Seconds = 60
|
|
||||||
)
|
|
||||||
|
|
||||||
$condition = {
|
|
||||||
$vmStatus = Get-AnkaVMStatus -VMName $VMName
|
|
||||||
if ($vmStatus -eq "failed") {
|
|
||||||
Write-Host "`t [-] $VMName is in failed status"
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
Get-AnkaVMIPAddress -VMName $VMName
|
|
||||||
}
|
|
||||||
$null = Invoke-WithRetry -BreakCondition $condition -RetryCount $RetryCount -Seconds $Seconds
|
|
||||||
}
|
|
||||||
|
|
||||||
function Wait-AnkaVMSSHService {
|
|
||||||
param (
|
|
||||||
[Parameter(Mandatory)]
|
|
||||||
[ValidateNotNullOrEmpty()]
|
|
||||||
[string] $VMName,
|
|
||||||
|
|
||||||
[int] $RetryCount = 20,
|
|
||||||
[int] $Seconds = 60
|
|
||||||
)
|
|
||||||
|
|
||||||
Start-Sleep -Seconds $Seconds
|
|
||||||
Write-Host "`t[*] Waiting for '$VMName' VM to get an IP address"
|
|
||||||
Wait-AnkaVMIPAddress -VMName $VMName -RetryCount $RetryCount -Seconds $Seconds
|
|
||||||
|
|
||||||
$ipAddress = Get-AnkaVMIPAddress -VMName $VMName
|
|
||||||
Write-Host "`t[*] The '$ipAddress' IP address for '$VMName' VM"
|
|
||||||
|
|
||||||
Write-Host "`t[*] Checking if SSH on a port is open"
|
|
||||||
$isSSHPortOpen = Test-SSHPort -IPAddress $ipAddress
|
|
||||||
if (-not $isSSHPortOpen) {
|
|
||||||
Write-Host "`t[x] SSH port is closed"
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function Set-AnkaVMUuid {
|
|
||||||
param(
|
|
||||||
[Parameter(Mandatory)]
|
|
||||||
[ValidateNotNullOrEmpty()]
|
|
||||||
[string] $VMName,
|
|
||||||
|
|
||||||
[Parameter(Mandatory)]
|
|
||||||
[ValidateNotNullOrEmpty()]
|
|
||||||
[string] $Uuid
|
|
||||||
)
|
|
||||||
|
|
||||||
$command = "anka modify $VMName set custom-variable hw.uuid $Uuid"
|
|
||||||
Write-Host "`t[*] Setting $VMName uuid to $Uuid"
|
|
||||||
Invoke-AnkaCommand -Command $command
|
|
||||||
}
|
|
||||||
@@ -1,227 +0,0 @@
|
|||||||
[CmdletBinding()]
|
|
||||||
param(
|
|
||||||
[Parameter(Mandatory)]
|
|
||||||
[ValidateNotNullOrEmpty()]
|
|
||||||
[version] $MacOSVersion,
|
|
||||||
|
|
||||||
[ValidateNotNullOrEmpty()]
|
|
||||||
[string] $TemplateUsername,
|
|
||||||
|
|
||||||
[Parameter(Mandatory)]
|
|
||||||
[ValidateNotNullOrEmpty()]
|
|
||||||
[string] $TemplatePassword,
|
|
||||||
|
|
||||||
[Parameter(Mandatory)]
|
|
||||||
[ValidateNotNullOrEmpty()]
|
|
||||||
[string] $RegistryUrl,
|
|
||||||
|
|
||||||
[ValidateNotNullOrEmpty()]
|
|
||||||
[string] $TemplateName,
|
|
||||||
|
|
||||||
[bool] $DownloadLatestVersion = $true,
|
|
||||||
[bool] $PushToRegistry = $true,
|
|
||||||
[bool] $BetaSearch = $false,
|
|
||||||
[bool] $InstallSoftwareUpdate = $true,
|
|
||||||
[bool] $EnableAutoLogon = $true,
|
|
||||||
[int] $CPUCount = 6,
|
|
||||||
[int] $RamSizeGb = 7,
|
|
||||||
[int] $DiskSizeGb = 325,
|
|
||||||
[string] $DisplayResolution = "1920x1080",
|
|
||||||
[string] $TagName = [DateTimeOffset]::Now.ToUnixTimeSeconds(),
|
|
||||||
[string] $Uuid = "4203018E-580F-C1B5-9525-B745CECA79EB"
|
|
||||||
)
|
|
||||||
|
|
||||||
$ErrorActionPreference = "Stop"
|
|
||||||
$WarningPreference = "SilentlyContinue"
|
|
||||||
|
|
||||||
# Import helper modules
|
|
||||||
Import-Module "$PSScriptRoot/Anka.Helpers.psm1"
|
|
||||||
Import-Module "$PSScriptRoot/Service.Helpers.psm1"
|
|
||||||
|
|
||||||
# Helper functions
|
|
||||||
function Invoke-EnableAutoLogon {
|
|
||||||
if (-not $EnableAutoLogon) {
|
|
||||||
Write-Host "`t[*] Skip configuring AutoLogon"
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
$ipAddress = Get-AnkaVMIPAddress -VMName $TemplateName
|
|
||||||
|
|
||||||
Wait-AnkaVMSSHService -VMName $TemplateName -Seconds 30
|
|
||||||
|
|
||||||
Write-Host "`t[*] Enable AutoLogon"
|
|
||||||
Enable-AutoLogon -HostName $ipAddress -UserName $TemplateUsername -Password $TemplatePassword
|
|
||||||
|
|
||||||
Write-Host "`t[*] Reboot '$TemplateName' VM to enable AutoLogon"
|
|
||||||
Restart-VMSSH -HostName $ipAddress | Show-StringWithFormat
|
|
||||||
|
|
||||||
Wait-AnkaVMSSHService -VMName $TemplateName -Seconds 30
|
|
||||||
|
|
||||||
Write-Host "`t[*] Checking if AutoLogon is enabled"
|
|
||||||
Test-AutoLogon -VMName $TemplateName -UserName $TemplateUsername
|
|
||||||
}
|
|
||||||
|
|
||||||
function Invoke-SoftwareUpdate {
|
|
||||||
param (
|
|
||||||
[Parameter(Mandatory)]
|
|
||||||
[ValidateNotNullOrEmpty()]
|
|
||||||
[string] $Password
|
|
||||||
)
|
|
||||||
|
|
||||||
if (-not $InstallSoftwareUpdate) {
|
|
||||||
Write-Host "`t[*] Skip installing software updates"
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
$ipAddress = Get-AnkaVMIPAddress -VMName $TemplateName
|
|
||||||
|
|
||||||
# Unenroll Seed
|
|
||||||
Write-Host "`t[*] Resetting the seed before requesting stable versions"
|
|
||||||
Remove-CurrentBetaSeed -HostName $ipAddress | Show-StringWithFormat
|
|
||||||
|
|
||||||
# Install Software Updates
|
|
||||||
# Security updates may not be able to install(hang, freeze) when AutoLogon is turned off
|
|
||||||
Write-Host "`t[*] Finding available software"
|
|
||||||
$newUpdates = Get-SoftwareUpdate -HostName $ipAddress
|
|
||||||
|
|
||||||
if (-not $newUpdates) {
|
|
||||||
Write-Host "`t[*] No Updates Available"
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
# Define the next macOS version
|
|
||||||
$command = "sw_vers"
|
|
||||||
$guestMacosVersion = Invoke-SSHPassCommand -HostName $ipAddress -Command $command
|
|
||||||
switch -regex ($guestMacosVersion[1]) {
|
|
||||||
'12.\d' { $nextOSVersion = 'macOS Ventura|macOS Sonoma|macOS Sequoia' }
|
|
||||||
'13.\d' { $nextOSVersion = 'macOS Sonoma|macOS Sequoia' }
|
|
||||||
'14.\d' { $nextOSVersion = 'macOS Sequoia' }
|
|
||||||
}
|
|
||||||
|
|
||||||
Write-Host "`t[*] Fetching Software Updates ready to install on '$TemplateName' VM:"
|
|
||||||
Show-StringWithFormat $newUpdates
|
|
||||||
$listOfNewUpdates = $($($newUpdates.Split("*")).Split("Title").where({$_ -match "Label:"}).Replace("Label: ", '').where({$_ -notmatch $nextOSVersion}))
|
|
||||||
Write-Host "`t[*] Installing Software Updates on '$TemplateName' VM:"
|
|
||||||
Install-SoftwareUpdate -HostName $ipAddress -listOfUpdates $listOfNewUpdates -Password $Password | Show-StringWithFormat
|
|
||||||
Write-Host "`t[*] Sleep 60 seconds before the software updates have been installed"
|
|
||||||
Start-Sleep -Seconds 60
|
|
||||||
Write-Host "`t[*] Waiting for loginwindow process"
|
|
||||||
Wait-LoginWindow -HostName $ipAddress | Show-StringWithFormat
|
|
||||||
# Re-enable AutoLogon after installing a new security software update
|
|
||||||
Invoke-EnableAutoLogon
|
|
||||||
|
|
||||||
foreach ($newupdate in $listOfNewUpdates) {
|
|
||||||
# Check software updates have been installed
|
|
||||||
$updates = Get-SoftwareUpdate -HostName $ipAddress
|
|
||||||
if ($updates.Contains("Action: restart") -and !($updates -match $nextOSVersion)) {
|
|
||||||
Write-Host "`t[x] Software updates failed to install: "
|
|
||||||
Show-StringWithFormat $updates
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Write-Host "`t[*] Show the install history:"
|
|
||||||
$hUpdates = Get-SoftwareUpdateHistory -HostName $ipAddress
|
|
||||||
Show-StringWithFormat $hUpdates
|
|
||||||
|
|
||||||
Write-Host "`t[*] The current macOS version:"
|
|
||||||
$command = "sw_vers"
|
|
||||||
Invoke-SSHPassCommand -HostName $ipAddress -Command $command | Show-StringWithFormat
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function Invoke-UpdateSettings {
|
|
||||||
param (
|
|
||||||
[Parameter(Mandatory)]
|
|
||||||
[ValidateNotNullOrEmpty()]
|
|
||||||
[string] $Password
|
|
||||||
)
|
|
||||||
$isConfRequired = $InstallSoftwareUpdate -or $EnableAutoLogon
|
|
||||||
if (-not $isConfRequired) {
|
|
||||||
Write-Host "`t[*] Skip additional configuration"
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
Write-Host "`t[*] Starting '$TemplateName' VM"
|
|
||||||
Start-AnkaVM -VMName $TemplateName
|
|
||||||
|
|
||||||
Write-Host "`t[*] Waiting for SSH service on '$TemplateName' VM"
|
|
||||||
Wait-AnkaVMSSHService -VMName $TemplateName -Seconds 30
|
|
||||||
|
|
||||||
# Configure AutoLogon
|
|
||||||
Invoke-EnableAutoLogon
|
|
||||||
|
|
||||||
# Install software updates
|
|
||||||
Invoke-SoftwareUpdate -Password $Password
|
|
||||||
|
|
||||||
Write-Host "`t[*] Stopping '$TemplateName' VM"
|
|
||||||
Stop-AnkaVM -VMName $TemplateName
|
|
||||||
}
|
|
||||||
|
|
||||||
function Test-VMStopped {
|
|
||||||
$vmStatus = Get-AnkaVMStatus -VMName $TemplateName
|
|
||||||
if ($vmStatus -ne "stopped") {
|
|
||||||
Write-Host "`t[x] VM '$TemplateName' state is not stopped. The current state is '$vmStatus'"
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# Password is passed as env-var "SSHPASS"
|
|
||||||
$env:SSHUSER = $TemplateUsername
|
|
||||||
$env:SSHPASS = $TemplatePassword
|
|
||||||
|
|
||||||
Write-Host "`n[#1] Download macOS application installer:"
|
|
||||||
$shortMacOSVersion = Get-ShortMacOSVersion -MacOSVersion $MacOSVersion
|
|
||||||
if ([string]::IsNullOrEmpty($TemplateName)) {
|
|
||||||
$osArch = $(arch)
|
|
||||||
if ($osArch -eq "arm64") {
|
|
||||||
$macOSInstaller = Get-MacOSIPSWInstaller -MacOSVersion $MacOSVersion -DownloadLatestVersion $DownloadLatestVersion -BetaSearch $BetaSearch
|
|
||||||
$TemplateName = "clean_macos_${shortMacOSVersion}_${osArch}"
|
|
||||||
} else {
|
|
||||||
$macOSInstaller = Get-MacOSInstaller -MacOSVersion $MacOSVersion -DownloadLatestVersion $DownloadLatestVersion -BetaSearch $BetaSearch
|
|
||||||
$TemplateName = "clean_macos_${shortMacOSVersion}"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Write-Host "`n[#2] Create a VM template:"
|
|
||||||
Write-Host "`t[*] Deleting existed template with name '$TemplateName' before creating a new one"
|
|
||||||
Remove-AnkaVM -VMName $TemplateName
|
|
||||||
|
|
||||||
# Temporary disable VNC for macOS 14
|
|
||||||
# It's probably Anka's bug fixed in 3.3.2
|
|
||||||
if ($shortMacOSVersion -eq "14") {
|
|
||||||
$env:ANKA_CREATE_VNC = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
Write-Host "`t[*] Creating Anka VM template with name '$TemplateName' and '$TemplateUsername' user"
|
|
||||||
Write-Host "`t[*] CPU Count: $CPUCount, RamSize: ${RamSizeGb}G, DiskSizeGb: ${DiskSizeGb}G, InstallerPath: $macOSInstaller, TemplateName: $TemplateName"
|
|
||||||
New-AnkaVMTemplate -InstallerPath "$macOSInstaller" `
|
|
||||||
-TemplateName $TemplateName `
|
|
||||||
-TemplateUsername $TemplateUsername `
|
|
||||||
-TemplatePassword $TemplatePassword `
|
|
||||||
-CPUCount $CPUCount `
|
|
||||||
-RamSizeGb $RamSizeGb `
|
|
||||||
-DiskSizeGb $DiskSizeGb | Show-StringWithFormat
|
|
||||||
|
|
||||||
Write-Host "`n[#3] Configure AutoLogon and/or install software updates:"
|
|
||||||
Invoke-UpdateSettings -Password $TemplatePassword
|
|
||||||
|
|
||||||
Write-Host "`n[#4] Finalization '$TemplateName' configuration and push to the registry:"
|
|
||||||
Write-Host "`t[*] The '$TemplateName' VM status is stopped"
|
|
||||||
Test-VMStopped
|
|
||||||
|
|
||||||
# Configure graphics settings
|
|
||||||
Write-Host "`t[*] Enabling Graphics Acceleration with Apple Metal for '$TemplateName' VM"
|
|
||||||
Set-AnkaVMVideoController -VMName $TemplateName -ShortMacOSVersion $ShortMacOSVersion
|
|
||||||
|
|
||||||
Write-Host "`t[*] Setting screen resolution to $DisplayResolution for $TemplateName"
|
|
||||||
Set-AnkaVMDisplayResolution -VMName $TemplateName -DisplayResolution $DisplayResolution
|
|
||||||
|
|
||||||
# Set static UUID
|
|
||||||
Set-AnkaVMUuid -VMName $TemplateName -Uuid $Uuid
|
|
||||||
|
|
||||||
if ($PushToRegistry) {
|
|
||||||
# Push a VM template (and tag) to the Cloud
|
|
||||||
Write-Host "`t[*] Pushing '$TemplateName' image with '$TagName' tag to the '$RegistryUrl' registry..."
|
|
||||||
Push-AnkaTemplateToRegistry -RegistryUrl $registryUrl -TagName $TagName -TemplateName $TemplateName
|
|
||||||
}
|
|
||||||
@@ -1,476 +0,0 @@
|
|||||||
function Enable-AutoLogon {
|
|
||||||
param (
|
|
||||||
[Parameter(Mandatory)]
|
|
||||||
[ValidateNotNullOrEmpty()]
|
|
||||||
[string] $HostName,
|
|
||||||
|
|
||||||
[Parameter(Mandatory)]
|
|
||||||
[ValidateNotNullOrEmpty()]
|
|
||||||
[string] $UserName,
|
|
||||||
|
|
||||||
[Parameter(Mandatory)]
|
|
||||||
[ValidateNotNullOrEmpty()]
|
|
||||||
[string] $Password
|
|
||||||
)
|
|
||||||
|
|
||||||
$url = "https://raw.githubusercontent.com/actions/runner-images/main/images/macos/assets/bootstrap-provisioner/setAutoLogin.sh"
|
|
||||||
$script = Invoke-RestMethod -Uri $url
|
|
||||||
$base64 = [Convert]::ToBase64String($script.ToCharArray())
|
|
||||||
$command = "echo $base64 | base64 --decode > ./setAutoLogin.sh;sudo bash ./setAutoLogin.sh '${UserName}' '${Password}';rm ./setAutoLogin.sh"
|
|
||||||
Invoke-SSHPassCommand -HostName $HostName -Command $command
|
|
||||||
}
|
|
||||||
|
|
||||||
function Invoke-SoftwareUpdateArm64 {
|
|
||||||
param (
|
|
||||||
[Parameter(Mandatory)]
|
|
||||||
[ValidateNotNullOrEmpty()]
|
|
||||||
[string] $HostName,
|
|
||||||
|
|
||||||
[Parameter(Mandatory)]
|
|
||||||
[ValidateNotNullOrEmpty()]
|
|
||||||
[string] $Password,
|
|
||||||
|
|
||||||
[Parameter(Mandatory)]
|
|
||||||
[ValidateNotNullOrEmpty()]
|
|
||||||
[array] $ListOfUpdates
|
|
||||||
)
|
|
||||||
|
|
||||||
# Define the next macOS version
|
|
||||||
$command = "sw_vers"
|
|
||||||
$guestMacosVersion = Invoke-SSHPassCommand -HostName $HostName -Command $command
|
|
||||||
switch -regex ($guestMacosVersion[1]) {
|
|
||||||
'12.\d' { $nextOSVersion = 'macOS Ventura|macOS Sonoma|macOS Sequoia' }
|
|
||||||
'13.\d' { $nextOSVersion = 'macOS Sonoma|macOS Sequoia' }
|
|
||||||
'14.\d' { $nextOSVersion = 'macOS Sequoia' }
|
|
||||||
}
|
|
||||||
|
|
||||||
$url = "https://raw.githubusercontent.com/actions/runner-images/main/images/macos/assets/auto-software-update-arm64.exp"
|
|
||||||
$script = Invoke-RestMethod -Uri $url
|
|
||||||
foreach ($update in $ListOfUpdates) {
|
|
||||||
if ($update -notmatch $nextOSVersion) {
|
|
||||||
$updatedScript = $script.Replace("MACOSUPDATE", $($($update.trim()).Replace(" ","\ ")))
|
|
||||||
$base64 = [Convert]::ToBase64String($updatedScript.ToCharArray())
|
|
||||||
$command = "echo $base64 | base64 --decode > ./auto-software-update-arm64.exp;chmod +x ./auto-software-update-arm64.exp; ./auto-software-update-arm64.exp ${Password};rm ./auto-software-update-arm64.exp"
|
|
||||||
Invoke-SSHPassCommand -HostName $HostName -Command $command
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function Get-AvailableVersions {
|
|
||||||
param (
|
|
||||||
[bool] $IsBeta = $false
|
|
||||||
)
|
|
||||||
|
|
||||||
if ($IsBeta) {
|
|
||||||
$searchPostfix = " beta"
|
|
||||||
}
|
|
||||||
|
|
||||||
$command = { /usr/sbin/softwareupdate --list-full-installers | grep "macOS" }
|
|
||||||
$condition = { $LASTEXITCODE -eq 0 }
|
|
||||||
$softwareUpdates = Invoke-WithRetry -Command $command -BreakCondition $condition | Where-Object { $_.Contains("Title: macOS") -and $_ -match $searchPostfix }
|
|
||||||
$allVersions = $softwareUpdates -replace "(\* )?(Title|Version|Size):" | ConvertFrom-Csv -Header OSName, OSVersion | Select-Object OSName, OSVersion -Unique
|
|
||||||
|
|
||||||
$allVersions
|
|
||||||
}
|
|
||||||
|
|
||||||
function Get-AvailableIPSWVersions {
|
|
||||||
param (
|
|
||||||
[bool] $IsBeta = $false,
|
|
||||||
[bool] $IsLatest = $true,
|
|
||||||
[string] $MacOSCodeNameOrVersion
|
|
||||||
)
|
|
||||||
|
|
||||||
if ($IsBeta) {
|
|
||||||
$command = { mist list firmware "$MacOSCodeNameOrVersion" --compatible --include-betas --latest --export "/Applications/export.json" }
|
|
||||||
} elseif ($IsLatest) {
|
|
||||||
$command = { mist list firmware "$MacOSCodeNameOrVersion" --compatible --latest --export "/Applications/export.json" }
|
|
||||||
} else {
|
|
||||||
$command = { mist list firmware "$MacOSCodeNameOrVersion" --compatible --export "/Applications/export.json" }
|
|
||||||
}
|
|
||||||
|
|
||||||
$condition = { $LASTEXITCODE -eq 0 }
|
|
||||||
Invoke-WithRetry -Command $command -BreakCondition $condition | Out-Null
|
|
||||||
$softwareList = get-content -Path "/Applications/export.json"
|
|
||||||
$availableBuilds = ($softwareList | ConvertFrom-Json).build
|
|
||||||
if ($null -eq $availableBuilds) {
|
|
||||||
Write-Host "Requested macOS '$MacOSCodeNameOrVersion' version not found in the list of available installers."
|
|
||||||
$command = { mist list firmware "$($MacOSCodeNameOrVersion.split('.')[0])" }
|
|
||||||
Invoke-WithRetry -Command $command -BreakCondition $condition
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
return $availableBuilds
|
|
||||||
}
|
|
||||||
|
|
||||||
function Get-MacOSIPSWInstaller {
|
|
||||||
param (
|
|
||||||
[Parameter(Mandatory)]
|
|
||||||
[version] $MacOSVersion,
|
|
||||||
|
|
||||||
[bool] $DownloadLatestVersion = $false,
|
|
||||||
[bool] $BetaSearch = $false
|
|
||||||
)
|
|
||||||
|
|
||||||
if ($MacOSVersion -eq [version] "13.0") {
|
|
||||||
$MacOSName = "macOS Ventura"
|
|
||||||
} elseif ($MacOSVersion -eq [version] "14.0") {
|
|
||||||
$MacOSName = "macOS Sonoma"
|
|
||||||
} else {
|
|
||||||
$MacOSName = $MacOSVersion.ToString()
|
|
||||||
}
|
|
||||||
|
|
||||||
Write-Host "`t[*] Finding available full installers"
|
|
||||||
if ($DownloadLatestVersion -eq $true) {
|
|
||||||
$targetBuild = Get-AvailableIPSWVersions -IsLatest $true -MacOSCodeNameOrVersion $MacOSName
|
|
||||||
Write-Host "`t[*] The 'DownloadLatestVersion' flag is set to true. Latest compatible macOS build of '$MacOSName' is '$targetBuild'"
|
|
||||||
} elseif ($BetaSearch -eq $true) {
|
|
||||||
$targetBuild = Get-AvailableIPSWVersions -IsBeta $true -MacOSCodeNameOrVersion $MacOSName
|
|
||||||
Write-Host "`t[*] The 'BetaSearch' flag is set to true. Latest compatible beta macOS build of '$MacOSName' is '$targetBuild'"
|
|
||||||
} else {
|
|
||||||
$targetBuild = Get-AvailableIPSWVersions -MacOSCodeNameOrVersion $MacOSName -IsLatest $false
|
|
||||||
Write-Host "`t[*] Available compatible macOS builds of '$MacOSName' are: $($targetBuild -join ', ')"
|
|
||||||
if ($targetBuild.Count -gt 1) {
|
|
||||||
Write-Error "`t[*] Please specify the exact build number of macOS you want to install"
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$installerPathPattern = "/Applications/Install ${macOSName}*.ipsw"
|
|
||||||
if (Test-Path $installerPathPattern) {
|
|
||||||
$previousInstallerPath = Get-Item -Path $installerPathPattern
|
|
||||||
Write-Host "`t[*] Removing '$previousInstallerPath' installation app before downloading the new one"
|
|
||||||
sudo rm -rf "$previousInstallerPath"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Download macOS installer
|
|
||||||
$installerDir = "/Applications/"
|
|
||||||
$installerName = "Install ${macOSName}.ipsw"
|
|
||||||
Write-Host "`t[*] Requested macOS '$targetBuild' version installer found, fetching it from mist database"
|
|
||||||
Invoke-WithRetry { mist download firmware "$targetBuild" --output-directory $installerDir --firmware-name "$installerName" } { $LASTEXITCODE -eq 0 } | Out-Null
|
|
||||||
if (Test-Path "$installerDir$installerName") {
|
|
||||||
$result = "$installerDir$installerName"
|
|
||||||
} else {
|
|
||||||
Write-Error "`t[*] Requested macOS '$targetBuild' version installer failed to download"
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
return $result
|
|
||||||
}
|
|
||||||
|
|
||||||
function Get-MacOSInstaller {
|
|
||||||
param (
|
|
||||||
[Parameter(Mandatory)]
|
|
||||||
[version] $MacOSVersion,
|
|
||||||
|
|
||||||
[bool] $DownloadLatestVersion = $false,
|
|
||||||
[bool] $BetaSearch = $false
|
|
||||||
)
|
|
||||||
|
|
||||||
# Enroll machine to DeveloperSeed if we need beta and unenroll otherwise
|
|
||||||
$seedutil = "/System/Library/PrivateFrameworks/Seeding.framework/Versions/Current/Resources/seedutil"
|
|
||||||
if ($BetaSearch) {
|
|
||||||
Write-Host "`t[*] Beta Version requested. Enrolling machine to DeveloperSeed"
|
|
||||||
sudo $seedutil enroll DeveloperSeed | Out-Null
|
|
||||||
} else {
|
|
||||||
Write-Host "`t[*] Resetting the seed before requesting stable versions"
|
|
||||||
sudo $seedutil unenroll | Out-Null
|
|
||||||
}
|
|
||||||
|
|
||||||
# Validate there is no software update at the moment
|
|
||||||
Test-SoftwareUpdate
|
|
||||||
|
|
||||||
# Validate availability OSVersion
|
|
||||||
Write-Host "`t[*] Finding available full installers"
|
|
||||||
$availableVersions = Get-AvailableVersions -IsBeta $BetaSearch
|
|
||||||
if ($DownloadLatestVersion) {
|
|
||||||
$shortMacOSVersion = Get-ShortMacOSVersion -MacOSVersion $MacOSVersion
|
|
||||||
$filterSearch = "${shortMacOSVersion}."
|
|
||||||
$filteredVersions = $availableVersions.Where{ $_.OSVersion.StartsWith($filterSearch) }
|
|
||||||
if (-not $filteredVersions) {
|
|
||||||
Write-Host "`t[x] Failed to find any macOS versions using '$filterSearch' search condition"
|
|
||||||
Show-StringWithFormat $availableVersions
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
Show-StringWithFormat $filteredVersions
|
|
||||||
$osVersions = $filteredVersions.OSVersion | Sort-Object { [version]$_ }
|
|
||||||
$MacOSVersion = $osVersions | Select-Object -Last 1
|
|
||||||
Write-Host "`t[*] The 'DownloadLatestVersion' flag is set. Latest macOS version is '$MacOSVersion' now"
|
|
||||||
}
|
|
||||||
|
|
||||||
$macOSName = $availableVersions.Where{ $MacOSVersion -eq $_.OSVersion }.OSName.Split(" ")[1]
|
|
||||||
if (-not $macOSName) {
|
|
||||||
Write-Host "`t[x] Requested macOS '$MacOSVersion' version not found in the list of available installers. Available versions are:`n$($availableVersions.OSVersion)"
|
|
||||||
Write-Host "`t[x] Make sure to pass '-BetaSearch `$true' if you need a beta version installer"
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
# Clear LastRecommendedMajorOSBundleIdentifier to prevent error during fetching updates
|
|
||||||
# Install failed with error: Update not found
|
|
||||||
Update-SoftwareBundle
|
|
||||||
|
|
||||||
# Download macOS installer
|
|
||||||
Write-Host "`t[*] Requested macOS '$MacOSVersion' version installer found, fetching it from Apple Software Update"
|
|
||||||
Invoke-WithRetry -Command { sudo /usr/local/bin/mist download installer $MacOSVersion application --force --export installer.json --output-directory /Applications } -BreakCondition { $LASTEXITCODE -eq 0 } | Out-Null
|
|
||||||
if (-not(Test-Path installer.json -PathType leaf)) {
|
|
||||||
Write-Host "`t[x] Failed to fetch $MacOSVersion macOS"
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
$installerPath = (Get-Content installer.json | Out-String | ConvertFrom-Json).options.applicationPath
|
|
||||||
if (-not $installerPath) {
|
|
||||||
Write-Host "`t[x] Path not found using '$installerPathPattern'"
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
Write-Host "`t[*] Installer successfully downloaded to '$installerPath'"
|
|
||||||
|
|
||||||
$installerPath
|
|
||||||
}
|
|
||||||
|
|
||||||
function Get-ShortMacOSVersion {
|
|
||||||
param (
|
|
||||||
[Parameter(Mandatory)]
|
|
||||||
[version] $MacOSVersion
|
|
||||||
)
|
|
||||||
|
|
||||||
# Take Major.Minor version for macOS 10 (10.14 or 10.15) and Major for all further versions
|
|
||||||
$MacOSVersion.Major -eq 10 ? $MacOSVersion.ToString(2) : $MacOSVersion.ToString(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
function Get-SoftwareUpdate {
|
|
||||||
param(
|
|
||||||
[Parameter(Mandatory)]
|
|
||||||
[ValidateNotNullOrEmpty()]
|
|
||||||
[string] $HostName
|
|
||||||
)
|
|
||||||
|
|
||||||
$command = "/usr/sbin/softwareupdate --list"
|
|
||||||
$result = Invoke-SSHPassCommand -HostName $HostName -Command $command
|
|
||||||
$result | Where-Object { $_ -match "(Label|Title):" } | Out-String
|
|
||||||
}
|
|
||||||
|
|
||||||
function Get-SoftwareUpdateHistory {
|
|
||||||
param (
|
|
||||||
[Parameter(Mandatory)]
|
|
||||||
[ValidateNotNullOrEmpty()]
|
|
||||||
[string] $HostName
|
|
||||||
)
|
|
||||||
|
|
||||||
$command = "/usr/sbin/softwareupdate --history"
|
|
||||||
Invoke-SSHPassCommand -HostName $HostName -Command $command | Out-String
|
|
||||||
}
|
|
||||||
|
|
||||||
function Install-SoftwareUpdate {
|
|
||||||
param(
|
|
||||||
[Parameter(Mandatory)]
|
|
||||||
[ValidateNotNullOrEmpty()]
|
|
||||||
[string] $HostName,
|
|
||||||
[array] $listOfUpdates,
|
|
||||||
[string] $Password
|
|
||||||
)
|
|
||||||
# If an update is happening on macOS arm64 we will use the additional tool to install updates.
|
|
||||||
$osArch = $(arch)
|
|
||||||
if ($osArch -eq "arm64") {
|
|
||||||
Invoke-SoftwareUpdateArm64 -HostName $HostName -Password $Password -ListOfUpdates $listOfUpdates
|
|
||||||
} else {
|
|
||||||
foreach ($update in $listOfUpdates) {
|
|
||||||
$command = "sudo /usr/sbin/softwareupdate --restart --verbose --install '$($update.trim())'"
|
|
||||||
Invoke-SSHPassCommand -HostName $HostName -Command $command
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function Invoke-SSHPassCommand {
|
|
||||||
param(
|
|
||||||
[Parameter(Mandatory)]
|
|
||||||
[ValidateNotNullOrEmpty()]
|
|
||||||
[string] $HostName,
|
|
||||||
|
|
||||||
[Parameter(Mandatory)]
|
|
||||||
[ValidateNotNullOrEmpty()]
|
|
||||||
[string] $Command,
|
|
||||||
|
|
||||||
[int] $ConnectTimeout = 10,
|
|
||||||
[int] $ConnectionAttempts = 10,
|
|
||||||
[int] $ServerAliveInterval = 30
|
|
||||||
)
|
|
||||||
|
|
||||||
$sshArg = @(
|
|
||||||
"sshpass"
|
|
||||||
"-e"
|
|
||||||
"ssh"
|
|
||||||
"-o UserKnownHostsFile=/dev/null"
|
|
||||||
"-o StrictHostKeyChecking=no"
|
|
||||||
"-o ConnectTimeout=$ConnectTimeout"
|
|
||||||
"-o ConnectionAttempts=$ConnectionAttempts"
|
|
||||||
"-o LogLevel=ERROR"
|
|
||||||
"-o ServerAliveInterval=$ServerAliveInterval"
|
|
||||||
"${env:SSHUSER}@${HostName}"
|
|
||||||
)
|
|
||||||
$sshPassOptions = $sshArg -join " "
|
|
||||||
if ($PSVersionTable.PSVersion.Major -eq 7 -and $PSVersionTable.PSVersion.Minor -le 2) {
|
|
||||||
$result = bash -c "$sshPassOptions \""$Command\"" 2>&1"
|
|
||||||
} else {
|
|
||||||
$result = bash -c "$sshPassOptions `"$Command`" 2>&1"
|
|
||||||
}
|
|
||||||
if ($LASTEXITCODE -ne 0) {
|
|
||||||
Write-Error "There is an error during command execution:`n$result"
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
$result
|
|
||||||
}
|
|
||||||
|
|
||||||
function Invoke-WithRetry {
|
|
||||||
param(
|
|
||||||
[scriptblock] $Command,
|
|
||||||
[scriptblock] $BreakCondition,
|
|
||||||
[int] $RetryCount = 20,
|
|
||||||
[int] $Seconds = 60
|
|
||||||
)
|
|
||||||
while ($RetryCount -gt 0) {
|
|
||||||
try {
|
|
||||||
if ($Command) {
|
|
||||||
$result = & $Command
|
|
||||||
}
|
|
||||||
|
|
||||||
if (& $BreakCondition) {
|
|
||||||
return $result
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
Write-Host "`t [!] Error during command execution: $_"
|
|
||||||
}
|
|
||||||
|
|
||||||
$RetryCount--
|
|
||||||
if ($RetryCount -eq 0) {
|
|
||||||
Write-Error "No more attempts left: $BreakCondition"
|
|
||||||
}
|
|
||||||
Write-Host "`t [/] Waiting $Seconds seconds before retrying. Retries left: $RetryCount"
|
|
||||||
Start-Sleep -Seconds $Seconds
|
|
||||||
}
|
|
||||||
|
|
||||||
$result
|
|
||||||
}
|
|
||||||
|
|
||||||
function Restart-VMSSH {
|
|
||||||
param(
|
|
||||||
[Parameter(Mandatory)]
|
|
||||||
[ValidateNotNullOrEmpty()]
|
|
||||||
[string] $HostName
|
|
||||||
)
|
|
||||||
|
|
||||||
#
|
|
||||||
# https://unix.stackexchange.com/questions/58271/closing-connection-after-executing-reboot-using-ssh-command
|
|
||||||
#
|
|
||||||
$command = '(sleep 1 && sudo reboot &) && exit'
|
|
||||||
Invoke-SSHPassCommand -HostName $HostName -Command $command
|
|
||||||
}
|
|
||||||
|
|
||||||
function Show-StringWithFormat {
|
|
||||||
param(
|
|
||||||
[Parameter(ValuefromPipeline)]
|
|
||||||
[object] $string
|
|
||||||
)
|
|
||||||
|
|
||||||
process {
|
|
||||||
($string | Out-String).Trim().split("`n") | ForEach-Object { Write-Host "`t $_" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function Remove-CurrentBetaSeed {
|
|
||||||
param(
|
|
||||||
[Parameter(Mandatory)]
|
|
||||||
[ValidateNotNullOrEmpty()]
|
|
||||||
[string] $HostName
|
|
||||||
)
|
|
||||||
|
|
||||||
$command = "sudo /System/Library/PrivateFrameworks/Seeding.framework/Versions/Current/Resources/seedutil unenroll"
|
|
||||||
Invoke-SSHPassCommand -HostName $HostName -Command $command | Out-String
|
|
||||||
}
|
|
||||||
|
|
||||||
function Test-AutoLogon {
|
|
||||||
param(
|
|
||||||
[Parameter(Mandatory)]
|
|
||||||
[ValidateNotNullOrEmpty()]
|
|
||||||
[string] $VMName,
|
|
||||||
|
|
||||||
[Parameter(Mandatory)]
|
|
||||||
[ValidateNotNullOrEmpty()]
|
|
||||||
[string] $UserName
|
|
||||||
)
|
|
||||||
|
|
||||||
Invoke-WithRetry -BreakCondition {
|
|
||||||
# pwsh crashes if it invokes directly
|
|
||||||
# https://github.com/dotnet/runtime/issues/59059
|
|
||||||
$ankaUser = "" | bash -c "anka run $VMName /usr/bin/id -nu"
|
|
||||||
$UserName -eq $ankaUser
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function Test-SoftwareUpdate {
|
|
||||||
param (
|
|
||||||
[string] $UpdateProcessName = "softwareupdate"
|
|
||||||
)
|
|
||||||
|
|
||||||
$command = {
|
|
||||||
$updateProcess = (Get-Process -Name $UpdateProcessName -ErrorAction SilentlyContinue).id
|
|
||||||
if ($updateProcess) {
|
|
||||||
# Workaround to get commandline param as it doesn't work for macOS atm https://github.com/PowerShell/PowerShell/issues/13943
|
|
||||||
$processName = /bin/ps -o command= $updateProcess
|
|
||||||
Write-Host "`t[*] Another software update process with '$updateProcess' id is in place with the following arguments '$processName'"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$condition = {
|
|
||||||
$null -eq (Get-Process -Name $UpdateProcessName -ErrorAction SilentlyContinue)
|
|
||||||
}
|
|
||||||
|
|
||||||
Invoke-WithRetry -Command $command -BreakCondition $condition
|
|
||||||
}
|
|
||||||
|
|
||||||
function Test-SSHPort {
|
|
||||||
param(
|
|
||||||
[Parameter(Mandatory)]
|
|
||||||
[ipaddress] $IPAddress,
|
|
||||||
|
|
||||||
[int] $Port = 22,
|
|
||||||
[int] $Timeout = 2000
|
|
||||||
)
|
|
||||||
|
|
||||||
Invoke-WithRetry -Command {$true} -BreakCondition {
|
|
||||||
try {
|
|
||||||
$client = [System.Net.Sockets.TcpClient]::new()
|
|
||||||
$client.ConnectAsync($IPAddress, $Port).Wait($Timeout)
|
|
||||||
}
|
|
||||||
catch {
|
|
||||||
$false
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
$client.Close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function Wait-LoginWindow {
|
|
||||||
param (
|
|
||||||
[Parameter(Mandatory)]
|
|
||||||
[ValidateNotNullOrEmpty()]
|
|
||||||
[string] $HostName,
|
|
||||||
|
|
||||||
[int] $RetryCount = 60,
|
|
||||||
[int] $Seconds = 60
|
|
||||||
)
|
|
||||||
|
|
||||||
$condition = {
|
|
||||||
$psCommand = "/bin/ps auxww"
|
|
||||||
$lw = "/System/Library/CoreServices/loginwindow.app/Contents/MacOS/loginwindow"
|
|
||||||
$ctk = "/System/Library/Frameworks/CryptoTokenKit.framework/ctkahp.bundle/Contents/MacOS/ctkahp"
|
|
||||||
$proc = Invoke-SSHPassCommand -HostName $HostName -Command $psCommand | Out-String
|
|
||||||
$proc.Contains($lw) -and $proc.Contains($ctk)
|
|
||||||
}
|
|
||||||
Invoke-WithRetry -RetryCount $RetryCount -Seconds $Seconds -BreakCondition $condition
|
|
||||||
}
|
|
||||||
|
|
||||||
function Update-SoftwareBundle {
|
|
||||||
$productVersion = sw_vers -productVersion
|
|
||||||
|
|
||||||
if ( $productVersion.StartsWith('11.') ) {
|
|
||||||
sudo rm -rf /Library/Preferences/com.apple.commerce.plist
|
|
||||||
sudo /usr/bin/defaults delete /Library/Preferences/com.apple.SoftwareUpdate.plist LastRecommendedMajorOSBundleIdentifier | Out-Null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user