modules/ISC.GoogleSourceSetup.psm1
|
#Requires -Version 5.1 Set-StrictMode -Version Latest function Import-SourceSetupModule { param( [Parameter(Mandatory)][string]$ModuleRoot, [Parameter(Mandatory)][string]$Name, [Parameter(Mandatory)][string]$FileName ) if (-not (Get-Module -Name $Name)) { Import-Module (Join-Path $ModuleRoot $FileName) -Force -Global -WarningAction SilentlyContinue } } function Import-AgentAdapterModule { if (-not (Get-Module -Name 'ISC.AgentAdapter')) { Import-Module (Join-Path $PSScriptRoot 'ISC.AgentAdapter.psm1') -Force -WarningAction SilentlyContinue } } function Initialize-GoogleSourceSetup { param( [string]$ModuleRoot, [switch]$NonInteractive ) if (-not $ModuleRoot) { $ModuleRoot = $PSScriptRoot } Import-SourceSetupModule -ModuleRoot $ModuleRoot -Name 'ISC.OperatorConsole' -FileName 'ISC.OperatorConsole.psm1' Import-SourceSetupModule -ModuleRoot $ModuleRoot -Name 'ISC.OperatorToolchain' -FileName 'ISC.OperatorToolchain.psm1' Import-SourceSetupModule -ModuleRoot $ModuleRoot -Name 'ISC.GoogleWorkspaceConnector' -FileName 'ISC.GoogleWorkspaceConnector.psm1' Import-SourceSetupModule -ModuleRoot $ModuleRoot -Name 'ISC.GoogleCiemConnector' -FileName 'ISC.GoogleCiemConnector.psm1' Initialize-OperatorConsole -NonInteractive:$NonInteractive Initialize-GoogleWorkspaceConnectorData Initialize-GoogleCiemConnectorData } function Get-GoogleAgentCatalog { Initialize-GoogleSourceSetup $catalog = Get-GoogleWorkspaceCatalog return [ordered]@{ grantTypes = @('ServiceAccount', 'ClientCredentials') featurePacks = @($catalog.FeaturePacks.Keys) requiredConfig = @('projectId') optionalConfig = @('grantType', 'organizationId', 'impersonateUser', 'features', 'rotateKey', 'outputDirectory') secretFields = @('clientSecret', 'refreshToken', 'privateKey', 'privateKeyPassword') manualSteps = @('domainWideDelegation', 'oauthClientCreation') } } function Get-GoogleResolvedConfig { param([Parameter(Mandatory)]$Request) Import-AgentAdapterModule $config = Get-AgentRequestValue -Object $Request -Name 'config' -Default @{} $decisions = Get-AgentRequestValue -Object $Request -Name 'decisions' -Default @{} $secretRefs = Get-AgentRequestValue -Object $Request -Name 'secretRefs' -Default @{} $outputDir = if (Get-AgentRequestValue -Object $config -Name 'outputDirectory') { [string](Get-AgentRequestValue -Object $config -Name 'outputDirectory') } else { (Join-Path (Get-Location) (Join-Path 'wizard-output' 'google-workspace-isc')) } return [PSCustomObject]@{ GrantType = if (Get-AgentRequestValue -Object $config -Name 'grantType') { [string](Get-AgentRequestValue -Object $config -Name 'grantType') } else { 'ServiceAccount' } OrganizationId = $(if (Get-AgentRequestValue -Object $config -Name 'organizationId') { [string](Get-AgentRequestValue -Object $config -Name 'organizationId') } else { $null }) ProjectId = [string](Get-AgentRequestValue -Object $config -Name 'projectId') ServiceAccountId = if (Get-AgentRequestValue -Object $config -Name 'serviceAccountId') { [string](Get-AgentRequestValue -Object $config -Name 'serviceAccountId') } else { 'sailpoint-isc-gws' } DisplayName = if (Get-AgentRequestValue -Object $config -Name 'displayName') { [string](Get-AgentRequestValue -Object $config -Name 'displayName') } else { 'SailPoint ISC Google Workspace' } ImpersonateUser = $(if (Get-AgentRequestValue -Object $config -Name 'impersonateUser') { [string](Get-AgentRequestValue -Object $config -Name 'impersonateUser') } else { $null }) Feature = @($(Get-AgentRequestValue -Object $config -Name 'features' -Default @())) AggregationOnly = [bool](Get-AgentRequestValue -Object $config -Name 'aggregationOnly' -Default $false) RotateKey = $(if ($null -ne (Get-AgentRequestValue -Object $config -Name 'rotateKey')) { [bool](Get-AgentRequestValue -Object $config -Name 'rotateKey') } else { $true }) OutputDirectory = $outputDir ClientId = $(if (Get-AgentRequestValue -Object $config -Name 'clientId') { [string](Get-AgentRequestValue -Object $config -Name 'clientId') } else { $null }) ClientSecretRef = $(if (Get-AgentRequestValue -Object $secretRefs -Name 'clientSecret') { [string](Get-AgentRequestValue -Object $secretRefs -Name 'clientSecret') } else { $null }) RefreshTokenRef = $(if (Get-AgentRequestValue -Object $secretRefs -Name 'refreshToken') { [string](Get-AgentRequestValue -Object $secretRefs -Name 'refreshToken') } else { $null }) KeyPasswordRef = $(if (Get-AgentRequestValue -Object $secretRefs -Name 'keyPassword') { [string](Get-AgentRequestValue -Object $secretRefs -Name 'keyPassword') } else { $null }) SkipDomainWideDelegation = [bool](Get-AgentRequestValue -Object $config -Name 'skipDomainWideDelegationWalkthrough' -Default $false) UpdateExistingServiceAccount = $(if ($null -ne (Get-AgentRequestValue -Object $decisions -Name 'updateExistingServiceAccount')) { [bool](Get-AgentRequestValue -Object $decisions -Name 'updateExistingServiceAccount') } else { $null }) } } function New-GoogleAgentPlan { param([Parameter(Mandatory)]$Request) Initialize-GoogleSourceSetup $resolved = Get-GoogleResolvedConfig -Request $Request $needsInput = [System.Collections.Generic.List[object]]::new() $warnings = [System.Collections.Generic.List[string]]::new() $manualSteps = [System.Collections.Generic.List[string]]::new() if ([string]::IsNullOrWhiteSpace($resolved.ProjectId)) { $needsInput.Add([ordered]@{ field = 'projectId'; reason = 'GCP project ID is required.' }) } if ($resolved.GrantType -eq 'ServiceAccount' -and [string]::IsNullOrWhiteSpace($resolved.ImpersonateUser)) { $needsInput.Add([ordered]@{ field = 'impersonateUser'; reason = 'Service Account grant type requires impersonate user email.' }) } if ($resolved.GrantType -eq 'ClientCredentials') { if (-not $resolved.ClientId) { $needsInput.Add([ordered]@{ field = 'clientId'; reason = 'Client Credentials grant type requires OAuth client ID.' }) } if (-not $resolved.ClientSecretRef) { $needsInput.Add([ordered]@{ field = 'secretRefs.clientSecret'; reason = 'Provide client secret via env: or file: reference.' }) } } if ((Test-NeedsGcp -FeatureNames $resolved.Feature) -and [string]::IsNullOrWhiteSpace($resolved.OrganizationId)) { $needsInput.Add([ordered]@{ field = 'organizationId'; reason = 'GCP/CIEM feature packs require organization ID.' }) } $manualSteps.Add('Complete domain-wide delegation in Google Admin console when using Service Account grant type.') if ($resolved.GrantType -eq 'ClientCredentials' -and -not $resolved.RefreshTokenRef) { $manualSteps.Add('Run OAuth authorization flow or supply refresh token via secret reference.') } return [ordered]@{ status = if ($needsInput.Count -gt 0) { 'needsInput' } else { 'ready' } requestHash = (Get-AgentRequestHash -Request $Request) resolved = $resolved discoveries = [ordered]@{} mutations = @('connect-gcloud', 'enable-apis', 'configure-service-account-or-oauth') prerequisites = @('Google Cloud SDK (gcloud)', 'Organization/project permissions') warnings = @($warnings) manualSteps = @($manualSteps) needsInput = @($needsInput) } } function Invoke-GoogleAgentApply { param( [Parameter(Mandatory)]$Request, [Parameter(Mandatory)]$Plan, [switch]$WhatIf ) Initialize-GoogleSourceSetup $resolved = Get-GoogleResolvedConfig -Request $Request if ($Plan.resolved) { foreach ($prop in $Plan.resolved.PSObject.Properties) { $resolved.$($prop.Name) = $prop.Value } } if ($WhatIf) { return [ordered]@{ status = 'whatIf'; grantType = $resolved.GrantType; projectId = $resolved.ProjectId } } $run = New-AgentRunDirectory -ConnectorSlug 'google-workspace-isc' -RunId (Get-AgentRequestValue -Object $Request -Name 'runId') $account = Connect-GoogleCloud -ProjectId $resolved.ProjectId $apis = @(Get-SelectedApis -FeatureNames $resolved.Feature) if ($apis.Count -gt 0) { Invoke-GCloud -GcloudArgs @('services', 'enable') + $apis + @('--project', $resolved.ProjectId, '--quiet') } $fields = [ordered]@{} $secretArtifacts = @() if ($resolved.GrantType -eq 'ServiceAccount') { $email = Get-ServiceAccountEmail -ProjectId $resolved.ProjectId -ServiceAccountId $resolved.ServiceAccountId $existing = Get-ExistingServiceAccount -Email $email if (-not $existing) { Invoke-GCloud -GcloudArgs @( 'iam', 'service-accounts', 'create', $resolved.ServiceAccountId, '--project', $resolved.ProjectId, '--display-name', $resolved.DisplayName ) } Set-GoogleCreatedServiceAccount -Email $email if ($resolved.RotateKey) { $jsonPath = Join-Path $resolved.OutputDirectory 'sailpoint-gws-key.json' $pemPath = Join-Path $resolved.OutputDirectory 'sailpoint-gws-rsa.pem' if (-not (Test-Path -LiteralPath $resolved.OutputDirectory)) { New-Item -ItemType Directory -Path $resolved.OutputDirectory -Force | Out-Null } Invoke-GCloud -GcloudArgs @('iam', 'service-accounts', 'keys', 'create', '--iam-account', $email, '--key-file-type', 'json', '--key-file', $jsonPath, '--quiet') $keyPassword = if ($resolved.KeyPasswordRef) { Resolve-AgentSecretReference -Reference $resolved.KeyPasswordRef } else { New-KeyPassword } $null = ConvertTo-EncryptedRsaPem -JsonKeyPath $jsonPath -PemPath $pemPath -Passphrase $keyPassword Set-AgentRestrictedFile -Path (Join-Path $run.Path 'private-key-password.txt') -Content $keyPassword $secretArtifacts += @( [ordered]@{ label = 'JSON key'; path = $jsonPath } [ordered]@{ label = 'Private Key Password'; path = (Join-Path $run.Path 'private-key-password.txt') } ) $fields['Grant Type'] = 'Service Account' $fields['Service Account Email'] = $email $fields['Impersonate User'] = $resolved.ImpersonateUser $fields['Private Key'] = (Get-Content -LiteralPath $pemPath -Raw) $fields['Private Key Password'] = $keyPassword } else { $fields['Grant Type'] = 'Service Account' $fields['Service Account Email'] = $email $fields['Impersonate User'] = $resolved.ImpersonateUser } } else { $clientSecret = Resolve-AgentSecretReference -Reference $resolved.ClientSecretRef $refreshToken = if ($resolved.RefreshTokenRef) { Resolve-AgentSecretReference -Reference $resolved.RefreshTokenRef } else { Get-GoogleRefreshToken -OAuthClientId $resolved.ClientId -OAuthClientSecret $clientSecret -RedirectUri 'http://localhost:8088' } Set-AgentRestrictedFile -Path (Join-Path $run.Path 'client-secret.txt') -Content $clientSecret Set-AgentRestrictedFile -Path (Join-Path $run.Path 'refresh-token.txt') -Content $refreshToken $secretArtifacts += @( [ordered]@{ label = 'Client Secret'; path = (Join-Path $run.Path 'client-secret.txt') } [ordered]@{ label = 'Refresh Token'; path = (Join-Path $run.Path 'refresh-token.txt') } ) $fields['Grant Type'] = 'Client Credentials' $fields['Client ID'] = $resolved.ClientId $fields['Client Secret'] = $clientSecret $fields['Refresh Token'] = $refreshToken } $settingsPath = Join-Path $resolved.OutputDirectory 'sailpoint-gws-connection-settings.txt' if (-not (Test-Path -LiteralPath $resolved.OutputDirectory)) { New-Item -ItemType Directory -Path $resolved.OutputDirectory -Force | Out-Null } Write-ConnectionSettings -Fields $fields -Title 'ISC source Connection Settings' -Path $settingsPath $publicSettings = [ordered]@{} foreach ($entry in $fields.GetEnumerator()) { if (-not (Test-ConnectionSettingIsSecret -Name $entry.Key)) { $publicSettings[$entry.Key] = $entry.Value } } $manual = @('Paste Connection Settings into ISC.') if (-not $resolved.SkipDomainWideDelegation -and $resolved.GrantType -eq 'ServiceAccount') { $manual += 'Complete domain-wide delegation in Google Admin console.' } return [ordered]@{ status = 'ok' connectionSettings = $publicSettings artifacts = @([ordered]@{ label = 'Connection settings'; path = $settingsPath }) secretArtifacts = $secretArtifacts manualSteps = $manual verification = [ordered]@{ projectId = $resolved.ProjectId; serviceAccount = (Get-GoogleCreatedServiceAccount) } situation = $manual } } Export-ModuleMember -Function @( 'Initialize-GoogleSourceSetup' 'Get-GoogleAgentCatalog' 'Get-GoogleResolvedConfig' 'New-GoogleAgentPlan' 'Invoke-GoogleAgentApply' ) |