Files
NetSPI-MicroBurst/Az/Get-AzMachineLearningData.ps1
2025-03-01 08:58:23 +00:00

291 lines
14 KiB
PowerShell

<#
File: Get-AzMachineLearningData.ps1
Author: Christian Bortone (@xybytes) - 2025
Description: PowerShell functions for dumping Azure Machine Learning Workspace information.
#>
function Get-AzMachineLearningData {
<#
.SYNOPSIS
PowerShell function for dumping information from Azure Machine Learning Workspaces.
.DESCRIPTION
The function will dump available information for an Azure ML Workspace. This includes compute instances, resources, models, keys, jobs, endpoints, etc.
.PARAMETER ResourceGroupName
Resource group name to use.
.PARAMETER Subscription
Subscription to use.
.PARAMETER folder
The folder to output to.
.EXAMPLE
PS C:\> Get-AzMachineLearningData -folder MLOutput -Verbose
VERBOSE: Logged In as christian@xybytes.com
VERBOSE: Dumping Workspaces from the "main-subscription" Subscription
VERBOSE: 1 Workspace(s) Enumerated
VERBOSE: Attempting to dump data from the space03 workspace
VERBOSE: Attempting to dump compute data
VERBOSE: 3 Compute Resource(s) Enumerated
VERBOSE: Attempting to dump endpoint data
VERBOSE: 1 Endpoint(s) Enumerated
VERBOSE: Attempting to dump jobs data
VERBOSE: 2 Compute Job(s) Enumerated
VERBOSE: Attempting to dump Models
VERBOSE: 3 Model(s) Enumerated
VERBOSE: Attempting to dump Connection(s)
VERBOSE: 2 Connection(s) Enumerated
VERBOSE: Attempting to dump secret for connection(s)
VERBOSE: Completed dumping of the space03 workspace
#>
[CmdletBinding()]
Param(
[Parameter(Mandatory=$false, HelpMessage="Subscription to use.")]
[string]$Subscription = "",
[Parameter(Mandatory=$false, HelpMessage="Folder to output to.")]
[string]$folder = ""
)
# Check login status and authenticate if necessary
$LoginStatus = Get-AzContext
$accountName = ($LoginStatus.Account).Id
if ($LoginStatus.Account -eq $null) {
Write-Warning "No active login. Prompting for login."
try {
Connect-AzAccount -ErrorAction Stop
} catch {
Write-Warning "Login process failed."
}
}
# Ensure subscription context is set
if ($Subscription) {
Select-AzSubscription -SubscriptionName $Subscription | Out-Null
} else {
# Prompt user to select subscription(s) if not provided
$Subscriptions = Get-AzSubscription -WarningAction SilentlyContinue
$subChoice = $Subscriptions | Out-GridView -Title "Select One or More Subscriptions" -PassThru
foreach ($sub in $subChoice) {
Get-AzBatchAccountData -Subscription $sub -folder $folder
}
return
}
Write-Verbose "Logged In as $accountName"
# Setup output folder, create if it does not exist
if ($folder -ne "") {
if (!(Test-Path $folder)) {
New-Item -ItemType Directory $folder | Out-Null
}
} else {
$folder = $PWD.Path
}
# Suppress breaking change warnings from Az module
Update-AzConfig -DisplayBreakingChangeWarning $false | Out-Null
Write-Verbose -Message ('Dumping Workspaces from the "' + (Get-AzContext).Subscription.Name + '" Subscription')
# Retrieve all ML Workspaces in the specified resource group
#$workspaces = Get-AzMLWorkspace -ResourceGroupName $ResourceGroupName
$workspaces = Get-AzResource -ResourceType Microsoft.MachineLearningServices/workspaces
Write-Verbose "`t$($workspaces.Count) Workspace(s) Enumerated"
# Iterate through each workspace
$workspaces | ForEach-Object {
$currentWorkspace = $_.Name
$ResourceGroupName = $_.ResourceGroupName
Write-Verbose "`t`tAttempting to dump data from the $currentWorkspace workspace"
# Retrieve and save compute resources
try {
Write-Verbose "`t`t`tAttempting to dump compute data"
$computes = Get-AzMLWorkspaceCompute -ResourceGroupName $ResourceGroupName -WorkspaceName $_.Name |
ForEach-Object {
$propAsObj = $_.Property | ConvertFrom-Json
[PSCustomObject]@{
Name = $_.Name
computeType = $propAsObj.properties.computeType
Id = $_.Id
IdentityType = $_.IdentityType
Location = $_.Location
createdOn = $propAsObj.createdOn
modifiedOn = $propAsObj.modifiedOn
isAttachedCompute = $propAsObj.isAttachedCompute
disableLocalAuth = $propAsObj.disableLocalAuth
subnet = $propAsObj.properties.subnet.id
sshPublicAccess = $propAsObj.properties.sshSettings.sshPublicAccess
adminUserName = $propAsObj.properties.sshSettings.adminUserName
sshPort = $propAsObj.properties.sshSettings.sshPort
publicIpAddress = $propAsObj.properties.connectivityEndpoints.publicIpAddress
privateIpAddress = $propAsObj.properties.connectivityEndpoints.privateIpAddress
lastOperation = $propAsObj.properties.lastOperation.operationTime
schedules = $propAsObj.properties.schedules.computeStartStop
vmSize = $propAsObj.properties.vmSize
applicationSharingPolicy = $propAsObj.properties.applicationSharingPolicy
endpointUri = $propAsObj.properties.applications.endpointUri
state = $propAsObj.properties.state
}
}
Write-Verbose "`t`t`t`t$($computes.Count) Compute Resource(s) Enumerated"
$computes | Out-File -Append "$folder\$currentWorkspace-Computes.txt"
} catch {
Write-Warning "Failed to retrieve compute instances for workspace: $currentWorkspace"
}
# Retrieve and save online endpoints
try {
Write-Verbose "`t`t`tAttempting to dump endpoint data"
$workspace_name = $_.Name
$endpoints = Get-AzMLWorkspaceOnlineEndpoint -ResourceGroupName $ResourceGroupName -WorkspaceName $workspace_name |
ForEach-Object {
$propAsObj = $_.EndpointPropertiesBaseProperty | ConvertFrom-Json
$sysdataAsObj = $_.SystemData | ConvertFrom-Json
$keys = Get-AzMLWorkspaceOnlineEndpointKey -ResourceGroupName $ResourceGroupName -WorkspaceName $workspace_name -Name $_.Name
[PSCustomObject]@{
Name = $_.Name
Id = $_.Id
Description = $_.Description
AuthMode = $_.AuthMode
Type = $_.Type
ScoringUri = $_.ScoringUri
SwaggerUri = $_.SwaggerUri
CreatedBy = $sysdataAsObj.createdBy
CreatedAt = $sysdataAsObj.createdAt
LastModifiedAt = $sysdataAsObj.lastModifiedAt
SystemDataCreatedAt = $_.SystemDataCreatedAt
Onlineendpointid = $propAsObj.'azureml.onlineendpointid'
AzureAsyncOperationUri = $propAsObj.AzureAsyncOperationUri
PrimaryKey = $keys.PrimaryKey
SecondaryKey = $keys.SecondaryKey
}
}
Write-Verbose "`t`t`t`t$($_.Name.Count) Endpoint(s) Enumerated"
$endpoints | Out-File -Append "$folder\$currentWorkspace-Endpoints.txt"
} catch {
Write-Warning "Failed to retrieve endpoints for workspace: $currentWorkspace"
}
# Retrieve and save jobs
try {
Write-Verbose "`t`t`tAttempting to dump jobs data"
$jobs = Get-AzMLWorkspaceJob -ResourceGroupName $ResourceGroupName -WorkspaceName $_.Name |
ForEach-Object {
$propAsObj = $_.Property | ConvertFrom-Json
[PSCustomObject]@{
Name = $_.Name
Id = $_.Id
SystemDataCreatedAt = $_.SystemDataCreatedAt
SystemDataCreatedBy = $_.SystemDataCreatedBy
jobType = $propAsObj.jobType
endpoint = $propAsObj.services.Studio.endpoint
command = $propAsObj.command
environmentId = $propAsObj.environmentId
outputs = $propAsObj.outputs.default
}
}
Write-Verbose "`t`t`t`t$($jobs.Count) Compute Job(s) Enumerated"
$jobs | Out-File -Append "$folder\$currentWorkspace-Jobs.txt"
} catch {
Write-Warning "Failed to retrieve jobs for workspace: $currentWorkspace"
}
# Retrieve and save models
try {
Write-Verbose "`t`t`tAttempting to dump Models"
$models = Get-AzMLWorkspaceModelContainer -ResourceGroupName $ResourceGroupName -WorkspaceName $_.Name |
ForEach-Object {
[PSCustomObject]@{
Name = $_.Name
Id = $_.Id
SystemDataCreatedAt = $_.SystemDataCreatedAt
Type = $_.Type
ProvisioningState = $_.ProvisioningState
IsArchived = $_.IsArchived
}
}
Write-Verbose "`t`t`t`t$($models.Count) Model(s) Enumerated"
$models | Out-File -Append "$folder\$currentWorkspace-Models.txt"
} catch {
Write-Warning "Failed to retrieve models for workspace: $currentWorkspace"
}
# Gather and store connections and keys
Write-Verbose "`t`t`tAttempting to dump Connection(s)"
$url = "https://management.azure.com/subscriptions/$Subscription/resourceGroups/$ResourceGroupName/providers/Microsoft.MachineLearningServices/workspaces/$currentWorkspace/connections?api-version=2023-08-01-preview"
try {
$AccessToken = Get-AzAccessToken -ResourceUrl "https://management.azure.com"
if ($AccessToken.Token -is [System.Security.SecureString]) {
$ssPtr = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($AccessToken.Token)
try {
$Token = [System.Runtime.InteropServices.Marshal]::PtrToStringBSTR($ssPtr)
} finally {
[System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($ssPtr)
}
} else {
$Token = $AccessToken.Token
}
if (-not $Token) {
Write-Error "Unable to retrieve the access token. Make sure you are logged in using Az PowerShell."
exit 1
}
} catch {
Write-Error "Error while obtaining the access token: $_"
exit 1
}
# HTTP headers
$headers = @{
"Authorization" = "Bearer $Token"
"Content-Type" = "application/json"
}
try {
# HTTP request to access connections within the workspace.
$connectionsResponse = Invoke-RestMethod -Uri $url -Method Get -Headers $headers -ErrorAction Stop
Write-Verbose "`t`t`t`t$($connectionsResponse.value.Count) Connection(s) Enumerated"
} catch {
Write-Error "Error while fetching connections: $_"
exit 1
}
# For each connection, retrieve the secret.
Write-Verbose "`t`t`t`t`tAttempting to dump secret for connection(s)"
foreach ($connection in $connectionsResponse.value) {
try {
$connectionName = $connection.name
$secretUrl = "https://management.azure.com/subscriptions/$Subscription/resourceGroups/$ResourceGroupName/providers/Microsoft.MachineLearningServices/workspaces/$currentWorkspace/connections/$connectionName/listsecrets?api-version=2023-08-01-preview"
$secretsResponse = Invoke-RestMethod -Uri $secretUrl -Method Post -Headers $headers -ErrorAction Stop -Verbose:$false 4>$null
$connectionObject = [PSCustomObject]@{
Name = $connection.name
Type = $connection.type
Secret = $secretsResponse.properties.credentials.key
}
$connectionObject | Out-File -Append "$folder\$currentWorkspace-Connections.txt"
} catch {
Write-Error "Error processing connection '$($connection.name)': $_"
}
}
Write-Verbose "`t`tCompleted dumping of the $currentWorkspace workspace"
}
}