Public/Connect-CIEM.ps1
|
function Connect-CIEM { <# .SYNOPSIS Establishes authentication to all configured cloud providers for CIEM scans. .DESCRIPTION Reads authentication configuration from config.json and connects to each enabled cloud provider. This function must be called once before running any CIEM scans. The connection is cached for the duration of the PowerShell session. Currently supports: - Azure (ServicePrincipalSecret, ServicePrincipalCertificate, ManagedIdentity, Interactive) - AWS (coming soon) .PARAMETER Provider Optional. Connect only to specific provider(s). If not specified, connects to the provider defined in config.json's cloudProvider setting. .PARAMETER Force Force re-authentication even if already connected. .OUTPUTS [PSCustomObject] Connection summary showing status for each provider. .EXAMPLE Connect-CIEM # Connects to the default provider from config.json .EXAMPLE Connect-CIEM -Provider Azure # Connects only to Azure .EXAMPLE Connect-CIEM -Force # Forces re-authentication even if already connected .NOTES This function must be called before running Invoke-CIEMScan or any other scan functions. Use Test-CIEMAuthenticated to check connection status. #> [CmdletBinding()] [OutputType([PSCustomObject])] param( [Parameter()] [ValidateSet('Azure', 'AWS')] [string[]]$Provider, [Parameter()] [switch]$Force ) $ErrorActionPreference = 'Stop' # Initialize auth context storage if not exists if (-not $script:AuthContext) { $script:AuthContext = @{} } # Determine which providers to connect if (-not $Provider) { $Provider = @($script:Config.cloudProvider) } $results = @() foreach ($p in $Provider) { Write-Verbose "Connecting to provider: $p" # Skip if already connected and not forcing if (-not $Force -and $script:AuthContext[$p]) { Write-Verbose "$p is already connected. Use -Force to re-authenticate." $results += [PSCustomObject]@{ Provider = $p Status = 'AlreadyConnected' Account = $script:AuthContext[$p].AccountId TenantId = $script:AuthContext[$p].TenantId Message = 'Already authenticated. Use -Force to re-authenticate.' } continue } try { switch ($p) { 'Azure' { $authContext = Connect-CIEMAzure $script:AuthContext['Azure'] = $authContext $results += [PSCustomObject]@{ Provider = 'Azure' Status = 'Connected' Account = $authContext.AccountId TenantId = $authContext.TenantId Subscriptions = $authContext.SubscriptionIds.Count Message = "Connected as $($authContext.AccountType)" } } 'AWS' { # AWS support coming soon $results += [PSCustomObject]@{ Provider = 'AWS' Status = 'NotSupported' Account = $null TenantId = $null Message = 'AWS provider support coming soon' } } } } catch { $script:AuthContext[$p] = $null $results += [PSCustomObject]@{ Provider = $p Status = 'Failed' Account = $null TenantId = $null Message = $_.Exception.Message } Write-Error "Failed to connect to $p : $_" } } # Display summary Write-Host "`nCIEM Connection Summary:" -ForegroundColor Cyan foreach ($r in $results) { $color = switch ($r.Status) { 'Connected' { 'Green' } 'AlreadyConnected' { 'Yellow' } 'Failed' { 'Red' } default { 'Gray' } } Write-Host " $($r.Provider): " -NoNewline Write-Host $r.Status -ForegroundColor $color -NoNewline if ($r.Account) { Write-Host " ($($r.Account))" -NoNewline } Write-Host "" } [PSCustomObject]@{ Providers = $results Timestamp = Get-Date } } function Connect-CIEMAzure { <# .SYNOPSIS Internal function to establish Azure authentication. #> [CmdletBinding()] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingConvertToSecureStringWithPlainText', '', Justification = 'Secret is already in memory from config file; conversion to SecureString is required for Connect-AzAccount')] [OutputType([PSCustomObject])] param() $ErrorActionPreference = 'Stop' $authConfig = $script:Config.azure.authentication $authMethod = $authConfig.method Write-Verbose "Azure authentication method: $authMethod" switch ($authMethod) { 'ServicePrincipalSecret' { $spConfig = $authConfig.servicePrincipal if (-not $spConfig.clientId -or -not $spConfig.clientSecret -or -not $authConfig.tenantId) { throw "Authentication method is 'ServicePrincipalSecret' but tenantId, clientId, or clientSecret not set in config.json" } $secureSecret = ConvertTo-SecureString $spConfig.clientSecret -AsPlainText -Force $credential = New-Object System.Management.Automation.PSCredential($spConfig.clientId, $secureSecret) Write-Verbose "Connecting as service principal: $($spConfig.clientId)" Connect-AzAccount -ServicePrincipal -Credential $credential -TenantId $authConfig.tenantId -ErrorAction Stop | Out-Null } 'ServicePrincipalCertificate' { $certConfig = $authConfig.certificate if (-not $certConfig.clientId -or -not $authConfig.tenantId) { throw "Authentication method is 'ServicePrincipalCertificate' but tenantId or clientId not set in config.json" } $connectParams = @{ ServicePrincipal = $true ApplicationId = $certConfig.clientId TenantId = $authConfig.tenantId } if ($certConfig.thumbprint) { $connectParams.CertificateThumbprint = $certConfig.thumbprint } elseif ($certConfig.path) { $connectParams.CertificatePath = $certConfig.path if ($certConfig.password) { $connectParams.CertificatePassword = ConvertTo-SecureString $certConfig.password -AsPlainText -Force } } else { throw "Certificate authentication requires either thumbprint or path in config.json" } Write-Verbose "Connecting with certificate for: $($certConfig.clientId)" Connect-AzAccount @connectParams -ErrorAction Stop | Out-Null } 'ManagedIdentity' { $miConfig = $authConfig.managedIdentity $connectParams = @{ Identity = $true } if ($miConfig.clientId) { $connectParams.AccountId = $miConfig.clientId Write-Verbose "Connecting with user-assigned managed identity: $($miConfig.clientId)" } else { Write-Verbose "Connecting with system-assigned managed identity" } Connect-AzAccount @connectParams -ErrorAction Stop | Out-Null } 'DeviceCode' { $connectParams = @{ UseDeviceAuthentication = $true } if ($authConfig.tenantId) { $connectParams.TenantId = $authConfig.tenantId } Write-Verbose "Connecting with device code authentication" Connect-AzAccount @connectParams -ErrorAction Stop | Out-Null } 'Interactive' { $connectParams = @{} if ($authConfig.tenantId) { $connectParams.TenantId = $authConfig.tenantId } Write-Verbose "Connecting with interactive authentication" Connect-AzAccount @connectParams -ErrorAction Stop | Out-Null } default { throw "Unknown authentication method '$authMethod'. Valid values: ServicePrincipalSecret, ServicePrincipalCertificate, ManagedIdentity, DeviceCode, Interactive" } } $context = Get-AzContext -ErrorAction Stop # Get all accessible subscriptions $subscriptions = Get-AzSubscription -TenantId $context.Tenant.Id -ErrorAction SilentlyContinue # Filter to configured subscriptions if specified $subscriptionFilter = $script:Config.azure.subscriptionFilter if ($subscriptionFilter -and $subscriptionFilter.Count -gt 0) { $subscriptions = $subscriptions | Where-Object { $subscriptionFilter -contains $_.Id } } $subscriptionIds = @($subscriptions | Select-Object -ExpandProperty Id) if ($subscriptionIds.Count -eq 0) { Write-Warning "No accessible subscriptions found in tenant $($context.Tenant.Id)" } else { Write-Verbose "Found $($subscriptionIds.Count) accessible subscription(s)" } # Determine account type $accountType = switch ($context.Account.Type) { 'User' { 'User' } 'ServicePrincipal' { 'ServicePrincipal' } 'ManagedService' { 'ManagedIdentity' } default { $context.Account.Type } } [PSCustomObject]@{ TenantId = $context.Tenant.Id SubscriptionIds = $subscriptionIds AccountId = $context.Account.Id AccountType = $accountType ConnectedAt = Get-Date } } |