Skip to content

Commit

Permalink
[Enhancement] GR2 validation 8 Guest user accounts (#156)
Browse files Browse the repository at this point in the history
* ItemName change, making it mandatory

* rename GR name and guest account control to v2.0

* list all guest user with updated logic

* Testing Gr2 V8 updates

* replace the ItemName

* Change the gUest account table to display unique users

* put back prev. workbook

* add roles to the table unique rows

* Mandatory validation and unique users only in guest account table

* unique guest user list

* updated modules

* test deployment

* storage bicep testing

* storage bicep testing

* add Comments to the guest table

* updating compliance messages

* removing branch from workflow

* write output to the pipeline
  • Loading branch information
dutt0 authored Jul 11, 2024
1 parent 719fda8 commit c2d3988
Show file tree
Hide file tree
Showing 14 changed files with 181 additions and 104 deletions.
Binary file modified psmodules/Check-ExternalAccounts.zip
Binary file not shown.
Binary file modified psmodules/GR-ComplianceChecks.zip
Binary file not shown.
2 changes: 1 addition & 1 deletion setup/IaC/modules/automationaccount.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ resource module5 'modules' = if (newDeployment || updatePSModules) {
properties: {
contentLink: {
uri: '${ModuleBaseURL}/Check-ExternalAccounts.zip'
version: '1.2.4'
version: '1.2.5'
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion setup/IaC/modules/gr.workbook
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,7 @@
"type": 3,
"content": {
"version": "KqlItem/1.0",
"query": "GR2ExternalUsers_CL \n| where ReportTime_s == \"{RunTime}\"\n| project DisplayName_s, Mail_s, Subscription_s",
"query": "GR2ExternalUsers_CL \n| where ReportTime_s == \"{RunTime}\"\n| project DisplayName_s, Mail_s, Subscription_s, Role_s, Comments_s",
"size": 0,
"timeContext": {
"durationMs": 86400000
Expand Down
69 changes: 40 additions & 29 deletions setup/IaC/modules/storage.bicep
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
param storageAccountName string
param location string
param containername string

resource guardrailsStorage 'Microsoft.Storage/storageAccounts@2021-06-01' = {
name: storageAccountName
location: location
Expand All @@ -15,37 +16,47 @@ resource guardrailsStorage 'Microsoft.Storage/storageAccounts@2021-06-01' = {
supportsHttpsTrafficOnly: true
minimumTlsVersion: 'TLS1_2'
}
resource blobServices 'blobServices'={
name: 'default'
properties: {
cors: {
corsRules: []
}
deleteRetentionPolicy: {
enabled: false
}
}

resource blobServices 'Microsoft.Storage/storageAccounts/blobServices@2021-06-01' = {
name: 'default'
parent: guardrailsStorage
properties: {
cors: {
corsRules: []
}
resource container1 'containers'={
name: containername
properties: {
immutableStorageWithVersioning: {
enabled: false
}
denyEncryptionScopeOverride: false
defaultEncryptionScope: '$account-encryption-key'
publicAccess: 'None'
}
deleteRetentionPolicy: {
enabled: false
}
resource container2 'containers'={
name: 'configuration'
properties: {
immutableStorageWithVersioning: {
enabled: false
}
denyEncryptionScopeOverride: false
defaultEncryptionScope: '$account-encryption-key'
publicAccess: 'None'
}
}
}

resource container1 'Microsoft.Storage/storageAccounts/blobServices/containers@2021-06-01' = {
name: '${guardrailsStorage.name}/default/${containername}'
properties: {
immutableStorageWithVersioning: {
enabled: false
}
denyEncryptionScopeOverride: false
defaultEncryptionScope: '$account-encryption-key'
publicAccess: 'None'
}
dependsOn: [
blobServices
]
}

resource container2 'Microsoft.Storage/storageAccounts/blobServices/containers@2021-06-01' = {
name: '${guardrailsStorage.name}/default/configuration'
properties: {
immutableStorageWithVersioning: {
enabled: false
}
denyEncryptionScopeOverride: false
defaultEncryptionScope: '$account-encryption-key'
publicAccess: 'None'
}
dependsOn: [
blobServices
]
}
4 changes: 2 additions & 2 deletions setup/modules.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@
"Control": "Guardrails2",
"ModuleType": "Builtin",
"Status": "Enabled",
"Required": "False",
"Script": "Check-ExternalUsers -ControlName $msgTable.CtrName2 -ItemName $msgTable.removeGuestAccounts -MsgTable $msgTable -ReportTime $ReportTime -itsgcode $vars.itsgcode",
"Required": "True",
"Script": "Check-ExternalUsers -ControlName $msgTable.CtrName2 -ItemName $msgTable.existingGuestAccounts -MsgTable $msgTable -ReportTime $ReportTime -itsgcode $vars.itsgcode",
"localVariables": [
{
"Name": "itsgcode",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
RootModule = 'Check-ExternalAccounts'

# Version number of this module.
ModuleVersion = '1.2.4'
ModuleVersion = '1.2.5'

# Supported PSEditions
# CompatiblePSEditions = @()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Checking for GUEST accounts
# Note that this URL only reads from the All-Users (not the deleted accounts) in the directory,
# This querly looks for accounts marked as GUEST
# This query looks for accounts marked as GUEST
# It does not list GUEST accounts from the list of deleted accounts.

function Check-ExternalUsers {
Expand All @@ -10,31 +10,37 @@
[string] $itsgcode,
[hashtable] $msgTable,
[Parameter(Mandatory=$true)]
[string]
$ReportTime
[string] $ReportTime
)

[psCustomObject] $guestUsersArray = New-Object System.Collections.ArrayList
[PSCustomObject] $ErrorList = New-Object System.Collections.ArrayList
[bool] $IsCompliant= $false

$guestUsers_wo_matchedUsers = @()
$guestUsersArray_grouped = @()
$unique_guestUsersArray = @()

$stopWatch = New-Object -TypeName System.Diagnostics.Stopwatch
$stopWatch.Start()

# Only get the Guests accounts
if ($debug) {Write-Output "Getting guest users in the tenant"}
$guestUsers = Get-AzADUser -Filter "usertype eq 'guest'"

$guestUsers = Get-AzADUser -Filter "usertype eq 'guest'"

# Default pass (v2.0) for no guest account OR if Guest accounts whether or not have any permissions on the Azure subscriptions
$IsCompliant= $true

# Find the number of guest accounts
if ($null -eq $guestUsers) {
# There are no Guest users in the tenant
Write-Output "No Guest Users found in the tenant"
$IsCompliant= $true
$comment = $msgTable.noGuestAccounts
$MitigationCommands = "N/A"
}
else {
if ($debug) {Write-Output "Found $($guestUsers.Count) Guest Users in the tenant"}

# get the Azure subscriptions
$subs=Get-AzSubscription -ErrorAction SilentlyContinue| Where-Object {$_.State -eq 'Enabled'}
if ($debug) {Write-Output "Found $($subs.Count) subscriptions"}

Expand All @@ -49,8 +55,7 @@
if ($debug) {Write-Output "Found $($subRoleAssignments.Count) Role Assignments in that subscription"}

# Find each guest users having a role assignment
$matchedUser = $guestUsers | Where-Object {$subRoleAssignments.ObjectId -contains $_.Id}

$matchedUser = $guestUsers | Where-Object {$subRoleAssignments.ObjectId -contains $_.Id}
if (!$null -eq $matchedUser) {
if ($debug) {Write-Output "Found $($matchedUser.Count) Guest users with role assignment"}

Expand All @@ -64,25 +69,53 @@
Type = $user.userType
CreatedDate = $user.createdDateTime
Enabled = $user.accountEnabled
Comments = $msgTable.guestMustbeRemoved
Roles = "True" # At least one role assigned to the user in this scope(i.e. subscription)
Comments = $msgTable.guestAssigned
ItemName= $ItemName
ReportTime = $ReportTime
itsgcode = $itsgcode
}
$guestUsersArray.add($Customuser)
}
}
else {
else{
Write-Output "Found no Guest users with role assignment"
}

# Find any guest users without having a role assignment
$guestUsers_wo_matchedUsers = $guestUsers | Where-Object { $_ -notin $matchedUser }
if (!$null -eq $guestUsers_wo_matchedUsers) {

# Add the guest users without role assignment to the list
foreach ($user in $guestUsers_wo_matchedUsers) {
$Customuser_noMatch = [PSCustomObject] @{
DisplayName = $user.DisplayName
Subscription = $sub.Name
Mail = $user.mail
Type = $user.userType
CreatedDate = $user.createdDateTime
Enabled = $user.accountEnabled
Roles = "False" # No role assigned to the user in this scope(i.e. subscription)
Comments = $msgTable.guestNotAssigned
ItemName= $ItemName
ReportTime = $ReportTime
itsgcode = $itsgcode
}
$guestUsersArray.add($Customuser_noMatch)
}
}
else{
Write-Output "All Guest users have role assignment"
}


}
}
}

# If there are no Guest accounts or Guest accounts don't have any permissions on the Azure subscriptions, it's fine
# we still create the Log Analytics table
if ($guestUsersArray.Count -eq 0) {
$IsCompliant= $true
$MitigationCommands = "N/A"
# Don't overwrite the comment if there are no guest users
if (!$null -eq $guestUsers) {
Expand All @@ -96,6 +129,7 @@
Type = "N/A"
CreatedDate = "N/A"
Enabled = "N/A"
Roles = "N/A"
Comments = $comment
ItemName= $ItemName
ReportTime = $ReportTime
Expand All @@ -104,14 +138,42 @@
$guestUsersArray.add($Customuser)
}
else {
$IsCompliant= $false
$comment = $msgTable.removeGuestAccountsComment
$MitigationCommands = $msgTable.removeGuestAccounts
$comment = $msgTable.existingGuestAccountsComment
$MitigationCommands = $msgTable.existingGuestAccounts

# Group by DisplayName and others, aggregate Subscription
$guestUsersArray_grouped = $guestUsersArray | Group-Object -Property DisplayName, Roles, Comments | ForEach-Object {
$subscriptions = $_.Group.Subscription -join ', '
[PSCustomObject]@{
DisplayName = $_.Group[0].DisplayName
Subscription = $subscriptions
Mail = $_.Group[0].Mail
Type = $_.Group[0].Type
CreatedDate = $_.Group[0].CreatedDate
Enabled = $_.Group[0].Enabled
Role = $_.Group[0].Roles
Comments = $_.Group[0].Comments
ItemName= $_.Group[0].ItemName
ReportTime = $_.Group[0].ReportTime
itsgcode = $_.Group[0].itsgcode
}
}
$filtered_unique_guestUsersArray_grouped = $guestUsersArray_grouped |
Sort-Object -Property Role -Descending | # Sort by Role descending so True comes before False
Sort-Object -Property DisplayName -Unique # Get unique DisplayNames, keeping the first occurrence

# Modify Subscription field to blank if Role = False
$unique_guestUsersArray = $filtered_unique_guestUsersArray_grouped | ForEach-Object {
if ($_.Role -eq "False") {
$_.Subscription = ""
}
$_ # Output the modified object
}
}

# Convert data to JSON format for input in Azure Log Analytics
#$JSONGuestUsers = ConvertTo-Json -inputObject $guestUsersArray
#Write-Output "Creating or updating Log Analytics table 'GR2ExternalUsers' and adding '$($guestUsers.Count)' guest user entries"
# $JSONGuestUsers = ConvertTo-Json -inputObject $guestUsersArray
# Write-Output "Creating or updating Log Analytics table 'GR2ExternalUsers' and adding '$($guestUsers.Count)' guest user entries"

# Add the list of non-compliant users to Log Analytics (in a different table)
<#Send-OMSAPIIngestionFile -customerId $WorkSpaceID -sharedkey $workspaceKey `
Expand All @@ -127,7 +189,7 @@
MitigationCommands = $MitigationCommands
}
$AdditionalResults = [PSCustomObject]@{
records = $guestUsersArray
records = $unique_guestUsersArray
logType = "GR2ExternalUsers"
}

Expand Down
Loading

0 comments on commit c2d3988

Please sign in to comment.