modules/Definitions/Aws.Entro.psm1
|
Set-StrictMode -Version Latest function Get-EntroAwsTemplatePath { param( [ValidateSet('Default', 'Europe')] [string]$Region = 'Default' ) Import-Module (Join-Path (Split-Path $PSScriptRoot -Parent) 'IW.EntroCatalog.psm1') -Force -WarningAction SilentlyContinue $base = Join-Path (Get-EntroConnectSkillRoot) 'integrations/amazon-web-services' if ($Region -eq 'Europe') { return Join-Path $base 'Entro Security AWS Template - Europe region.json' } Join-Path $base 'Entro Security AWS Template.json' } function Get-EntroAwsCloudFormationStackName { param([Parameter(Mandatory)][string]$ExternalId) $safe = ($ExternalId -replace '[^A-Za-z0-9-]', '-').Trim('-') if (-not $safe) { $safe = 'account' } $name = "Entro-$safe" if ($name.Length -gt 128) { $name = $name.Substring(0, 128).TrimEnd('-') } return $name } function Get-EntroAwsCloudFormationProbeResult { param([string]$StackStatus) $status = if ($StackStatus) { $StackStatus.Trim() } else { '' } if (-not $status -or $status -eq 'None') { return 'Create' } if ($status -in @('CREATE_COMPLETE', 'UPDATE_COMPLETE')) { return 'Skip' } if ($status -like '*IN_PROGRESS') { return 'Skip' } return 'Update' } function Get-EntroAwsWizardWorkDirectory { param([hashtable]$Context) $base = if ($Context['OutputDirectory']) { $Context['OutputDirectory'] } else { Get-Location } Join-Path $base 'entro-aws' } function Get-EntroAwsRoleArn { param([hashtable]$Context) if ($Context['EntroRoleArn']) { return [string]$Context['EntroRoleArn'] } $rn = if ($Context['EntroRoleName']) { $Context['EntroRoleName'] } else { 'EntroReadOnly' } $arn = aws iam get-role --role-name $rn --query Role.Arn --output text 2>$null if ($LASTEXITCODE -ne 0 -or [string]::IsNullOrWhiteSpace([string]$arn)) { return $null } $Context['EntroRoleArn'] = ([string]$arn).Trim() return $Context['EntroRoleArn'] } function Get-EntroAwsHandoffStep { param([hashtable]$Context) $method = if ($Context['Method']) { $Context['Method'] } else { 'AssumeRole' } $rn = if ($Context['EntroRoleName']) { $Context['EntroRoleName'] } else { 'EntroReadOnly' } $manual = switch ($method) { 'CloudFormation' { 'The wizard deployed the Entro CloudFormation stack, and the stack notifies Entro. In Entro, confirm the AWS account appears under Accounts.' } 'Terraform' { 'Apply Terraform-Entro.tf from the Entro AWS integration folder, then add the account in Entro (Add account, AWS) with the Role ARN it outputs.' } default { "In Entro, Add account, AWS, Assume Role: paste the Role ARN of $rn (printed at the end of this run) and an Environment name." } } @{ Id = 'entro-handoff' Title = 'Register in Entro UI' Kind = 'Manual' Manual = $manual } } function Register-AwsEntroDefinitions { Register-IntegrationWizardDefinition @{ Id = 'aws-entro' Platform = 'AWS' Product = 'Entro' CoreSwitch = 'Entro' CliName = 'aws' Title = 'Entro AWS integration' FolderName = 'Entro-AWS' AccessLevels = @() AdminRoles = @('IAM policy and role management') OutputsHelp = 'Paste Environment label and AWS Role ARN into the Entro admin console (Add account → AWS).' Inputs = @( @{ Name = 'EntroExternalId'; Source = 'Supply'; Prompt = 'Entro External ID from the wizard'; Purpose = 'IAM trust ExternalId for Entro'; Required = $true } @{ Name = 'Method'; Source = 'Supply'; Prompt = 'Integration method'; Purpose = 'How Entro connects'; Required = $true; ValidateSet = @('AssumeRole', 'CloudFormation', 'Terraform'); Default = 'AssumeRole' } @{ Name = 'EntroTemplateRegion'; Source = 'Supply'; Prompt = 'Entro template region'; Purpose = 'Default vs Europe SNS region'; Required = $false; ValidateSet = @('Default', 'Europe'); Default = 'Default' } @{ Name = 'EntroRoleName'; Source = 'Supply'; Prompt = 'Entro IAM role name'; Purpose = 'Entro read role'; Required = $false; Default = 'EntroReadOnly' } @{ Name = 'OutputDirectory'; Source = 'Supply'; Prompt = 'Output directory'; Purpose = 'Wizard output directory'; Required = $false } ) Steps = { param($ctx) @( @{ Id = 'entro-trust' Title = 'Create Entro trust role and read policy' Kind = 'Ensure' Probe = { param($c) $method = if ($c['Method']) { $c['Method'] } else { 'AssumeRole' } if ($method -eq 'Terraform') { return 'Skip' } if ($method -eq 'CloudFormation') { if ([string]::IsNullOrWhiteSpace([string]$c['EntroExternalId'])) { return 'Create' } $stack = Get-EntroAwsCloudFormationStackName -ExternalId ([string]$c['EntroExternalId']) $region = if ($c['Region']) { $c['Region'] } else { 'us-east-1' } $status = aws cloudformation describe-stacks --stack-name $stack --region $region --query 'Stacks[0].StackStatus' --output text 2>$null if ($LASTEXITCODE -ne 0) { return 'Create' } return Get-EntroAwsCloudFormationProbeResult -StackStatus ([string]$status) } $rn = if ($c['EntroRoleName']) { $c['EntroRoleName'] } else { 'EntroReadOnly' } $exists = aws iam get-role --role-name $rn 2>$null if ($LASTEXITCODE -eq 0) { return 'Skip' } 'Create' } Run = { param($c) $method = if ($c['Method']) { $c['Method'] } else { 'AssumeRole' } if ($method -eq 'Terraform') { Write-Info 'Terraform method: use Entro Terraform-Entro.tf in the Entro AWS integration folder.' return } $modRoot = Join-Path (Split-Path (Split-Path $PSScriptRoot -Parent) -Parent) 'modules' Import-Module (Join-Path $modRoot 'ISC.EntroAws.psm1') -Force $tplRegion = if ($c['EntroTemplateRegion']) { $c['EntroTemplateRegion'] } else { 'Default' } $tpl = Get-EntroAwsTemplatePath -Region $tplRegion $work = Get-EntroAwsWizardWorkDirectory -Context $c $rn = if ($c['EntroRoleName']) { $c['EntroRoleName'] } else { 'EntroReadOnly' } if ($method -eq 'CloudFormation') { $stack = Get-EntroAwsCloudFormationStackName -ExternalId ([string]$c['EntroExternalId']) $region = if ($c['Region']) { $c['Region'] } else { 'us-east-1' } Invoke-EntroAwsCloudFormationDeploy -TemplatePath $tpl -StackName $stack ` -ExternalId $c['EntroExternalId'] -Region $region | Out-Null } else { $c['EntroRoleArn'] = Invoke-EntroAwsAssumeRoleSetup -EntroExternalId $c['EntroExternalId'] -RoleName $rn ` -TemplatePath $tpl -WorkDirectory $work } } BuildScript = { param($c) $method = if ($c['Method']) { $c['Method'] } else { 'AssumeRole' } if ($method -eq 'Terraform') { return '# Use Entro Terraform-Entro.tf from the Entro AWS integration catalog.' } $rn = if ($c['EntroRoleName']) { $c['EntroRoleName'] } else { 'EntroReadOnly' } $extId = [string]$c['EntroExternalId'] @" # ----------------------------------------------------------------------------- # Entro AWS Integration Setup ($method) # ----------------------------------------------------------------------------- `$roleName = '$rn' `$externalId = '$extId' `$entroAccountId = '937217723901' 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 Entro Trust Policy JSON `$trustRoleArn = "arn:aws:iam::`$(`$entroAccountId):role/EntroTrustRole-`$externalId" `$userArn = "arn:aws:iam::`$(`$entroAccountId):user/liminal-saas-assume-role" `$trustPolicy = [ordered]@{ Version = '2012-10-17' Statement = @( [ordered]@{ Effect = 'Allow' Principal = @{ AWS = `$userArn } Action = 'sts:AssumeRole' Condition = @{ StringEquals = @{ 'sts:ExternalId' = `$externalId } } }, [ordered]@{ Effect = 'Allow' Principal = @{ AWS = `$trustRoleArn } Action = 'sts:AssumeRole' Condition = @{ StringEquals = @{ 'sts:ExternalId' = `$externalId } } } ) } | ConvertTo-Json -Depth 5 -Compress # Create or Update IAM Role Write-Host "Ensuring IAM Role `$roleName for Entro..." -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 "Entro Security AWS Integration Role" --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 SecurityAudit policy 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 `$roleArn = "arn:aws:iam::`$accountId:role/`$roleName" Write-Host '' Write-Host '========================================================================' -ForegroundColor Cyan Write-Host "Entro AWS Role configuration complete: `$roleArn" -ForegroundColor Green Write-Host "Register in Entro UI (Add account -> AWS -> Assume Role):" -ForegroundColor Yellow Write-Host " Role ARN: `$roleArn" Write-Host " External ID: `$externalId" Write-Host " Environment: (your label)" Write-Host '========================================================================' -ForegroundColor Cyan "@ } } (Get-EntroAwsHandoffStep -Context $ctx) ) } OnRunComplete = { param($ctx) $method = if ($ctx['Method']) { $ctx['Method'] } else { 'AssumeRole' } Write-Host '' Write-Host 'Entro handoff' -ForegroundColor Cyan Write-Host ' Environment: (your label)' Write-Host " External ID: $($ctx['EntroExternalId'])" if ($method -eq 'CloudFormation') { $tplRegion = if ($ctx['EntroTemplateRegion']) { $ctx['EntroTemplateRegion'] } else { 'Default' } Write-Host " Template: $(Get-EntroAwsTemplatePath -Region $tplRegion)" } elseif ($method -eq 'AssumeRole') { $arn = Get-EntroAwsRoleArn -Context $ctx Write-Host " Role ARN: $(if ($arn) { $arn } else { '(not found; run aws iam get-role)' })" } Write-Host '' } } } Export-ModuleMember -Function Register-AwsEntroDefinitions, Get-EntroAwsTemplatePath, Get-EntroAwsCloudFormationStackName, Get-EntroAwsCloudFormationProbeResult |