modules/IW.Core.psm1
|
# SailPoint Connect core: definition registry, progressive disclosure, wizard driver. Set-StrictMode -Version Latest $script:IntegrationWizardDefinitions = @{} $script:IntegrationWizardPlatforms = @{} function Register-IntegrationWizardPlatform { param( [Parameter(Mandatory)] [string]$Name, [Parameter(Mandatory)] [string]$Title, [Parameter(Mandatory)] [string[]]$CoreOptions, [string]$CliName = 'aws', [hashtable]$EntroCompanions = @{} ) $script:IntegrationWizardPlatforms[$Name] = [ordered]@{ Name = $Name Title = $Title CoreOptions = $CoreOptions CliName = $CliName EntroCompanions = $EntroCompanions } } function Register-IntegrationWizardDefinition { param( [Parameter(Mandatory)] [hashtable]$Definition ) if (-not $Definition.Id) { throw 'Definition requires Id.' } $script:IntegrationWizardDefinitions[$Definition.Id] = $Definition } function Get-IntegrationWizardDefinition { param([Parameter(Mandatory)][string]$Id) $script:IntegrationWizardDefinitions[$Id] } function Get-IntegrationWizardDefinitionsForPlatform { param([Parameter(Mandatory)][string]$Platform) $script:IntegrationWizardDefinitions.Values | Where-Object { $_.Platform -eq $Platform } } function Get-IntegrationWizardWizardRoot { param([Parameter(Mandatory)][string]$Platform) Split-Path (Split-Path $PSScriptRoot -Parent) -Parent } function Import-IntegrationWizardConsole { param( [string]$WizardRoot, [switch]$NonInteractive ) $mod = Join-Path $WizardRoot 'modules/ISC.OperatorConsole.psm1' Import-Module $mod -Force -Global -WarningAction SilentlyContinue Import-Module (Join-Path $WizardRoot 'modules/ISC.OperatorToolchain.psm1') -Force -Global -WarningAction SilentlyContinue Initialize-OperatorConsole -NonInteractive:$NonInteractive } function Show-IntegrationWizardPlatformHelp { param( [Parameter(Mandatory)][string]$Platform, [Parameter(Mandatory)][string]$ScriptName, [Parameter(Mandatory)][hashtable]$CoreOptionHelp ) $meta = $script:IntegrationWizardPlatforms[$Platform] Write-Host '' Write-Host "$($meta.Title) integration wizard" -ForegroundColor Cyan Write-Host " ./$ScriptName -Wizard Guided setup, asks every question" foreach ($key in $CoreOptionHelp.Keys) { Write-Host " ./$ScriptName -Wizard -$key Guided setup, $($CoreOptionHelp[$key].Short) already chosen" } foreach ($key in $CoreOptionHelp.Keys) { Write-Host " ./$ScriptName -$key [...] $($CoreOptionHelp[$key].Long)" } Write-Host 'Then choose what to do: -Action Run | BuildScript | BuildRequirements' Write-Host ' Artifacts go under ~/.sailpoint-connect/<project>/<platform>/<integration>/ (-Project to skip the project menu)' Write-Host ' Combine ISC connector + Entro: pass both switches (e.g. -SaaSConnector -Entro on AWS)' $first = ($CoreOptionHelp.Keys | Select-Object -First 1) if ($first) { Write-Host "Next: ./$ScriptName -$first (lists its options)" } Write-Host '' } function Test-IntegrationWizardContextSwitch { param([hashtable]$Context, [string]$Name) return $Context.ContainsKey($Name) -and [bool]$Context[$Name] } function Get-IntegrationWizardEntroCompanionConfig { param( [Parameter(Mandatory)][string]$Platform, [Parameter(Mandatory)][string]$ConnectorCoreSwitch ) $meta = $script:IntegrationWizardPlatforms[$Platform] if (-not $meta -or -not $meta.EntroCompanions) { return $null } $meta.EntroCompanions[$ConnectorCoreSwitch] } function Resolve-IntegrationWizardEntroAddon { param( [Parameter(Mandatory)][string]$Platform, [Parameter(Mandatory)][hashtable]$Context, [Parameter(Mandatory)][hashtable]$ConnectorDefinition, [switch]$Wizard, [switch]$NonInteractive ) $companion = Get-IntegrationWizardEntroCompanionConfig -Platform $Platform -ConnectorCoreSwitch $ConnectorDefinition.CoreSwitch if (-not $companion) { return } if ($companion['EntroSwitch'] -and (Test-IntegrationWizardContextSwitch -Context $Context -Name $companion['EntroSwitch'])) { $Context[$companion['EntroSwitch']] = $true return } if ($companion['Variants']) { foreach ($v in @($companion['Variants'])) { if (Test-IntegrationWizardContextSwitch -Context $Context -Name $v) { return } } } if (-not $Wizard) { return } while ($true) { $configureEntro = Read-YesNo -Prompt 'Also configure Entro for this platform?' -Default $false if (-not $configureEntro) { if ($companion['EntroSwitch']) { $Context[$companion['EntroSwitch']] = $false } if ($companion['Variants']) { foreach ($v in @($companion['Variants'])) { $Context[$v] = $false } } return } if ($companion['EntroSwitch']) { $Context[$companion['EntroSwitch']] = $true return } if ($companion['Variants']) { try { $picked = Read-Choice -Prompt 'Which Entro integration?' -Options $companion['Variants'] ` -Labels @($companion['Variants'] | ForEach-Object { switch ($_) { 'GcpEntro' { 'Entro Google GCP organization' } 'WorkspaceEntro' { 'Entro Google Workspace' } default { $_ } } }) -Default $companion['Variants'][0] $Context[$picked] = $true return } catch { if (Test-PromptBack $_) { continue } throw } } } } function Get-IntegrationWizardTargets { param( [Parameter(Mandatory)][string]$Platform, [Parameter(Mandatory)][hashtable]$Context ) $all = @(Get-IntegrationWizardDefinitionsForPlatform -Platform $Platform) $active = @($all | Where-Object { Test-IntegrationWizardContextSwitch -Context $Context -Name $_.CoreSwitch }) $isc = @($active | Where-Object { $_.Product -eq 'ISC' }) $entro = @($active | Where-Object { $_.Product -eq 'Entro' }) if ($isc.Count -gt 1) { throw 'Only one ISC connector target can be selected per run.' } if ($isc.Count -eq 1 -and $isc[0]['CoreSwitch'] -eq 'CiemConnector' -and $entro.Count -gt 0) { throw 'CIEM connector cannot be combined with Entro in the same run.' } if ($entro.Count -gt 1) { throw 'Only one Entro target can be selected per run.' } if ($isc.Count -eq 0 -and $entro.Count -eq 0) { return @() } if ($isc.Count -eq 0) { return @($entro[0]) } if ($entro.Count -eq 0) { return @($isc[0]) } return @($isc[0], $entro[0]) } function Merge-IntegrationWizardDefinitions { param( [Parameter(Mandatory)][object[]]$Members ) if ($Members.Count -eq 1) { return $Members[0] } $cli = $Members[0].CliName foreach ($m in $Members) { if ($m.CliName -ne $cli) { throw 'Combined definitions must use the same CLI.' } } $mergedInputs = @{} foreach ($m in $Members) { foreach ($input in @($m.Inputs)) { if (-not $input) { continue } $name = $input.Name if ($mergedInputs.ContainsKey($name)) { $existing = $mergedInputs[$name] if ($existing.Purpose -ne $input.Purpose) { throw "Input name collision '$name' with different purpose; use distinct names (e.g. IscExternalId vs EntroExternalId)." } continue } $mergedInputs[$name] = $input } } $titles = @($Members | ForEach-Object { $_.Title }) $ids = @($Members | ForEach-Object { $_.Id }) -join '+' $access = @() foreach ($m in $Members) { if ($m['AccessLevels'] -and @($m['AccessLevels']).Count -gt 0) { $access = @($m['AccessLevels']); break } } $membersCopy = @($Members) $folderNames = @($Members | ForEach-Object { if ($_['FolderName']) { [string]$_['FolderName'] } else { [string]$_.Title } }) return @{ IsComposite = $true Id = $ids Platform = $Members[0].Platform Product = 'Combined' CliName = $cli Title = ($titles -join ' + ') FolderName = ($folderNames -join ' and ') AccessLevels = $access Inputs = @($mergedInputs.Values) Members = $membersCopy AdminRoles = @($Members | ForEach-Object { @($_['AdminRoles']) } | ForEach-Object { $_ }) OutputsHelp = ($Members | ForEach-Object { if ($_['OutputsHelp']) { $_['OutputsHelp'] } }) -join "`n`n" Steps = { param($ctx) $allSteps = @() foreach ($member in $membersCopy) { $allSteps += & $member.Steps $ctx } $allSteps } } } function Resolve-IntegrationWizardPlan { param( [Parameter(Mandatory)][string]$Platform, [Parameter(Mandatory)][hashtable]$Context ) $members = @(Get-IntegrationWizardTargets -Platform $Platform -Context $Context) if ($members.Count -eq 0) { return $null } if ($members.Count -eq 1) { return $members[0] } Merge-IntegrationWizardDefinitions -Members $members } function Get-IntegrationWizardDefinitionByCoreSwitch { param( [Parameter(Mandatory)][string]$Platform, [Parameter(Mandatory)][hashtable]$BoundParameters ) foreach ($def in (Get-IntegrationWizardDefinitionsForPlatform -Platform $Platform)) { if ($def.CoreSwitch -and $BoundParameters.ContainsKey($def.CoreSwitch)) { return $def } } $null } function Test-IntegrationWizardInputWhen { param($InputDef, [hashtable]$Context) if (-not $InputDef) { return $false } if (-not ($InputDef.ContainsKey('When') -or ($InputDef.PSObject.Properties.Name -contains 'When'))) { return $true } $when = if ($InputDef -is [hashtable]) { $InputDef['When'] } else { $InputDef.When } if (-not $when) { return $true } & $when $Context } function Test-IntegrationWizardRequiredInputs { param( [Parameter(Mandatory)][hashtable]$Definition, [Parameter(Mandatory)][hashtable]$Context ) $missing = @() foreach ($input in @($Definition.Inputs)) { if (-not $input) { continue } if (-not (Test-IntegrationWizardInputWhen -InputDef $input -Context $Context)) { continue } $name = $input.Name if ($input['Required'] -and [string]::IsNullOrWhiteSpace([string]$Context[$name])) { $missing += $name } } ,$missing } function Show-IntegrationWizardDefinitionHelp { param( [Parameter(Mandatory)][hashtable]$Definition, [Parameter(Mandatory)][string]$ScriptName, [Parameter(Mandatory)][hashtable]$Context, [string[]]$Missing ) Write-Host '' Write-Host "$($Definition.Title)" -ForegroundColor Cyan if ($Definition['IsComposite']) { Write-Host ' Combined targets:' foreach ($m in @($Definition.Members)) { Write-Host " -$($m['CoreSwitch']) $($m['Title'])" } } elseif ($Definition['CoreSwitch']) { Write-Host " Core switch: -$($Definition['CoreSwitch'])" } if ($Definition['AccessLevels'] -and @($Definition['AccessLevels']).Count -gt 1) { Write-Host ' -Access ReadOnly|ReadWrite' } Write-Host ' -Action Run|BuildScript|BuildRequirements' Write-Host ' -OutputDirectory' Write-Host ' -NonInteractive -OutputFormat Text|Json' Write-Host '' Write-Host 'Inputs:' foreach ($input in @($Definition.Inputs)) { if (-not $input) { continue } if (-not (Test-IntegrationWizardInputWhen -InputDef $input -Context $Context)) { continue } $req = if ($input['Required']) { ' (required)' } else { '' } $src = if ($input['Source'] -eq 'Discover') { ' [discover]' } else { '' } Write-Host " -$($input.Name)$req$src — $($input['Purpose'])" } if ($Missing -and $Missing.Count -gt 0) { Write-Host '' Write-Host 'Missing:' -ForegroundColor Yellow foreach ($m in $Missing) { Write-Host " -$m" -ForegroundColor Yellow } } Write-Host '' } function Resolve-IntegrationWizardAccessLevel { param( [Parameter(Mandatory)][hashtable]$Definition, [Parameter(Mandatory)][hashtable]$Context, [switch]$Wizard, [switch]$NonInteractive ) $levels = @($Definition['AccessLevels']) if ($levels.Count -le 1) { if ($levels.Count -eq 1) { $Context['Access'] = $levels[0] } return } if (-not [string]::IsNullOrWhiteSpace([string]$Context['Access'])) { return } if (-not $Wizard) { if ($NonInteractive) { throw "Missing required -Access ($($levels -join '|'))." } return } $Context['Access'] = Read-Choice -Prompt 'Access level for this connector:' ` -Options $levels ` -Labels @($levels | ForEach-Object { if ($_ -eq 'ReadOnly') { 'Read only (aggregation, no provisioning writes)' } else { 'Read and write (includes provisioning permissions)' } }) -Default 'ReadOnly' } function Show-IntegrationWizardConnectionCheck { param( [Parameter(Mandatory)][string]$CliName, [Parameter(Mandatory)][hashtable]$SessionState ) Write-Step 'Current CLI session' if ($SessionState.Installed -and $SessionState.SignedIn) { Write-Ok "${CliName}: $($SessionState.Summary)" } elseif ($SessionState.Installed) { $detail = if ($SessionState['Summary']) { " ($($SessionState['Summary']))" } else { '' } Write-Info "$CliName is installed but not signed in.$detail" } else { Write-Info "$CliName was not found on PATH." } } function Resolve-IntegrationWizardConnectionChoice { param( [Parameter(Mandatory)][string]$CliName, [Parameter(Mandatory)][hashtable]$SessionState, [switch]$Wizard, [switch]$NonInteractive ) if (-not $Wizard) { if ($SessionState['Mode']) { return $SessionState['Mode'] } return 'UseSession' } Show-IntegrationWizardConnectionCheck -CliName $CliName -SessionState $SessionState # "Use this session" is only offered when a usable session exists. An installed CLI with # expired or missing credentials cannot introspect, so that choice is omitted. $options = @('SignIn', 'NoSession') $labels = @( 'Install or sign in / switch account' 'Continue without a session (type values manually)' ) $default = 'SignIn' if ($SessionState.SignedIn) { $options = @('UseSession') + $options $labels = @('Use this session (introspect and pick lists)') + $labels $default = 'UseSession' } $pick = Read-Choice -Prompt 'How do you want to connect?' -Options $options -Labels $labels -Default $default $SessionState['Mode'] = $pick return $pick } function Collect-IntegrationWizardInputs { param( [Parameter(Mandatory)][hashtable]$Definition, [Parameter(Mandatory)][hashtable]$Context, [switch]$Wizard, [switch]$NonInteractive, [scriptblock]$Discover ) $inputList = [System.Collections.Generic.List[hashtable]]::new() foreach ($inp in @($Definition.Inputs)) { if (-not $inp) { continue } $inputList.Add($inp) } $idx = 0 $prompted = [System.Collections.Generic.HashSet[string]]::new() while ($idx -ge 0 -and $idx -lt $inputList.Count) { $input = $inputList[$idx] if (-not (Test-IntegrationWizardInputWhen -InputDef $input -Context $Context)) { $idx++ continue } $name = $input.Name if ($Context.ContainsKey($name) -and -not [string]::IsNullOrWhiteSpace([string]$Context[$name]) -and (-not $prompted.Contains($name))) { $idx++ continue } if (-not $Wizard) { $idx++ continue } $discoverValues = $null if ($Context['ConnectionMode'] -ne 'NoSession' -and $input['Source'] -eq 'Discover' -and $Discover) { $discoverValues = & $Discover $name $Context } try { if ($discoverValues -and @($discoverValues).Count -gt 0) { $typed = 'Type a different value' $options = @($discoverValues) + @($typed) $picked = Read-Choice -Prompt $input['Prompt'] -Options $options -Default $discoverValues[0] if ($picked -ne $typed) { $Context[$name] = $picked [void]$prompted.Add($name) $idx++ continue } } if ($input['ValidateSet']) { $Context[$name] = Read-Choice -Prompt $input['Prompt'] -Options $input['ValidateSet'] -Default $input['Default'] } else { $Context[$name] = Read-InputString -Prompt $input['Prompt'] -Default $input['Default'] -Required:([bool]$input['Required']) } [void]$prompted.Add($name) $idx++ } catch { if (Test-PromptBack $_) { $Context.Remove($name) [void]$prompted.Remove($name) $idx-- if ($idx -ge 0) { $prevName = $inputList[$idx].Name $Context.Remove($prevName) [void]$prompted.Remove($prevName) } continue } throw } } if ($idx -lt 0) { Invoke-PromptBack } } function Show-IntegrationWizardSummary { param( [Parameter(Mandatory)][hashtable]$Definition, [Parameter(Mandatory)][hashtable]$Context ) Write-Host '' Write-Host 'Plan summary' -ForegroundColor Cyan if ($Context['IntegrationProjectName']) { Write-Host " Project: $($Context['IntegrationProjectName'])" Write-Host " Folder: $($Context['IntegrationFolderPath'])" } Write-Host " Target: $($Definition.Title)" if ($Context['Access']) { Write-Host " Access: $($Context['Access'])" } if ($Context['Action']) { Write-Host " Action: $($Context['Action'])" } foreach ($input in @($Definition.Inputs)) { if (-not $input) { continue } if (-not (Test-IntegrationWizardInputWhen -InputDef $input -Context $Context)) { continue } $val = $Context[$input.Name] if ($input['Secret']) { $val = if ($val) { '***' } else { '(created at run)' } } Write-Host " $($input.Name): $val" } Write-Host '' } function Resolve-IntegrationWizardAction { param( [Parameter(Mandatory)][hashtable]$Context, [switch]$Wizard ) if (-not [string]::IsNullOrWhiteSpace([string]$Context['Action'])) { return } if (-not $Wizard) { throw 'Missing required -Action (Run|BuildScript|BuildRequirements).' } $Context['Action'] = Read-Choice -Prompt 'What should happen next?' ` -Options @('Run', 'BuildScript', 'BuildRequirements') ` -Labels @( 'Run changes (apply on the target now)' 'Build script (PowerShell 7 + platform CLI, review before running)' 'Build requirements (Markdown document)' ) -Default 'Run' } function Start-IntegrationWizardFlow { [CmdletBinding(SupportsShouldProcess = $true)] param( [Parameter(Mandatory)][string]$Platform, [Parameter(Mandatory)][string]$ScriptName, [Parameter(Mandatory)][hashtable]$CoreOptionHelp, [Parameter(Mandatory)][hashtable]$Context, [scriptblock]$PickTarget, [scriptblock]$Discover, [scriptblock]$Execute ) $wizard = [bool]$Context['Wizard'] $nonInteractive = [bool]$Context['NonInteractive'] $plan = $null try { $plan = Resolve-IntegrationWizardPlan -Platform $Platform -Context $Context } catch { Write-Error $_.Exception.Message return } if (-not $plan -and -not $wizard) { Show-IntegrationWizardPlatformHelp -Platform $Platform -ScriptName $ScriptName -CoreOptionHelp $CoreOptionHelp return } $projectMod = Join-Path $PSScriptRoot 'IW.Project.psm1' if (-not (Get-Module -Name 'IW.Project')) { Import-Module $projectMod -Force -WarningAction SilentlyContinue } if ($wizard) { $stage = 0 $needWorkspace = Test-IntegrationWizardNeedsProjectContext -Wizard:$wizard -Context $Context $pickedTarget = $false $wizardComplete = $false :wizardLoop while (-not $wizardComplete) { try { switch ($stage) { 0 { if ($needWorkspace -and [string]::IsNullOrWhiteSpace([string]$Context['IntegrationProjectName'])) { Select-IntegrationProjectWorkspace -Context $Context } $stage = 1 } 1 { if (-not $plan -or $pickedTarget) { if ($PickTarget) { :pickLoop while ($true) { $connectorDef = & $PickTarget if (-not $connectorDef) { break :pickLoop } $pickedTarget = $true if ($connectorDef.Product -eq 'ISC') { try { Resolve-IntegrationWizardEntroAddon -Platform $Platform -Context $Context -ConnectorDefinition $connectorDef ` -Wizard:$wizard -NonInteractive:$nonInteractive break :pickLoop } catch { if (Test-PromptBack $_) { continue } throw } } else { break :pickLoop } } if ($connectorDef) { $plan = Resolve-IntegrationWizardPlan -Platform $Platform -Context $Context } } } elseif ($plan -and -not $plan['IsComposite'] -and $plan.Product -eq 'ISC') { Resolve-IntegrationWizardEntroAddon -Platform $Platform -Context $Context -ConnectorDefinition $plan ` -Wizard:$wizard -NonInteractive:$nonInteractive try { $plan = Resolve-IntegrationWizardPlan -Platform $Platform -Context $Context } catch { Write-Error $_.Exception.Message return } } if (-not $plan) { Show-IntegrationWizardPlatformHelp -Platform $Platform -ScriptName $ScriptName -CoreOptionHelp $CoreOptionHelp return } $stage = 2 } 2 { Resolve-IntegrationWizardAccessLevel -Definition $plan -Context $Context -Wizard:$wizard -NonInteractive:$nonInteractive $meta = $script:IntegrationWizardPlatforms[$Platform] if ($needWorkspace) { Initialize-IntegrationProjectContext -Platform $Platform -PlatformTitle $meta.Title -Plan $plan -Context $Context -ScriptName $ScriptName } $stage = 3 } 3 { $meta = $script:IntegrationWizardPlatforms[$Platform] $session = Get-IntegrationWizardCliSession -CliName $meta.CliName $mode = Resolve-IntegrationWizardConnectionChoice -CliName $meta.CliName -SessionState $session -Wizard:$wizard -NonInteractive:$nonInteractive $Context['ConnectionMode'] = $mode if ($mode -eq 'SignIn') { Install-IntegrationWizardCli -CliName $meta.CliName -NonInteractive:$nonInteractive Invoke-IntegrationWizardCliLogin -CliName $meta.CliName -NonInteractive:$nonInteractive } $stage = 4 } 4 { Collect-IntegrationWizardInputs -Definition $plan -Context $Context -Wizard:$wizard -NonInteractive:$nonInteractive -Discover $Discover $stage = 5 } 5 { Resolve-IntegrationWizardAction -Context $Context -Wizard:$wizard $stage = 6 } 6 { Show-IntegrationWizardSummary -Definition $plan -Context $Context if (-not (Read-YesNo -Prompt 'Proceed?' -Default $true)) { Write-Host 'Cancelled.' -ForegroundColor Yellow if (Get-Command Close-IntegrationProjectActivityLog -ErrorAction SilentlyContinue) { Close-IntegrationProjectActivityLog } return } $wizardComplete = $true break :wizardLoop } } } catch { if (Test-PromptBack $_) { if ($stage -eq 6) { $Context.Remove('Action') $stage = 5 continue } elseif ($stage -eq 5) { $stage = 4 continue } elseif ($stage -eq 4) { $Context.Remove('ConnectionMode') $stage = 3 continue } elseif ($stage -eq 3) { if ($plan['AccessLevels'] -and @($plan['AccessLevels']).Count -gt 1) { $Context.Remove('Access') $stage = 2 } else { $stage = 1 } continue } elseif ($stage -eq 2) { $Context.Remove('Access') $stage = 1 continue } elseif ($stage -le 1) { Write-Host '' Write-Host 'Cancelled.' -ForegroundColor Yellow if (Get-Command Close-IntegrationProjectActivityLog -ErrorAction SilentlyContinue) { Close-IntegrationProjectActivityLog } return } } throw } } } else { if (-not $plan) { Show-IntegrationWizardPlatformHelp -Platform $Platform -ScriptName $ScriptName -CoreOptionHelp $CoreOptionHelp return } Resolve-IntegrationWizardAccessLevel -Definition $plan -Context $Context -Wizard:$false -NonInteractive:$nonInteractive if (Test-IntegrationWizardNeedsProjectContext -Wizard:$false -Context $Context) { $meta = $script:IntegrationWizardPlatforms[$Platform] Initialize-IntegrationProjectContext -Platform $Platform -PlatformTitle $meta.Title -Plan $plan -Context $Context -ScriptName $ScriptName } if ([string]::IsNullOrWhiteSpace([string]$Context['Action'])) { Show-IntegrationWizardDefinitionHelp -Definition $plan -ScriptName $ScriptName -Context $Context -Missing @('Action') return } } if ($Context['IntegrationProjectName']) { $record = Get-IntegrationProjectSelectionSnapshot -Context $Context -Definition $plan -Platform $Platform -ScriptName $ScriptName Write-IntegrationProjectSelection -Record $record Write-IntegrationProjectLogLine -Level 'INFO' -Message "Recorded selection for project $($Context['IntegrationProjectName'])" } try { & $Execute $plan $Context } finally { if (Get-Command Close-IntegrationProjectActivityLog -ErrorAction SilentlyContinue) { Close-IntegrationProjectActivityLog } } } function Get-IntegrationWizardCliSession { param([Parameter(Mandatory)][string]$CliName) $state = @{ Installed = $false SignedIn = $false Summary = '' } $cmd = Get-Command $CliName -ErrorAction SilentlyContinue if (-not $cmd) { return $state } $state.Installed = $true try { switch ($CliName) { 'aws' { $json = aws sts get-caller-identity --output json 2>$null if ($LASTEXITCODE -eq 0 -and $json) { $o = $json | ConvertFrom-Json $state.SignedIn = $true $state.Summary = "account $($o.Account) ($($o.Arn))" } } 'az' { $json = az account show --output json 2>$null if ($LASTEXITCODE -eq 0 -and $json) { $o = $json | ConvertFrom-Json $state.SignedIn = $true $state.Summary = "subscription $($o.name) ($($o.id))" } } 'gcloud' { $acct = gcloud config get-value account 2>$null $proj = gcloud config get-value project 2>$null if ($acct) { # A configured account is not enough: its token may have expired. Only a token # that can be minted counts as signed in. gcloud auth print-access-token --quiet 2>$null | Out-Null if ($LASTEXITCODE -eq 0) { $state.SignedIn = $true $state.Summary = "account $acct project $proj" } else { $state.Summary = "credentials for $acct have expired" } } } 'databricks' { $out = databricks auth describe --output json 2>$null if ($LASTEXITCODE -eq 0 -and $out) { $state.SignedIn = $true $state.Summary = 'Databricks CLI authenticated' } } } } catch { } return $state } function Install-IntegrationWizardCli { param( [Parameter(Mandatory)][string]$CliName, [switch]$NonInteractive ) switch ($CliName) { 'aws' { Ensure-CliCommand -CommandNames @('aws', 'aws.exe') ` -WingetPackageId 'Amazon.AWSCLI' -BrewPackage 'awscli' ` -ManualHint 'Install AWS CLI v2 from https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html' | Out-Null } 'az' { Ensure-AzCliCommand -ErrorAction Stop } 'gcloud' { Ensure-GCloudCommand -ErrorAction Stop } 'databricks' { if (-not (Get-Command databricks -ErrorAction SilentlyContinue)) { if ($NonInteractive) { throw 'Install the Databricks CLI: https://docs.databricks.com/dev-tools/cli/install.html' } Write-Info 'Install the Databricks CLI, then press Enter.' Read-Host | Out-Null } } } } function Invoke-IntegrationWizardCliLogin { param( [Parameter(Mandatory)][string]$CliName, [switch]$NonInteractive ) if ($NonInteractive) { throw "Sign in with: $(Get-IntegrationWizardLoginHint -CliName $CliName)" } Write-Info (Get-IntegrationWizardLoginHint -CliName $CliName) switch ($CliName) { 'aws' { aws sso login 2>$null; if ($LASTEXITCODE -ne 0) { aws configure } } 'az' { az login | Out-Null } 'gcloud' { gcloud auth login } 'databricks' { databricks auth login | Out-Null } } } function Get-IntegrationWizardLoginHint { param([string]$CliName) switch ($CliName) { 'aws' { return 'Run: aws sso login (or aws configure)' } 'az' { return 'Run: az login' } 'gcloud' { return 'Run: gcloud auth login' } 'databricks' { return 'Run: databricks auth login' } default { return "Sign in to $CliName" } } } Export-ModuleMember -Function @( 'Register-IntegrationWizardPlatform' 'Register-IntegrationWizardDefinition' 'Get-IntegrationWizardDefinition' 'Get-IntegrationWizardDefinitionsForPlatform' 'Get-IntegrationWizardWizardRoot' 'Import-IntegrationWizardConsole' 'Show-IntegrationWizardPlatformHelp' 'Show-IntegrationWizardDefinitionHelp' 'Get-IntegrationWizardDefinitionByCoreSwitch' 'Get-IntegrationWizardTargets' 'Merge-IntegrationWizardDefinitions' 'Resolve-IntegrationWizardPlan' 'Resolve-IntegrationWizardEntroAddon' 'Resolve-IntegrationWizardConnectionChoice' 'Test-IntegrationWizardRequiredInputs' 'Test-IntegrationWizardInputWhen' 'Start-IntegrationWizardFlow' 'Get-IntegrationWizardCliSession' 'Install-IntegrationWizardCli' 'Invoke-IntegrationWizardCliLogin' 'Get-IntegrationWizardLoginHint' ) |