modules/IW.Access.psm1
|
#Requires -Version 7 Set-StrictMode -Version Latest function Get-RequirementAccessClass { param( [Parameter(Mandatory)] [string]$Kind, [Parameter(Mandatory)] [string]$Name ) switch ($Kind) { 'MicrosoftGraphPermission' { return Get-GraphPermissionAccessClass -Permission $Name } 'GoogleOAuthScope' { return Get-GoogleOAuthScopeAccessClass -Scope $Name } 'GoogleIamPermission' { return Get-GoogleIamPermissionAccessClass -Permission $Name } 'AwsAction' { return Get-AwsActionAccessClass -Action $Name } 'AwsIamRole' { return 'ReadOnly' } 'EntroGcpCustomPermission' { if ($Name -match '^logging\.queries\.(create|delete|update)$') { return 'ReadWrite' } return Get-GoogleIamPermissionAccessClass -Permission $Name } default { throw "Unknown requirement kind: $Kind" } } } function Get-GraphPermissionAccessClass { param([Parameter(Mandatory)][string]$Permission) if ($Permission -eq 'user_impersonation') { return 'ReadWrite' } if ($Permission -eq 'offline_access') { return 'ReadOnly' } if ($Permission -eq 'User.Read') { return 'ReadOnly' } if ($Permission -match 'ReadWrite|\.Write\.|Invite\.|EnableDisable|ManageAsApp') { return 'ReadWrite' } if ($Permission -match '\.Read\.|Read\.All|Read\.Directory|AuditLog\.Read') { return 'ReadOnly' } return 'ReadWrite' } function Get-GoogleOAuthScopeAccessClass { param([Parameter(Mandatory)][string]$Scope) if ($Scope -match '\.readonly$') { return 'ReadOnly' } if ($Scope -match 'admin\.directory\.(user|group)(/|$)' -and $Scope -notmatch '\.readonly') { return 'ReadWrite' } if ($Scope -match 'admin\.directory\.rolemanagement$') { return 'ReadWrite' } if ($Scope -match 'apps\.groups\.settings|gmail\.settings\.|gmail\.modify|mail\.google\.com') { return 'ReadWrite' } if ($Scope -match 'admin\.directory\.domain') { return 'ReadWrite' } if ($Scope -match 'cloud-platform|/auth/iam') { return 'ReadOnly' } if ($Scope -match 'admin\.reports\.') { return 'ReadOnly' } return 'ReadWrite' } function Get-GoogleIamPermissionAccessClass { param([Parameter(Mandatory)][string]$Permission) $last = ($Permission -split '\.')[-1] switch ($last) { { $_ -in @('create', 'delete', 'update', 'setIamPolicy') } { return 'ReadWrite' } default { return 'ReadOnly' } } } function Get-AwsActionAccessClass { param([Parameter(Mandatory)][string]$Action) if ($Action -eq 'kms:GenerateDataKey') { return 'ReadWrite' } if ($Action -match ':Simulate|:GenerateCredentialReport|:GenerateServiceLastAccessedDetails') { return 'ReadOnly' } if ($Action -match ':Create|:Delete|:Update|:Put|:Attach|:Detach|:Add|:Remove|:Provision') { return 'ReadWrite' } if ($Action -match ':Get|:List|:Describe|:Lookup') { return 'ReadOnly' } return 'ReadOnly' } function Test-RequirementIncludedForAccess { param( [Parameter(Mandatory)][string]$Kind, [Parameter(Mandatory)][string]$Name, [ValidateSet('ReadOnly', 'ReadWrite')] [string]$Access ) if ($Access -eq 'ReadWrite') { return $true } return (Get-RequirementAccessClass -Kind $Kind -Name $Name) -eq 'ReadOnly' } function ConvertTo-ReadOnlyGoogleOAuthScope { param([Parameter(Mandatory)][string]$Scope) switch -Regex ($Scope) { '^https://www\.googleapis\.com/auth/admin\.directory\.user$' { return 'https://www.googleapis.com/auth/admin.directory.user.readonly' } '^https://www\.googleapis\.com/auth/admin\.directory\.group$' { return 'https://www.googleapis.com/auth/admin.directory.group.readonly' } '^https://www\.googleapis\.com/auth/admin\.directory\.rolemanagement$' { return $null } default { return $Scope } } } function Filter-PermissionsForAccess { param( [Parameter(Mandatory)][object[]]$Permissions, [string]$Access, [Parameter(Mandatory)][string]$Kind ) if ([string]::IsNullOrWhiteSpace($Access) -or $Access -eq 'ReadWrite') { return @($Permissions) } $filtered = @($Permissions | Where-Object { $value = if ($_.Value) { [string]$_.Value } else { [string]$_ } Test-RequirementIncludedForAccess -Kind $Kind -Name $value -Access 'ReadOnly' }) return $filtered } function Get-EntroGcpPinnedCustomRolePermissions { return @( 'logging.buckets.get', 'logging.buckets.list', 'logging.exclusions.get', 'logging.exclusions.list' 'logging.links.get', 'logging.links.list', 'logging.locations.get', 'logging.locations.list' 'logging.logEntries.download', 'logging.logEntries.list', 'logging.logMetrics.get', 'logging.logMetrics.list' 'logging.logServiceIndexes.list', 'logging.logServices.list', 'logging.logs.list' 'logging.operations.get', 'logging.operations.list', 'logging.privateLogEntries.list' 'logging.queries.create', 'logging.queries.delete', 'logging.queries.get', 'logging.queries.list' 'logging.queries.listShared', 'logging.queries.update', 'logging.sinks.get', 'logging.sinks.list' 'logging.usage.get', 'logging.views.access', 'logging.views.get', 'logging.views.list' 'logging.views.listLogs', 'logging.views.listResourceKeys', 'logging.views.listResourceValues' 'resourcemanager.organizations.get', 'secretmanager.versions.access', 'secretmanager.secrets.list' 'secretmanager.versions.list', 'secretmanager.locations.list', 'secretmanager.secrets.getIamPolicy' 'iam.serviceAccounts.list', 'iam.serviceAccounts.get', 'iam.serviceAccounts.getIamPolicy' 'iam.serviceAccountKeys.get', 'iam.serviceAccountKeys.list', 'iam.roles.get' 'cloudfunctions.functions.get', 'cloudfunctions.functions.list', 'cloudbuild.builds.get' 'cloudbuild.builds.list', 'cloudbuild.operations.get', 'cloudbuild.operations.list' 'recommender.iamPolicyInsights.get', 'recommender.iamPolicyInsights.list' 'recommender.iamPolicyLateralMovementInsights.get', 'recommender.iamPolicyLateralMovementInsights.list' 'recommender.iamPolicyRecommendations.get', 'recommender.iamPolicyRecommendations.list' 'recommender.iamPolicyRecommenderConfig.get', 'recommender.iamServiceAccountInsights.get' 'recommender.iamServiceAccountInsights.list', 'recommender.locations.get', 'recommender.locations.list' 'recommender.cloudAssetInsights.get', 'recommender.cloudAssetInsights.list' 'resourcemanager.projects.get', 'resourcemanager.projects.list' 'cloudasset.assets.listResource', 'cloudasset.assets.listIamPolicy', 'cloudasset.assets.listOrgPolicy' 'cloudasset.assets.listAccessPolicy', 'cloudasset.assets.listOSInventories' 'policyanalyzer.serviceAccountKeyLastAuthenticationActivities.query' 'policyanalyzer.serviceAccountLastAuthenticationActivities.query' 'serviceusage.quotas.get', 'serviceusage.services.get', 'serviceusage.services.list' 'iam.roles.list', 'resourcemanager.organizations.getIamPolicy', 'resourcemanager.projects.getIamPolicy' 'resourcemanager.folders.getIamPolicy', 'resourcemanager.folders.get' ) } function Get-ContextFeatureNames { param([hashtable]$Context) if (-not $Context.ContainsKey('Feature') -or $null -eq $Context['Feature']) { return ,@() } $f = $Context['Feature'] if ($f -is [array]) { return ,@($f | ForEach-Object { [string]$_ } | Where-Object { $_ }) } return ,@([string]$f -split ',' | ForEach-Object { $_.Trim() } | Where-Object { $_ }) } function Split-RequirementsByAccessClass { param( [Parameter(Mandatory)][string[]]$Names, [Parameter(Mandatory)][string]$Kind ) $readOnly = [System.Collections.Generic.List[string]]::new() $readWrite = [System.Collections.Generic.List[string]]::new() foreach ($name in @($Names | Sort-Object -Unique)) { if ((Get-RequirementAccessClass -Kind $Kind -Name $name) -eq 'ReadWrite') { $readWrite.Add($name) } else { $readOnly.Add($name) } } return @{ ReadOnly = $readOnly.ToArray(); ReadWrite = $readWrite.ToArray() } } function Get-IntegrationWizardAccessRequirementLists { param( [Parameter(Mandatory)][hashtable]$Definition, [Parameter(Mandatory)][hashtable]$Context ) $moduleRoot = $PSScriptRoot $access = if ($Context.ContainsKey('Access')) { [string]$Context['Access'] } else { '' } $readOnly = [System.Collections.Generic.List[string]]::new() $readWrite = [System.Collections.Generic.List[string]]::new() $targets = if ($Definition['IsComposite']) { @($Definition.Members) } else { @($Definition) } foreach ($def in $targets) { $id = [string]$def.Id switch -Regex ($id) { '^azure-connector$' { Import-Module (Join-Path $moduleRoot 'ISC.EntraConnector.psm1') -Force -WarningAction SilentlyContinue Import-Module (Join-Path $moduleRoot 'ISC.EntraCiemConnector.psm1') -Force -WarningAction SilentlyContinue Initialize-EntraConnectorData Initialize-EntraCiemConnectorData $mode = if ($access -eq 'ReadWrite' -and $Context['PermissionMode']) { $Context['PermissionMode'] } else { 'Granular' } $features = Get-ContextFeatureNames -Context $Context $app = @(Get-SelectedPermissions -Mode $mode -FeatureNames $features -Access $access) $deleg = @(Get-SelectedDelegatedPermissions -FeatureNames $features -Access $access) foreach ($p in @($app + $deleg)) { $line = "$($p.Resource): $($p.Value)" if ((Get-RequirementAccessClass -Kind 'MicrosoftGraphPermission' -Name $p.Value) -eq 'ReadWrite') { $readWrite.Add($line) } else { $readOnly.Add($line) } } } '^aws-saas-connector$' { Import-Module (Join-Path $moduleRoot 'ISC.AwsConnector.psm1') -Force -Global -WarningAction SilentlyContinue Import-Module (Join-Path $moduleRoot 'ISC.AwsSaasConnector.psm1') -Force -Global -WarningAction SilentlyContinue Import-Module (Join-Path $moduleRoot 'ISC.AwsCiemConnector.psm1') -Force -Global -WarningAction SilentlyContinue Initialize-AwsConnectorData Initialize-AwsSaasConnectorData $set = if ($Context['PolicySet']) { $Context['PolicySet'] } else { 'Mgo' } $features = Get-ContextFeatureNames -Context $Context $aggOnly = ($access -eq 'ReadOnly') $docs = Get-SelectedPolicyDocuments -Set $set -FeatureNames $features -SkipProvisioning:$aggOnly -Partition 'aws' foreach ($doc in $docs.Values) { $obj = $doc | ConvertFrom-Json foreach ($stmt in @($obj.Statement)) { foreach ($act in @($stmt.Action)) { $split = Split-RequirementsByAccessClass -Names @($act) -Kind 'AwsAction' foreach ($a in $split.ReadOnly) { $readOnly.Add($a) } foreach ($a in $split.ReadWrite) { $readWrite.Add($a) } } } } } '^aws-ciem-connector$' { Import-Module (Join-Path $moduleRoot 'ISC.AwsCiemConnector.psm1') -Force -WarningAction SilentlyContinue Initialize-AwsCiemConnectorData $ciemActions = @(Get-CiemPolicyActions) $split = Split-RequirementsByAccessClass -Names $ciemActions -Kind 'AwsAction' foreach ($a in $split.ReadOnly) { $readOnly.Add($a) } foreach ($a in $split.ReadWrite) { $readWrite.Add($a) } $ic = Get-CiemIdentityCenterRequirementActions foreach ($a in @($ic.ReadOnly)) { $readOnly.Add($a) } if ($access -ne 'ReadOnly') { foreach ($a in @($ic.ReadWrite)) { $readWrite.Add($a) } } } '^google-(saas|ciem)-connector$' { Import-Module (Join-Path $moduleRoot 'ISC.GoogleCiemConnector.psm1') -Force -Global -WarningAction SilentlyContinue Import-Module (Join-Path $moduleRoot 'ISC.GoogleWorkspaceConnector.psm1') -Force -Global -WarningAction SilentlyContinue Initialize-GoogleWorkspaceConnectorData Initialize-GoogleCiemConnectorData $features = Get-ContextFeatureNames -Context $Context $scopes = @(Get-SelectedScopes -FeatureNames $features -Access $access) if ($scopes.Count -gt 0) { $split = Split-RequirementsByAccessClass -Names $scopes -Kind 'GoogleOAuthScope' foreach ($s in $split.ReadOnly) { $readOnly.Add($s) } foreach ($s in $split.ReadWrite) { $readWrite.Add($s) } } $skipGcpWrite = ($access -eq 'ReadOnly') $iam = @(Get-GoogleCustomRolePermissions -FeatureNames $features -SkipGcpWrite:$skipGcpWrite) if ($iam.Count -gt 0) { $splitIam = Split-RequirementsByAccessClass -Names $iam -Kind 'GoogleIamPermission' foreach ($p in $splitIam.ReadOnly) { $readOnly.Add($p) } foreach ($p in $splitIam.ReadWrite) { $readWrite.Add($p) } } } '^google-gcp-entro$' { $split = Split-RequirementsByAccessClass -Names @(Get-EntroGcpPinnedCustomRolePermissions) -Kind 'EntroGcpCustomPermission' foreach ($p in $split.ReadOnly) { $readOnly.Add($p) } foreach ($p in $split.ReadWrite) { $readWrite.Add($p) } $readOnly.Add('Entro predefined org roles: logging.privateLogViewer, organizationViewer, secretmanager.viewer, and nine other viewer roles (see Entro GCP catalog)') } '^databricks-connector$' { $readWrite.Add('Databricks Account Admin (account-level service principal)') } '^github-entro$' { $readOnly.Add('repo') $readOnly.Add('read:org') } '^github-connector$' { $readOnly.Add('repo') $readOnly.Add('read:org') $features = Get-ContextFeatureNames -Context $Context if ($features -contains 'AgentDiscovery') { $readOnly.Add('admin:org') $readOnly.Add('manage_billing:copilot') } if ($features -contains 'ActivityInsights') { $readOnly.Add('read:audit_log') } } '^azure-entro$' { $readOnly.Add('Microsoft Graph: User.Read') $readOnly.Add('Microsoft Graph: User.Read.All') $readOnly.Add('Microsoft Graph: Directory.Read.All') $readOnly.Add('Microsoft Graph: Application.Read.All') $readOnly.Add('Microsoft Graph: AuditLog.Read.All') } '^aws-entro$|^google-workspace-entro$' { $readOnly.Add('Entro connected identity (read-only discovery; onboarding steps create IAM/app resources in the cloud)') } } } return @{ ReadOnly = @($readOnly | Sort-Object -Unique) ReadWrite = @($readWrite | Sort-Object -Unique) Access = $access } } function Get-EntraPermissionCatalogForRequirements { param( [string]$Mode, [string[]]$FeatureNames, [string]$Access ) if (-not (Get-Command Get-SelectedPermissions -ErrorAction SilentlyContinue)) { return @{ ReadOnly = @(); ReadWrite = @() } } $app = @(Get-SelectedPermissions -Mode $Mode -FeatureNames $FeatureNames -Access $Access) $deleg = @(Get-SelectedDelegatedPermissions -FeatureNames $FeatureNames -Access $Access) $all = @($app + $deleg) $readOnly = @($all | Where-Object { (Get-RequirementAccessClass -Kind 'MicrosoftGraphPermission' -Name $_.Value) -eq 'ReadOnly' }) $readWrite = @($all | Where-Object { (Get-RequirementAccessClass -Kind 'MicrosoftGraphPermission' -Name $_.Value) -eq 'ReadWrite' }) return @{ ReadOnly = $readOnly; ReadWrite = $readWrite; Granted = $all } } Export-ModuleMember -Function @( 'Get-RequirementAccessClass' 'Test-RequirementIncludedForAccess' 'ConvertTo-ReadOnlyGoogleOAuthScope' 'Filter-PermissionsForAccess' 'Get-EntraPermissionCatalogForRequirements' 'Get-IntegrationWizardAccessRequirementLists' 'Get-EntroGcpPinnedCustomRolePermissions' 'Split-RequirementsByAccessClass' ) |