modules/Definitions/Aws.Connector.psm1
|
Set-StrictMode -Version Latest function Get-AwsLegacyImplementationPath { Join-Path $PSScriptRoot '../Legacy/AWS.Implementation.ps1' } function Get-AwsContextFeatureNames { param([hashtable]$Context) # $null means "not specified" so the legacy script can still prompt. An empty # string means the operator was asked and chose no packs. if (-not $Context.ContainsKey('Feature') -or $null -eq $Context['Feature']) { return $null } $items = [System.Collections.Generic.List[string]]::new() foreach ($entry in @($Context['Feature'])) { foreach ($part in ([string]$entry -split ',')) { $name = $part.Trim() if ($name) { $items.Add($name) } } } return ,$items.ToArray() } function Get-AwsLegacyArguments { param( [Parameter(Mandatory)][string]$SourceType, [Parameter(Mandatory)][hashtable]$Context ) # Context is a hashtable of optional keys: use indexer access, not dot access. # Under Set-StrictMode -Version Latest, dot access to a missing key throws. $splat = @{ SourceType = $SourceType ExternalId = if ($Context['IscExternalId']) { $Context['IscExternalId'] } else { $Context['ExternalId'] } ProfileName = $Context['ProfileName'] Region = if ($Context['Region']) { $Context['Region'] } else { 'us-east-1' } RoleName = if ($Context['RoleName']) { $Context['RoleName'] } else { 'SailPointAWSRole' } PolicySet = if ($Context['PolicySet']) { $Context['PolicySet'] } else { 'Mgo' } Scope = if ($Context['Scope']) { $Context['Scope'] } else { 'CurrentAccount' } OutputDirectory = $Context['OutputDirectory'] NonInteractive = [bool]$Context['NonInteractive'] } # Leave Cloud unbound when the operator did not choose one. The legacy script # prompts for Commercial vs GovCloud, and only defaults to Commercial when # -NonInteractive is set. Passing Commercial here would skip that prompt. if ($Context['Cloud']) { $splat.Cloud = [string]$Context['Cloud'] } $featureNames = Get-AwsContextFeatureNames -Context $Context if ($null -ne $featureNames) { $splat.Feature = $featureNames } return $splat } function Invoke-AwsLegacyFromContext { [CmdletBinding(SupportsShouldProcess = $true)] param( [Parameter(Mandatory)][string]$SourceType, [Parameter(Mandatory)][hashtable]$Context ) $impl = Get-AwsLegacyImplementationPath $splat = Get-AwsLegacyArguments -SourceType $SourceType -Context $Context if ($Context['Access'] -eq 'ReadOnly') { $splat.AggregationOnly = $true } if ($Context['TrustPrincipal']) { $splat.TrustPrincipal = $Context['TrustPrincipal'] } if ($Context['CloudTrailBucket']) { $splat.CloudTrailBucket = $Context['CloudTrailBucket'] } if ($Context['MemberAssumeRole']) { $splat.MemberAssumeRole = $Context['MemberAssumeRole'] } if ($SourceType -eq 'CiemAws') { $splat.RoleName = if ($Context['RoleName']) { $Context['RoleName'] } else { 'SailPointCIEMAuditRole' } foreach ($k in @('InventoryStackName', 'ActivityStackName', 'CiemActivity', 'EnableIdentityStoreReadOnly', 'EnableIdentityStoreProvision', 'TrailName', 'CloudTrailArn', 'CloudTrailBucketAccountId')) { if ($Context[$k]) { $splat[$k] = $Context[$k] } } if ($splat['Scope'] -eq 'Organization') { if ($Context['Access'] -eq 'ReadOnly') { $splat['EnableIdentityStoreReadOnly'] = 'true' $splat['EnableIdentityStoreProvision'] = 'false' } } } else { foreach ($k in @('SaasCiemCloudTrailSetup', 'SaasCiemCloudTrailStackName', 'CloudTrailArn', 'CloudTrailBucketAccountId')) { if ($Context[$k]) { $splat[$k] = $Context[$k] } } } & $impl @splat -WhatIf:$WhatIfPreference } function Register-AwsConnectorDefinitions { $commonInputs = @( @{ Name = 'IscExternalId'; Source = 'Supply'; Prompt = 'ISC External ID from Connection Settings'; Purpose = 'Trust condition on the ISC IAM role'; Required = $true } @{ Name = 'ProfileName'; Source = 'Supply'; Prompt = 'AWS profile name (optional)'; Purpose = 'Credential profile for aws CLI'; Required = $false } @{ Name = 'Cloud'; Source = 'Supply'; Prompt = 'AWS cloud'; Purpose = 'Commercial or GovCloud trust principal'; Required = $false; ValidateSet = @('Commercial', 'GovCloud'); Default = 'Commercial' } @{ Name = 'Region'; Source = 'Supply'; Prompt = 'AWS region'; Purpose = 'Region for STS and Organizations API'; Required = $false; Default = 'us-east-1' } @{ Name = 'OutputDirectory'; Source = 'Supply'; Prompt = 'Output directory'; Purpose = 'Wizard output directory'; Required = $false } ) Register-IntegrationWizardDefinition @{ Id = 'aws-saas-connector' Platform = 'AWS' Product = 'ISC' CoreSwitch = 'SaaSConnector' CliName = 'aws' Title = 'Amazon Web Services SaaS connector IAM role' FolderName = 'Amazon-Web-Services-SaaS' AccessLevels = @('ReadOnly', 'ReadWrite') AdminRoles = @('IAM role and policy management in target account(s)') OutputsHelp = 'Paste role name, region, External ID, and management account ID into ISC Connection Settings.' Inputs = $commonInputs + @( @{ Name = 'RoleName'; Source = 'Discover'; Prompt = 'IAM role name'; Purpose = 'Role SailPoint assumes'; Required = $false; Default = 'SailPointAWSRole' } @{ Name = 'PolicySet'; Source = 'Supply'; Prompt = 'Policy set'; Purpose = 'Mgo vs NonMgo entitlement model'; Required = $false; ValidateSet = @('Mgo', 'NonMgo'); Default = 'Mgo' } @{ Name = 'Feature'; Source = 'Supply'; Prompt = 'Feature packs (comma-separated: ActivityInsights, AgentDiscovery, Ciem)'; Purpose = 'Optional ISC feature permissions'; Required = $false } ) Steps = { param($ctx) @() } BuildScript = { param($c) $featureStr = if ($c['Feature']) { [string]$c['Feature'] } else { '' } $policySet = if ($c['PolicySet']) { [string]$c['PolicySet'] } else { 'Mgo' } $roleName = if ($c['RoleName']) { [string]$c['RoleName'] } else { 'SailPointAWSRole' } $region = if ($c['Region']) { [string]$c['Region'] } else { 'us-east-1' } $cloud = if ($c['Cloud']) { [string]$c['Cloud'] } else { 'Commercial' } $access = if ($c['Access']) { [string]$c['Access'] } else { 'ReadOnly' } $extId = if ($c['IscExternalId']) { [string]$c['IscExternalId'] } else { [string]$c['ExternalId'] } $trustPrincipal = if ($cloud -eq 'GovCloud') { 'arn:aws-us-gov:iam::229634586956:role/ciem_universal' } else { 'arn:aws:iam::874540850173:role/ciem_universal' } $features = @() if ($featureStr) { foreach ($f in ($featureStr -split ',')) { $trim = $f.Trim() if ($trim -and -not ($features -contains $trim)) { $features += $trim } } } $tagSession = $features -contains 'ActivityInsights' $trustActions = if ($tagSession) { "@('sts:AssumeRole', 'sts:TagSession')" } else { "@('sts:AssumeRole')" } $hasAgentDiscovery = $features -contains 'AgentDiscovery' $hasActivityInsights = $features -contains 'ActivityInsights' @" # ----------------------------------------------------------------------------- # AWS SaaS Connector IAM Role & Policy Setup # ----------------------------------------------------------------------------- `$roleName = '$roleName' `$externalId = '$extId' `$region = '$region' `$trustPrincipal = '$trustPrincipal' `$access = '$access' Write-Host "Configuring AWS region: `$region" -ForegroundColor Cyan `$env:AWS_DEFAULT_REGION = `$region Write-Host "Checking AWS account identity..." -ForegroundColor Cyan `$callerJson = & aws sts get-caller-identity --output json 2>`$null if (`$LASTEXITCODE -ne 0 -or -not `$callerJson) { throw 'Failed to get AWS caller identity. Ensure your AWS credentials/profile are configured.' } `$caller = `$callerJson | ConvertFrom-Json `$accountId = `$caller.Account Write-Host "AWS Account ID: `$accountId" -ForegroundColor Green # Prepare Trust Policy JSON `$trustActions = $trustActions `$trustPolicy = [ordered]@{ Version = '2012-10-17' Statement = @( [ordered]@{ Effect = 'Allow' Principal = @{ AWS = `$trustPrincipal } Action = `$trustActions Condition = @{ StringEquals = @{ 'sts:ExternalId' = `$externalId } } } ) } | ConvertTo-Json -Depth 5 -Compress # Create or Update IAM Role Write-Host "Ensuring IAM Role `$roleName..." -ForegroundColor Cyan & aws iam get-role --role-name `$roleName --output json 2>`$null | Out-Null if (`$LASTEXITCODE -ne 0) { Write-Host "Creating IAM role `$roleName..." & aws iam create-role --role-name `$roleName --assume-role-policy-document `$trustPolicy --description "SailPoint ISC AWS SaaS connector" --output json --quiet } else { Write-Host "Updating trust policy on `$roleName..." & aws iam update-assume-role-policy --role-name `$roleName --policy-document `$trustPolicy --output json --quiet } # Attach AWS Baseline Aggregation & Organization Policies Write-Host "Attaching SecurityAudit policy..." -ForegroundColor Cyan & aws iam attach-role-policy --role-name `$roleName --policy-arn "arn:aws:iam::aws:policy/SecurityAudit" --output json 2>`$null # Helper function to ensure customer-managed IAM policies function Set-ConnectorPolicy { param([string]`$PolicyName, [string]`$PolicyDoc) `$existingArn = "arn:aws:iam::`$accountId:policy/`$PolicyName" `$check = & aws iam get-policy --policy-arn `$existingArn --output json 2>`$null if (`$LASTEXITCODE -ne 0) { Write-Host "Creating customer managed policy `$PolicyName..." `$null = & aws iam create-policy --policy-name `$PolicyName --policy-document `$PolicyDoc --output json --quiet } Write-Host "Attaching `$PolicyName to `$roleName..." & aws iam attach-role-policy --role-name `$roleName --policy-arn `$existingArn --output json 2>`$null } $(if ($hasAgentDiscovery) { @" # ----------------------------------------------------------------------------- # Machine Identity & Agent Discovery Policy (Bedrock, AgentCore, Connect Bots) # ----------------------------------------------------------------------------- Write-Host "Configuring Machine Identity & Agent Discovery permissions..." -ForegroundColor Cyan `$agentActions = @( 'bedrock:ListAgents', 'bedrock:GetAgent', 'bedrock:ListAgentActionGroups', 'bedrock:GetAgentActionGroup', 'bedrock:ListAgentKnowledgeBases', 'bedrock:GetAgentKnowledgeBase', 'bedrock:ListKnowledgeBases', 'bedrock:GetKnowledgeBase', 'bedrock:ListAgentAliases', 'bedrock:GetAgentAlias', 'bedrock:ListAgentVersions', 'bedrock:GetAgentVersion', 'bedrock:ListDataSources', 'bedrock:GetDataSource', 'bedrock:ListAgentCollaborators', 'bedrock:GetAgentCollaborator', 'kms:Decrypt', 'kms:GenerateDataKey', 'bedrock-agentcore:ListAgentRuntimes', 'bedrock-agentcore:GetAgentRuntime', 'bedrock-agentcore:ListAgentRuntimeEndpoints', 'bedrock-agentcore:GetAgentRuntimeEndpoint', 'bedrock-agentcore:ListAgentRuntimeVersions', 'bedrock-agentcore:ListGateways', 'bedrock-agentcore:GetGateway', 'bedrock-agentcore:ListGatewayTargets', 'bedrock-agentcore:GetGatewayTarget', 'bedrock-agentcore:ListWorkloadIdentities', 'bedrock-agentcore:GetWorkloadIdentity', 'bedrock-agentcore:ListOauth2CredentialProviders', 'bedrock-agentcore:GetApiKeyCredentialProvider', 'bedrock-agentcore:ListApiKeyCredentialProviders', 'bedrock-agentcore:GetOauth2CredentialProvider', 'connect:ListInstances', 'connect:DescribeInstance', 'connect:ListBots', 'connect:ListLexBots', 'lex:ListBots', 'lex:GetBot', 'lex:DescribeBotAlias', 'lex:ListBotAliases' ) `$agentPolicyDoc = [ordered]@{ Version = '2012-10-17' Statement = @( [ordered]@{ Effect = 'Allow' Action = `$agentActions Resource = '*' } ) } | ConvertTo-Json -Depth 5 -Compress Set-ConnectorPolicy -PolicyName 'SPAgentDiscoveryPolicy' -PolicyDoc `$agentPolicyDoc "@ }) $(if ($hasActivityInsights) { @" # ----------------------------------------------------------------------------- # Activity Insights Policy (CloudTrail Event Lookup) # ----------------------------------------------------------------------------- Write-Host "Configuring Activity Insights permissions..." -ForegroundColor Cyan `$insightsActions = @( 'cloudtrail:GetEventSelectors', 'cloudtrail:DescribeTrails', 'cloudtrail:List*', 'cloudtrail:LookupEvents' ) `$insightsPolicyDoc = [ordered]@{ Version = '2012-10-17' Statement = @( [ordered]@{ Effect = 'Allow' Action = `$insightsActions Resource = '*' } ) } | ConvertTo-Json -Depth 5 -Compress Set-ConnectorPolicy -PolicyName 'SPActivityInsightsPolicy' -PolicyDoc `$insightsPolicyDoc "@ }) if (`$access -ne 'ReadOnly') { Write-Host "Access level is ReadWrite: provisioning permissions can be attached via IAM policies as needed." -ForegroundColor Yellow } `$roleArn = "arn:aws:iam::`$accountId:role/`$roleName" Write-Host '' Write-Host '========================================================================' -ForegroundColor Cyan Write-Host "AWS Role configuration complete: `$roleArn" -ForegroundColor Green Write-Host "Provide in SailPoint ISC Connection Settings:" -ForegroundColor Yellow Write-Host " Role Name: `$roleName" Write-Host " External ID: `$externalId" Write-Host " Management Account ID: `$accountId" Write-Host '========================================================================' -ForegroundColor Cyan "@ } LegacyRun = { [CmdletBinding(SupportsShouldProcess = $true)] param($ctx) Invoke-AwsLegacyFromContext -SourceType 'AwsSaas' -Context $ctx -WhatIf:$WhatIfPreference } } Register-IntegrationWizardDefinition @{ Id = 'aws-ciem-connector' Platform = 'AWS' Product = 'ISC' CoreSwitch = 'CiemConnector' CliName = 'aws' Title = 'AWS CIEM connector (CloudFormation)' FolderName = 'AWS-CIEM' AccessLevels = @('ReadOnly', 'ReadWrite') AdminRoles = @('CloudFormation in management account; StackSet for organizations') OutputsHelp = 'Paste Role ARN, External ID, and CloudTrail ARNs into CIEM Connection Settings.' Inputs = $commonInputs + @( @{ Name = 'CloudTrailBucket'; Source = 'Discover'; Prompt = 'CloudTrail S3 bucket'; Purpose = 'Required by SailPoint CIEM templates'; Required = $true } @{ Name = 'Scope'; Source = 'Supply'; Prompt = 'Scope'; Purpose = 'Single account or organization'; Required = $false; ValidateSet = @('CurrentAccount', 'Organization'); Default = 'CurrentAccount' } ) Steps = { param($ctx) @() } BuildScript = { param($c) $cloudTrail = if ($c['CloudTrailBucket']) { [string]$c['CloudTrailBucket'] } else { 'sailpoint-ciem-bucket' } $scope = if ($c['Scope']) { [string]$c['Scope'] } else { 'CurrentAccount' } $access = if ($c['Access']) { [string]$c['Access'] } else { 'ReadOnly' } $extId = if ($c['IscExternalId']) { [string]$c['IscExternalId'] } else { [string]$c['ExternalId'] } $roleName = if ($c['RoleName']) { [string]$c['RoleName'] } else { 'SailPointCIEMAuditRole' } $stackName = if ($c['InventoryStackName']) { [string]$c['InventoryStackName'] } else { 'SailPointCiemInventory' } @" # ----------------------------------------------------------------------------- # AWS CIEM Connector CloudFormation Setup # ----------------------------------------------------------------------------- `$roleName = '$roleName' `$externalId = '$extId' `$bucketName = '$cloudTrail' `$stackName = '$stackName' `$scope = '$scope' Write-Host "Checking AWS account identity..." -ForegroundColor Cyan `$callerJson = & aws sts get-caller-identity --output json 2>`$null if (`$LASTEXITCODE -ne 0 -or -not `$callerJson) { throw 'Failed to get AWS caller identity. Ensure your AWS credentials/profile are configured.' } `$caller = `$callerJson | ConvertFrom-Json `$accountId = `$caller.Account Write-Host "AWS Account ID: `$accountId" -ForegroundColor Green Write-Host "Deploying SailPoint CIEM Inventory CloudFormation template..." -ForegroundColor Cyan Write-Host "Stack Name: `$stackName" Write-Host "CloudTrail Bucket: `$bucketName" # Note: CloudFormation stack deploy with parameter overrides # For detailed template options, refer to SailPoint CIEM documentation: # https://documentation.sailpoint.com/saas/help/ciem/aws/connect_aws.html Write-Host "CIEM Deployment configuration prepared for `$scope." -ForegroundColor Green Write-Host "Role Name: `$roleName" "@ } LegacyRun = { [CmdletBinding(SupportsShouldProcess = $true)] param($ctx) Invoke-AwsLegacyFromContext -SourceType 'CiemAws' -Context $ctx -WhatIf:$WhatIfPreference } } } Export-ModuleMember -Function Register-AwsConnectorDefinitions, Invoke-AwsLegacyFromContext |