Private/Resolve-FDAConnection.ps1
|
function Read-FDASelection { <# .SYNOPSIS Prompt for a numeric menu choice and re-prompt until it is in range. .PARAMETER Max Highest valid number. .PARAMETER Prompt Text shown to the user. .PARAMETER AllowZero Permit 0 (used for the '<create new>' menu entry). #> [CmdletBinding()] param( [Parameter(Mandatory)] [int] $Max, [Parameter(Mandatory)] [string] $Prompt, [switch] $AllowZero ) $low = if ($AllowZero) { 0 } else { 1 } while ($true) { $raw = Read-Host $Prompt if ($raw -match '^\s*\d+\s*$') { $n = [int]$raw.Trim() if ($n -ge $low -and $n -le $Max) { return $n } } Write-Host "Please enter a number between $low and $Max." -ForegroundColor Red } } function Get-FDATokenTenantId { <# .SYNOPSIS Best-effort: read the 'tid' (tenant) claim out of a Fabric access token. Used as a fallback when ARM tenant enumeration is unavailable. #> [CmdletBinding()] param() try { $token = Get-FDAAccessToken -Scope 'https://api.fabric.microsoft.com/.default' $parts = $token.Split('.') if ($parts.Count -lt 2) { return $null } $payload = $parts[1].Replace('-', '+').Replace('_', '/') switch ($payload.Length % 4) { 2 { $payload += '==' } 3 { $payload += '=' } } $json = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($payload)) $claims = $json | ConvertFrom-Json if ($claims.PSObject.Properties.Name -contains 'tid') { return $claims.tid } return $null } catch { Write-Verbose "Could not decode tenant from token: $($_.Exception.Message)" return $null } } function Resolve-FDATenant { <# .SYNOPSIS Determine which tenant to operate against after an interactive sign-in. .DESCRIPTION Enumerates the tenants the signed-in user can access via Azure Resource Manager. If exactly one is found it is used automatically; if several are found the user is prompted to pick one. If ARM is inaccessible (e.g. the user has no Azure RBAC), falls back to the tenant ('tid') claim on a Fabric token. #> [CmdletBinding()] param() $tenants = @() try { $armToken = Get-FDAAccessToken -Scope 'https://management.azure.com/.default' $resp = Invoke-RestMethod -Method Get ` -Uri 'https://management.azure.com/tenants?api-version=2020-01-01' ` -Headers @{ Authorization = "Bearer $armToken" } -ErrorAction Stop if (($resp.PSObject.Properties.Name -contains 'value') -and $resp.value) { $tenants = @($resp.value) } } catch { Write-Verbose "ARM tenant enumeration unavailable: $($_.Exception.Message)" } if ($tenants.Count -eq 0) { $tid = Get-FDATokenTenantId if ($tid) { Write-Verbose "Falling back to token tenant claim: $tid" return $tid } throw 'Unable to determine a tenant automatically. Re-run with -TenantId.' } if ($tenants.Count -eq 1) { return $tenants[0].tenantId } Write-Host '' Write-Host 'You have access to multiple tenants:' -ForegroundColor Yellow for ($i = 0; $i -lt $tenants.Count; $i++) { $t = $tenants[$i] $name = if ($t.PSObject.Properties.Name -contains 'displayName' -and $t.displayName) { $t.displayName } elseif ($t.PSObject.Properties.Name -contains 'defaultDomain' -and $t.defaultDomain) { $t.defaultDomain } else { '(unnamed)' } Write-Host (' [{0}] {1} ({2})' -f ($i + 1), $name, $t.tenantId) } $sel = Read-FDASelection -Max $tenants.Count -Prompt 'Select tenant number' return $tenants[$sel - 1].tenantId } function Resolve-FDAWorkspace { <# .SYNOPSIS Prompt the user to select an existing Fabric workspace or create one. Returns the chosen workspace id. #> [CmdletBinding()] param() $workspaces = @(Get-FDAWorkspaceList) Write-Host '' Write-Host 'Fabric workspaces:' -ForegroundColor Yellow Write-Host ' [0] <Create a new workspace>' for ($i = 0; $i -lt $workspaces.Count; $i++) { Write-Host (' [{0}] {1} ({2})' -f ($i + 1), $workspaces[$i].displayName, $workspaces[$i].id) } $sel = Read-FDASelection -Max $workspaces.Count -Prompt 'Select workspace number (0 to create new)' -AllowZero if ($sel -eq 0) { $name = Read-Host 'New workspace display name' if (-not $name) { throw 'A workspace display name is required.' } Write-Verbose "Creating workspace '$name'..." $ws = New-FDAWorkspace -DisplayName $name if (-not $ws -or -not $ws.id) { throw "Workspace '$name' was not created." } Write-Host "Created workspace: $($ws.displayName) ($($ws.id))" -ForegroundColor Green return $ws.id } return $workspaces[$sel - 1].id } function Resolve-FDAEventhouse { <# .SYNOPSIS Prompt the user to select an existing Eventhouse in the workspace or create one. Returns the chosen Eventhouse id, waiting for endpoints to materialize when a new Eventhouse is provisioned. #> [CmdletBinding()] param([Parameter(Mandatory)] [string] $WorkspaceId) $eventhouses = @(Get-FDAEventhouseList -WorkspaceId $WorkspaceId) Write-Host '' Write-Host 'Eventhouses in the selected workspace:' -ForegroundColor Yellow Write-Host ' [0] <Create a new Eventhouse>' for ($i = 0; $i -lt $eventhouses.Count; $i++) { Write-Host (' [{0}] {1} ({2})' -f ($i + 1), $eventhouses[$i].displayName, $eventhouses[$i].id) } $sel = Read-FDASelection -Max $eventhouses.Count -Prompt 'Select Eventhouse number (0 to create new)' -AllowZero if ($sel -ne 0) { return $eventhouses[$sel - 1].id } $name = Read-Host 'New Eventhouse display name' if (-not $name) { throw 'An Eventhouse display name is required.' } Write-Verbose "Creating Eventhouse '$name' in workspace $WorkspaceId..." $eh = New-FDAEventhouse -WorkspaceId $WorkspaceId -DisplayName $name if (-not $eh -or -not $eh.id) { throw "Eventhouse '$name' was not created." } $eventhouseId = $eh.id # Endpoints are provisioned asynchronously; wait for them so the caller can # immediately resolve the query/ingest URIs. Write-Host 'Waiting for Eventhouse endpoints to become available...' -ForegroundColor DarkGray $deadline = (Get-Date).AddMinutes(5) do { Start-Sleep -Seconds 5 $endpoints = Get-FDAEventhouseEndpoint -WorkspaceId $WorkspaceId -EventhouseId $eventhouseId if ($endpoints.QueryServiceUri) { break } } while ((Get-Date) -lt $deadline) if (-not $endpoints.QueryServiceUri) { throw 'Eventhouse created but endpoints did not materialize within 5 minutes.' } Write-Host "Created Eventhouse: $($endpoints.DisplayName) ($eventhouseId)" -ForegroundColor Green return $eventhouseId } |