diff --git a/DCToolbox/DCToolbox.psm1 b/DCToolbox/DCToolbox.psm1 index e35b501..e7551d3 100644 --- a/DCToolbox/DCToolbox.psm1 +++ b/DCToolbox/DCToolbox.psm1 @@ -1058,11 +1058,13 @@ function Install-DCToolbox { Write-Verbose -Message "Not found! Installing DCToolbox $LatestVersion..." Install-Module DCToolbox -Scope CurrentUser -Force -Verbose:$false Write-Verbose -Message "Done!" - } elseif ($ModuleVersion -ne $LatestVersion) { + } + elseif ($ModuleVersion -ne $LatestVersion) { Write-Verbose -Message "Found DCToolbox $ModuleVersion. Upgrading to $LatestVersion..." Install-Module DCToolbox -Scope CurrentUser -Force -Verbose:$false Write-Verbose -Message "Done!" - } else { + } + else { Write-Verbose -Message "DCToolbox $ModuleVersion found!" } @@ -1105,7 +1107,8 @@ function Confirm-DCPowerShellVersion { return exit - } else { + } + else { Write-Verbose -Message "PowerShell $($PSVersionTable.PSVersion.Major).$($PSVersionTable.PSVersion.Minor) found!" } } @@ -1147,10 +1150,12 @@ function Install-DCMicrosoftGraphPowerShellModule { if (!($ModuleVersion)) { Write-Verbose -Message "Not found! Installing the Graph PowerShell module..." Install-Module Microsoft.Graph -Scope CurrentUser -Force -Verbose:$false - } elseif (($ModuleVersion).Version.Major -le 2 -and ($ModuleVersion).Version.Minor -lt 11) { + } + elseif (($ModuleVersion).Version.Major -le 2 -and ($ModuleVersion).Version.Minor -lt 11) { Write-Verbose -Message "Found version $(($ModuleVersion).Version.Major).$(($ModuleVersion).Version.Minor). Upgrading..." Install-Module Microsoft.Graph -Scope CurrentUser -Force -Verbose:$false - } else { + } + else { Write-Verbose -Message "Graph PowerShell $(($ModuleVersion).Version.Major).$(($ModuleVersion).Version.Minor) found!" } } @@ -1264,12 +1269,12 @@ function Invoke-DCEntraIDDeviceAuthFlow { # STEP 1: Get a device authentication code to use in browser. - $Headers=@{} + $Headers = @{} $Headers["Content-Type"] = 'application/x-www-form-urlencoded' $body = @{ "client_id" = $ClientID - "scope" = "openid offline_access" + "scope" = "openid offline_access" } $authResponse = Invoke-RestMethod -UseBasicParsing -Method Post -Uri "https://login.microsoftonline.com/$TenantID/oauth2/v2.0/devicecode" -Headers $Headers -Body $body @@ -1290,8 +1295,8 @@ function Invoke-DCEntraIDDeviceAuthFlow { for ($i = 0; $i -lt 60; $i++) { try { $body = @{ - "grant_type" = "urn:ietf:params:oauth:grant-type:device_code" - "client_id" = $ClientID + "grant_type" = "urn:ietf:params:oauth:grant-type:device_code" + "client_id" = $ClientID "device_code" = $authResponse.device_code } @@ -1316,7 +1321,8 @@ function Invoke-DCEntraIDDeviceAuthFlow { Write-Host -ForegroundColor Yellow "Access token was copied to clipboard!" Write-Host "" $Tokens.access_token | Set-Clipboard - } else { + } + else { Write-Host "" Write-Host -ForegroundColor Yellow "Refresh token:" @@ -1328,15 +1334,18 @@ function Invoke-DCEntraIDDeviceAuthFlow { } return - } catch { + } + catch { if (($_ | ConvertFrom-Json).error -eq 'code_expired') { Write-Host "" Write-Host -ForegroundColor Red 'Verification code expired!' Write-Host "" return - } elseif (($_ | ConvertFrom-Json).error -eq 'authorization_pending') { + } + elseif (($_ | ConvertFrom-Json).error -eq 'authorization_pending') { Start-Sleep -Seconds 5 - } else { + } + else { Write-Host "" Write-Host -ForegroundColor Red ($_ | ConvertFrom-Json).error Write-Host "" @@ -1441,7 +1450,10 @@ function Invoke-DCMsGraphQuery { .PARAMETER GraphBody The request body of the Graph call. This is often used with methids like POST, PUT and PATCH. It is not used with GET. - + + .PARAMETER AdditionalHeaders + The additional headers to use with the Graph call. Example: @{'ConsistencyLevel' = 'eventual' } + .INPUTS None @@ -1455,7 +1467,10 @@ function Invoke-DCMsGraphQuery { .EXAMPLE Invoke-DCMsGraphQuery -AccessToken $AccessToken -GraphMethod 'GET' -GraphUri 'https://graph.microsoft.com/v1.0/users/' - #> + + .EXAMPLE + Invoke-DCMsGraphQuery -AccessToken $AccessToken -GraphMethod 'GET' -GraphUri 'https://graph.microsoft.com/v1.0/users/?$count=true&search="displayName:-admin"' -AdditionalHeaders @{'ConsistencyLevel' = 'eventual' } + #> param ( @@ -1469,7 +1484,10 @@ function Invoke-DCMsGraphQuery { [string]$GraphUri, [parameter(Mandatory = $false)] - [string]$GraphBody = '' + [string]$GraphBody = '', + + [parameter(Mandatory = $false)] + [hashtable]$AdditionalHeaders ) # Check if authentication was successfull. @@ -1479,7 +1497,9 @@ function Invoke-DCMsGraphQuery { 'Content-Type' = "application\json" 'Authorization' = "Bearer $AccessToken" } - + If ($AdditionalHeaders) { + $HeaderParams += $AdditionalHeaders + } # Create an empty array to store the result. $QueryRequest = @() @@ -1510,7 +1530,11 @@ function Invoke-DCMsGraphQuery { } } - + While ($QueryRequest.nextLink) { + $QueryRequest = Invoke-RestMethod -Headers $HeaderParams -Uri $QueryRequest.'nextLink' -UseBasicParsing -Method $GraphMethod -ContentType "application/json" + $QueryResult += $QueryRequest.value + } + $QueryResult } else { @@ -1593,7 +1617,8 @@ function Enable-DCEntraIDPIMRole { # Check if the MSAL module is installed. if (Get-Module -ListAvailable -Name "msal.ps") { # Do nothing. - } else { + } + else { Write-Verbose -Verbose -Message 'Installing MSAL module...' Install-Package msal.ps -Force | Out-Null } @@ -1646,7 +1671,7 @@ function Enable-DCEntraIDPIMRole { # Get all policy rules belonging to this role management policy: $PolicyRules = Get-MgPolicyRoleManagementPolicyRule -UnifiedRoleManagementPolicyId $Policy.Id - $MaximumDuration = ($PolicyRules | where id -eq 'Expiration_EndUser_Assignment').AdditionalProperties.maximumDuration + $MaximumDuration = ($PolicyRules | Where-Object id -EQ 'Expiration_EndUser_Assignment').AdditionalProperties.maximumDuration $CustomObject | Add-Member -MemberType NoteProperty -Name 'maximumGrantPeriodInHours' -Value ($MaximumDuration -replace 'PT', '' -replace 'H', '') $CustomObject | Add-Member -MemberType NoteProperty -Name 'StartDateTime' -Value $RoleAssignment.StartDateTime @@ -1748,25 +1773,25 @@ function Enable-DCEntraIDPIMRole { if ($ExistingActivations) { $params = @{ - "PrincipalId" = "$CurrentAccountId" + "PrincipalId" = "$CurrentAccountId" "RoleDefinitionId" = "$($Role.RoleDefinitionId)" "DirectoryScopeId" = "/" - "Action" = "SelfDeactivate" + "Action" = "SelfDeactivate" } $Result = New-MgRoleManagementDirectoryRoleAssignmentScheduleRequest -BodyParameter $params } $params = @{ - "PrincipalId" = "$CurrentAccountId" + "PrincipalId" = "$CurrentAccountId" "RoleDefinitionId" = "$($Role.RoleDefinitionId)" - "Justification" = "$Reason" + "Justification" = "$Reason" "DirectoryScopeId" = "/" - "Action" = "SelfActivate" - "ScheduleInfo" = @{ + "Action" = "SelfActivate" + "ScheduleInfo" = @{ "StartDateTime" = Get-Date - "Expiration" = @{ - "Type" = "AfterDuration" + "Expiration" = @{ + "Type" = "AfterDuration" "Duration" = "PT$Duration`H" } } @@ -2355,9 +2380,9 @@ function Invoke-DCM365DataExfiltration { # Connect to Microsoft Graph with application credentials. Write-Verbose -Verbose -Message "Connecting to Microsoft Graph as Service Principal '$ClientID'..." $Parameters = @{ - ClientID = $ClientID + ClientID = $ClientID ClientSecret = $ClientSecret - TenantName = $TenantName + TenantName = $TenantName } $AccessToken = Connect-DCMsGraphAsApplication @Parameters @@ -2368,7 +2393,7 @@ function Invoke-DCM365DataExfiltration { $Parameters = @{ AccessToken = $AccessToken GraphMethod = 'GET' - GraphUri = "https://graph.microsoft.com/v1.0/groups?`$filter=groupTypes/any(c:c+eq+'Unified')&`$select=id,displayName,description" + GraphUri = "https://graph.microsoft.com/v1.0/groups?`$filter=groupTypes/any(c:c+eq+'Unified')&`$select=id,displayName,description" } $M365Groups = Invoke-DCMsGraphQuery @Parameters @@ -2381,7 +2406,7 @@ function Invoke-DCM365DataExfiltration { $Parameters = @{ AccessToken = $AccessToken GraphMethod = 'GET' - GraphUri = "https://graph.microsoft.com/v1.0/groups/$($Group.id)/drive?`$select=id,name,webUrl" + GraphUri = "https://graph.microsoft.com/v1.0/groups/$($Group.id)/drive?`$select=id,name,webUrl" } Invoke-DCMsGraphQuery @Parameters @@ -2396,14 +2421,14 @@ function Invoke-DCM365DataExfiltration { $Parameters = @{ AccessToken = $AccessToken GraphMethod = 'GET' - GraphUri = "https://graph.microsoft.com/v1.0/drives/$($DocumentLibrary.id)/root/children" + GraphUri = "https://graph.microsoft.com/v1.0/drives/$($DocumentLibrary.id)/root/children" } $RootContent = Invoke-DCMsGraphQuery @Parameters - $RootContent | where file + $RootContent | Where-Object file # Download files in root directory. - foreach ($File in ($RootContent | where file)) { + foreach ($File in ($RootContent | Where-Object file)) { Write-Verbose -Verbose -Message "------ Downloading '$($File.Name)' ($([math]::round($File.Size/1MB, 2)) MB)..." $HeaderParams = @{ @@ -2416,18 +2441,18 @@ function Invoke-DCM365DataExfiltration { } } - foreach ($Item in ($RootContent | where folder)) { + foreach ($Item in ($RootContent | Where-Object folder)) { $Parameters = @{ AccessToken = $AccessToken GraphMethod = 'GET' - GraphUri = "https://graph.microsoft.com/v1.0/drives/$($DocumentLibrary.id)/items/$($Item.id)/children" + GraphUri = "https://graph.microsoft.com/v1.0/drives/$($DocumentLibrary.id)/items/$($Item.id)/children" } $SubContentLevel1 = Invoke-DCMsGraphQuery @Parameters - $SubContentLevel1 | where file + $SubContentLevel1 | Where-Object file # Download files in sub SubContentLevel1. - foreach ($File in ($SubContentLevel1 | where file)) { + foreach ($File in ($SubContentLevel1 | Where-Object file)) { Write-Verbose -Verbose -Message "------ Downloading '$($File.Name)' ($([math]::round($File.Size/1MB, 2)) MB)..." $HeaderParams = @{ @@ -2441,18 +2466,18 @@ function Invoke-DCM365DataExfiltration { } # Go through folders in SubContentLevel1. - foreach ($Item in ($SubContentLevel1 | where folder)) { + foreach ($Item in ($SubContentLevel1 | Where-Object folder)) { $Parameters = @{ AccessToken = $AccessToken GraphMethod = 'GET' - GraphUri = "https://graph.microsoft.com/v1.0/drives/$($DocumentLibrary.id)/items/$($Item.id)/children" + GraphUri = "https://graph.microsoft.com/v1.0/drives/$($DocumentLibrary.id)/items/$($Item.id)/children" } $SubContentLevel2 = Invoke-DCMsGraphQuery @Parameters - $SubContentLevel2 | where file + $SubContentLevel2 | Where-Object file # Download files in sub SubContentLevel2. - foreach ($File in ($SubContentLevel2 | where file)) { + foreach ($File in ($SubContentLevel2 | Where-Object file)) { Write-Verbose -Verbose -Message "------ Downloading '$($File.Name)' ($([math]::round($File.Size/1MB, 2)) MB)..." $HeaderParams = @{ @@ -2466,18 +2491,18 @@ function Invoke-DCM365DataExfiltration { } # Go through folders in SubContentLevel2. - foreach ($Item in ($SubContentLevel2 | where folder)) { + foreach ($Item in ($SubContentLevel2 | Where-Object folder)) { $Parameters = @{ AccessToken = $AccessToken GraphMethod = 'GET' - GraphUri = "https://graph.microsoft.com/v1.0/drives/$($DocumentLibrary.id)/items/$($Item.id)/children" + GraphUri = "https://graph.microsoft.com/v1.0/drives/$($DocumentLibrary.id)/items/$($Item.id)/children" } $SubContentLevel3 = Invoke-DCMsGraphQuery @Parameters - $SubContentLevel3 | where file + $SubContentLevel3 | Where-Object file # Download files in sub SubContentLevel3. - foreach ($File in ($SubContentLevel3 | where file)) { + foreach ($File in ($SubContentLevel3 | Where-Object file)) { Write-Verbose -Verbose -Message "------ Downloading '$($File.Name)' ($([math]::round($File.Size/1MB, 2)) MB)..." $HeaderParams = @{ @@ -2496,7 +2521,7 @@ function Invoke-DCM365DataExfiltration { # Copy result to clipboard and exit. - $Files | Select-Object Name,size | Set-Clipboard + $Files | Select-Object Name, size | Set-Clipboard Write-Verbose -Verbose -Message "File list copied to clipboard!" Write-Verbose -Verbose -Message "All done!" } @@ -2585,9 +2610,9 @@ function Invoke-DCM365DataWiper { # Connect to Microsoft Graph with application credentials. Write-Verbose -Verbose -Message "Connecting to Microsoft Graph as Service Principal '$ClientID'..." $Parameters = @{ - ClientID = $ClientID + ClientID = $ClientID ClientSecret = $ClientSecret - TenantName = $TenantName + TenantName = $TenantName } $AccessToken = Connect-DCMsGraphAsApplication @Parameters @@ -2598,7 +2623,7 @@ function Invoke-DCM365DataWiper { $Parameters = @{ AccessToken = $AccessToken GraphMethod = 'GET' - GraphUri = "https://graph.microsoft.com/v1.0/groups?`$filter=groupTypes/any(c:c+eq+'Unified')&`$select=id,displayName,description" + GraphUri = "https://graph.microsoft.com/v1.0/groups?`$filter=groupTypes/any(c:c+eq+'Unified')&`$select=id,displayName,description" } $M365Groups = Invoke-DCMsGraphQuery @Parameters @@ -2611,7 +2636,7 @@ function Invoke-DCM365DataWiper { $Parameters = @{ AccessToken = $AccessToken GraphMethod = 'GET' - GraphUri = "https://graph.microsoft.com/v1.0/groups/$($Group.id)/drive?`$select=id,name,webUrl" + GraphUri = "https://graph.microsoft.com/v1.0/groups/$($Group.id)/drive?`$select=id,name,webUrl" } Invoke-DCMsGraphQuery @Parameters @@ -2626,20 +2651,20 @@ function Invoke-DCM365DataWiper { $Parameters = @{ AccessToken = $AccessToken GraphMethod = 'GET' - GraphUri = "https://graph.microsoft.com/v1.0/drives/$($DocumentLibrary.id)/root/children" + GraphUri = "https://graph.microsoft.com/v1.0/drives/$($DocumentLibrary.id)/root/children" } $RootContent = Invoke-DCMsGraphQuery @Parameters - $RootContent | where file + $RootContent | Where-Object file # Delete files in root directory. - foreach ($File in ($RootContent | where file)) { + foreach ($File in ($RootContent | Where-Object file)) { Write-Verbose -Verbose -Message "------ Deleting '$($File.Name)' ($([math]::round($File.Size/1MB, 2)) MB)..." $Parameters = @{ AccessToken = $AccessToken GraphMethod = 'DELETE' - GraphUri = "https://graph.microsoft.com/v1.0/drives/$($DocumentLibrary.id)/items/$($File.id)" + GraphUri = "https://graph.microsoft.com/v1.0/drives/$($DocumentLibrary.id)/items/$($File.id)" } if (!($WhatIf)) { @@ -2647,24 +2672,24 @@ function Invoke-DCM365DataWiper { } } - foreach ($Item in ($RootContent | where folder)) { + foreach ($Item in ($RootContent | Where-Object folder)) { $Parameters = @{ AccessToken = $AccessToken GraphMethod = 'GET' - GraphUri = "https://graph.microsoft.com/v1.0/drives/$($DocumentLibrary.id)/items/$($Item.id)/children" + GraphUri = "https://graph.microsoft.com/v1.0/drives/$($DocumentLibrary.id)/items/$($Item.id)/children" } $SubContentLevel1 = Invoke-DCMsGraphQuery @Parameters - $SubContentLevel1 | where file + $SubContentLevel1 | Where-Object file # Delete files in sub SubContentLevel1. - foreach ($File in ($SubContentLevel1 | where file)) { + foreach ($File in ($SubContentLevel1 | Where-Object file)) { Write-Verbose -Verbose -Message "------ Deleting '$($File.Name)' ($([math]::round($File.Size/1MB, 2)) MB)..." $Parameters = @{ AccessToken = $AccessToken GraphMethod = 'DELETE' - GraphUri = "https://graph.microsoft.com/v1.0/drives/$($DocumentLibrary.id)/items/$($File.id)" + GraphUri = "https://graph.microsoft.com/v1.0/drives/$($DocumentLibrary.id)/items/$($File.id)" } if (!($WhatIf)) { @@ -2673,24 +2698,24 @@ function Invoke-DCM365DataWiper { } # Go through folders in SubContentLevel1. - foreach ($Item in ($SubContentLevel1 | where folder)) { + foreach ($Item in ($SubContentLevel1 | Where-Object folder)) { $Parameters = @{ AccessToken = $AccessToken GraphMethod = 'GET' - GraphUri = "https://graph.microsoft.com/v1.0/drives/$($DocumentLibrary.id)/items/$($Item.id)/children" + GraphUri = "https://graph.microsoft.com/v1.0/drives/$($DocumentLibrary.id)/items/$($Item.id)/children" } $SubContentLevel2 = Invoke-DCMsGraphQuery @Parameters - $SubContentLevel2 | where file + $SubContentLevel2 | Where-Object file # Delete files in sub SubContentLevel2. - foreach ($File in ($SubContentLevel2 | where file)) { + foreach ($File in ($SubContentLevel2 | Where-Object file)) { Write-Verbose -Verbose -Message "------ Deleting '$($File.Name)' ($([math]::round($File.Size/1MB, 2)) MB)..." $Parameters = @{ AccessToken = $AccessToken GraphMethod = 'DELETE' - GraphUri = "https://graph.microsoft.com/v1.0/drives/$($DocumentLibrary.id)/items/$($File.id)" + GraphUri = "https://graph.microsoft.com/v1.0/drives/$($DocumentLibrary.id)/items/$($File.id)" } if (!($WhatIf)) { @@ -2699,24 +2724,24 @@ function Invoke-DCM365DataWiper { } # Go through folders in SubContentLevel2. - foreach ($Item in ($SubContentLevel2 | where folder)) { + foreach ($Item in ($SubContentLevel2 | Where-Object folder)) { $Parameters = @{ AccessToken = $AccessToken GraphMethod = 'GET' - GraphUri = "https://graph.microsoft.com/v1.0/drives/$($DocumentLibrary.id)/items/$($Item.id)/children" + GraphUri = "https://graph.microsoft.com/v1.0/drives/$($DocumentLibrary.id)/items/$($Item.id)/children" } $SubContentLevel3 = Invoke-DCMsGraphQuery @Parameters - $SubContentLevel3 | where file + $SubContentLevel3 | Where-Object file # Delete files in sub SubContentLevel3. - foreach ($File in ($SubContentLevel3 | where file)) { + foreach ($File in ($SubContentLevel3 | Where-Object file)) { Write-Verbose -Verbose -Message "------ Deleting '$($File.Name)' ($([math]::round($File.Size/1MB, 2)) MB)..." $Parameters = @{ AccessToken = $AccessToken GraphMethod = 'DELETE' - GraphUri = "https://graph.microsoft.com/v1.0/drives/$($DocumentLibrary.id)/items/$($File.id)" + GraphUri = "https://graph.microsoft.com/v1.0/drives/$($DocumentLibrary.id)/items/$($File.id)" } if (!($WhatIf)) { @@ -2730,7 +2755,7 @@ function Invoke-DCM365DataWiper { # Copy result to clipboard and exit. - $Files | Select-Object Name,size | Set-Clipboard + $Files | Select-Object Name, size | Set-Clipboard Write-Verbose -Verbose -Message "File list copied to clipboard!" Write-Verbose -Verbose -Message "All done!" } @@ -2849,14 +2874,15 @@ function Invoke-DCHuntingQuery { $Results = ($Results | ConvertFrom-Json).results - $Properties = @(($Results | Select-Object -First 1).PSObject.Properties | Where-Object { $_.Name -notlike "*@odata.type"}).Name + $Properties = @(($Results | Select-Object -First 1).PSObject.Properties | Where-Object { $_.Name -notlike "*@odata.type" }).Name $CountIsPresent = $false [string[]]$Properties = foreach ($Property in $Properties) { if ($Property -eq 'count_') { $CountIsPresent = $true - } else { + } + else { $Property } } @@ -2868,8 +2894,8 @@ function Invoke-DCHuntingQuery { $Results | Select-Object -Property $Properties if (!($Results)) { - Write-host '-- empty result --' - Write-host '' + Write-Host '-- empty result --' + Write-Host '' } } @@ -2955,21 +2981,21 @@ function New-DCEntraIDAppPermissionsReport { $APIPermissions = foreach ($ServicePrincipal in $ServicePrincipals) { $Permissions = (Invoke-MgGraphRequest -Method GET -Uri "https://graph.microsoft.com/v1.0/servicePrincipals/$($ServicePrincipal.Id)/appRoleAssignments" | ConvertTo-Json -Depth 10 | ConvertFrom-Json).value - $Id = ($Applications | where appId -eq $ServicePrincipal.appId).id + $Id = ($Applications | Where-Object appId -EQ $ServicePrincipal.appId).id $Owners = $null if ($Id) { $Owners = ((Invoke-MgGraphRequest -Method GET -Uri "https://graph.microsoft.com/v1.0/applications/$Id/owners" | ConvertTo-Json -Depth 10 | ConvertFrom-Json).value).userPrincipalName | Format-List | Out-String } - $publisherDomain = ($Applications | where appId -eq $ServicePrincipal.appId).publisherDomain + $publisherDomain = ($Applications | Where-Object appId -EQ $ServicePrincipal.appId).publisherDomain - $AppCertificates = ($Applications | where appId -eq $ServicePrincipal.appId).keyCredentials | Format-Table -Property displayName, startDateTime, endDateTime | Out-String + $AppCertificates = ($Applications | Where-Object appId -EQ $ServicePrincipal.appId).keyCredentials | Format-Table -Property displayName, startDateTime, endDateTime | Out-String - $AppSecrets = ($Applications | where appId -eq $ServicePrincipal.appId).passwordCredentials | Format-Table -Property displayName, startDateTime, endDateTime | Out-String + $AppSecrets = ($Applications | Where-Object appId -EQ $ServicePrincipal.appId).passwordCredentials | Format-Table -Property displayName, startDateTime, endDateTime | Out-String foreach ($Permission in $Permissions) { - $AppRole = $AppRoles | where Id -eq $Permission.appRoleId + $AppRole = $AppRoles | Where-Object Id -EQ $Permission.appRoleId $CustomObject = New-Object -TypeName psobject $CustomObject | Add-Member -MemberType NoteProperty -Name "Name" -Value $Permission.principalDisplayName @@ -3100,7 +3126,7 @@ function New-DCEntraIDStaleAccountReport { # Connect to Microsoft Graph with delegated credentials. $Parameters = @{ - ClientID = $ClientID + ClientID = $ClientID ClientSecret = $ClientSecret } @@ -3112,16 +3138,18 @@ function New-DCEntraIDStaleAccountReport { if ($OnlyMembers) { $GraphUri = "https://graph.microsoft.com/beta/users?select=displayName,userPrincipalName,userType,accountEnabled,onPremisesSyncEnabled,companyName,department,country,signInActivity,assignedLicenses&`$filter=userType eq 'Member'" - } elseif ($OnlyGuests) { + } + elseif ($OnlyGuests) { $GraphUri = "https://graph.microsoft.com/beta/users?select=displayName,userPrincipalName,userType,accountEnabled,onPremisesSyncEnabled,companyName,department,country,signInActivity,assignedLicenses&`$filter=userType eq 'Guest'" - } else { + } + else { $GraphUri = "https://graph.microsoft.com/beta/users?select=displayName,userPrincipalName,userType,accountEnabled,onPremisesSyncEnabled,companyName,department,country,signInActivity,assignedLicenses" } $Parameters = @{ AccessToken = $AccessToken GraphMethod = 'GET' - GraphUri = $GraphUri + GraphUri = $GraphUri } $Result = Invoke-DCMsGraphQuery @Parameters @@ -3132,13 +3160,15 @@ function New-DCEntraIDStaleAccountReport { # Compare sign in date against non-interactive sign-in date. try { $lastSignInDateTime = Get-Date -Date $User.signInActivity.lastSignInDateTime - } catch { + } + catch { $lastSignInDateTime = $null } try { $lastNonInteractiveSignInDateTime = Get-Date -Date $User.signInActivity.lastNonInteractiveSignInDateTime - } catch { + } + catch { $lastNonInteractiveSignInDateTime = $null } @@ -3146,7 +3176,8 @@ function New-DCEntraIDStaleAccountReport { if ($lastNonInteractiveSignInDateTime -gt $lastSignInDateTime) { $LastSignInActivity = $lastNonInteractiveSignInDateTime - } else { + } + else { $LastSignInActivity = $lastSignInDateTime } @@ -3160,7 +3191,7 @@ function New-DCEntraIDStaleAccountReport { $Parameters = @{ AccessToken = $AccessToken GraphMethod = 'GET' - GraphUri = $GraphUri + GraphUri = $GraphUri } $Groups = Invoke-DCMsGraphQuery @Parameters @@ -3168,7 +3199,8 @@ function New-DCEntraIDStaleAccountReport { $MemberOf = foreach ($Group in $Groups) { if ($Groups.count -gt 1) { "$($Group.displayName)" - } else { + } + else { "$($Group.displayName; )" } } @@ -3189,7 +3221,8 @@ function New-DCEntraIDStaleAccountReport { if ($User.assignedLicenses.skuId) { $CustomObject | Add-Member -MemberType NoteProperty -Name "assignedLicenses" -Value $true - } else { + } + else { $CustomObject | Add-Member -MemberType NoteProperty -Name "assignedLicenses" -Value $false } @@ -3324,7 +3357,8 @@ function Get-DCConditionalAccessPolicies { } $Result | Format-List - } elseif ($NamesOnly) { + } + elseif ($NamesOnly) { $Result = foreach ($Policy in $ExistingPolicies) { if ($Policy.DisplayName.StartsWith($PrefixFilter)) { $CustomObject = New-Object -TypeName psobject @@ -3334,7 +3368,8 @@ function Get-DCConditionalAccessPolicies { } $Result - } else { + } + else { $Result = foreach ($Policy in $ExistingPolicies) { if ($Policy.DisplayName.StartsWith($PrefixFilter)) { $CustomObject = New-Object -TypeName psobject @@ -3354,7 +3389,8 @@ function Get-DCConditionalAccessPolicies { if ($ShowTargetResources) { $Result | Format-List - } else { + } + else { $Result | Format-Table } } @@ -3430,40 +3466,44 @@ function Remove-DCConditionalAccessPolicies { # Prompt for confirmation: if ($PrefixFilter -ne '') { - $title = 'Confirm' + $title = 'Confirm' $question = "Do you want to remove all Conditional Access policies with prefix '$PrefixFilter' in tenant '$(((Get-MgContext).Account.Split('@'))[1] )'? WARNING: ALL THESE POLICIES WILL BE DELETED!!" - $choices = '&Yes', '&No' + $choices = '&Yes', '&No' $decision = $Host.UI.PromptForChoice($title, $question, $choices, 1) if ($decision -eq 0) { Write-Host "" - } else { + } + else { return } - } else { - $title = 'Confirm' + } + else { + $title = 'Confirm' $question = "Do you want to remove all Conditional Access policies in tenant '$(((Get-MgContext).Account.Split('@'))[1] )'? WARNING: ALL POLICIES WILL BE DELETED!!" - $choices = '&Yes', '&No' + $choices = '&Yes', '&No' $decision = $Host.UI.PromptForChoice($title, $question, $choices, 1) if ($decision -eq 0) { Write-Host "" - } else { + } + else { return } } # Prompt for confirmation: - $title = 'Confirm' + $title = 'Confirm' $question = "ARE YOU REALLY REALLY SURE?" - $choices = '&Yes', '&No' + $choices = '&Yes', '&No' $decision = $Host.UI.PromptForChoice($title, $question, $choices, 1) if ($decision -eq 0) { Write-Host "" Write-Verbose -Verbose -Message "Starting deletion..." - } else { + } + else { return } @@ -3560,26 +3600,29 @@ function Rename-DCConditionalAccessPolicies { if ($PrefixFilter -eq '') { # Prompt for confirmation: - $title = 'Confirm' + $title = 'Confirm' $question = "Do you want to add prefix '$AddCustomPrefix' to ALL Conditional Access policies in tenant '$(((Get-MgContext).Account.Split('@'))[1] )'?" - $choices = '&Yes', '&No' + $choices = '&Yes', '&No' $decision = $Host.UI.PromptForChoice($title, $question, $choices, 1) if ($decision -eq 0) { Write-Host "" - } else { + } + else { return } - } else { + } + else { # Prompt for confirmation: - $title = 'Confirm' + $title = 'Confirm' $question = "Do you want to rename all Conditional Access policies with prefix '$PrefixFilter' in tenant '$(((Get-MgContext).Account.Split('@'))[1] )' to '$AddCustomPrefix'?" - $choices = '&Yes', '&No' + $choices = '&Yes', '&No' $decision = $Host.UI.PromptForChoice($title, $question, $choices, 1) if ($decision -eq 0) { Write-Host "" - } else { + } + else { return } } @@ -3604,7 +3647,8 @@ function Rename-DCConditionalAccessPolicies { Update-MgIdentityConditionalAccessPolicy -ConditionalAccessPolicyId $Policy.Id -BodyParameter $params Start-Sleep -Seconds 1 - } else { + } + else { Write-Verbose -Verbose -Message "Renaming '$($Policy.DisplayName)' to '$($Policy.DisplayName -replace $PrefixFilter, $AddCustomPrefix)'..." # Rename policy: @@ -3849,27 +3893,30 @@ function Deploy-DCConditionalAccessBaselinePoC { # Prompt for confirmation: if ($SkipReportOnlyMode) { - $title = 'Confirm' + $title = 'Confirm' $question = "Do you want to deploy the Conditional Access baseline PoC (production mode) in tenant '$(((Get-MgContext).Account.Split('@'))[1] )'? WARNING: ALL POLICIES will go live for ALL USERS! Remove -SkipReportOnlyMode to deploy in report-only mode instead." - $choices = '&Yes', '&No' + $choices = '&Yes', '&No' $decision = $Host.UI.PromptForChoice($title, $question, $choices, 1) if ($decision -eq 0) { Write-Host "" Write-Verbose -Verbose -Message "Starting deployment..." - } else { + } + else { return } - } else { - $title = 'Confirm' + } + else { + $title = 'Confirm' $question = "Do you want to deploy the Conditional Access baseline PoC (report-only) in tenant '$(((Get-MgContext).Account.Split('@'))[1] )'?" - $choices = '&Yes', '&No' + $choices = '&Yes', '&No' $decision = $Host.UI.PromptForChoice($title, $question, $choices, 0) if ($decision -eq 0) { Write-Host "" Write-Verbose -Verbose -Message "Starting deployment..." - } else { + } + else { return } } @@ -3884,10 +3931,11 @@ function Deploy-DCConditionalAccessBaselinePoC { if ($ExistingExcludeGroup) { Write-Verbose -Verbose -Message "The group '$ExcludeGroupDisplayName' already exists!" - } else { + } + else { # Create group if none existed. Write-Verbose -Verbose -Message "Could not find '$ExcludeGroupDisplayName'. Creating group..." - $ExistingExcludeGroup = New-MgGroup -DisplayName $ExcludeGroupDisplayName -MailNickName $($ExcludeGroupDisplayName.Replace(' ', '_')) -MailEnabled:$False -SecurityEnable -IsAssignableToRole + $ExistingExcludeGroup = New-MgGroup -DisplayName $ExcludeGroupDisplayName -MailNickname $($ExcludeGroupDisplayName.Replace(' ', '_')) -MailEnabled:$False -SecurityEnable -IsAssignableToRole # Sleep for 5 seconds. Start-Sleep -Seconds 5 @@ -3907,10 +3955,11 @@ function Deploy-DCConditionalAccessBaselinePoC { if ($ExistingServiceAccountGroup) { Write-Verbose -Verbose -Message "The group '$ServiceAccountGroupDisplayName' already exists!" - } else { + } + else { # Create group if none existed. Write-Verbose -Verbose -Message "Could not find '$ServiceAccountGroupDisplayName'. Creating group..." - $ExistingServiceAccountGroup = New-MgGroup -DisplayName $ServiceAccountGroupDisplayName -MailNickName $($ServiceAccountGroupDisplayName.Replace(' ', '_')) -MailEnabled:$False -SecurityEnable -IsAssignableToRole + $ExistingServiceAccountGroup = New-MgGroup -DisplayName $ServiceAccountGroupDisplayName -MailNickname $($ServiceAccountGroupDisplayName.Replace(' ', '_')) -MailEnabled:$False -SecurityEnable -IsAssignableToRole } @@ -3922,7 +3971,8 @@ function Deploy-DCConditionalAccessBaselinePoC { if ($ExistingCorpNetworkNamedLocation) { Write-Verbose -Verbose -Message "The named location '$NamedLocationCorpNetwork' already exists!" - } else { + } + else { # Create named location if none existed. Write-Verbose -Verbose -Message "Could not find '$NamedLocationCorpNetwork'. Creating named location..." @@ -3930,15 +3980,15 @@ function Deploy-DCConditionalAccessBaselinePoC { $PublicIp = (Get-DCPublicIp).ip $params = @{ - "@odata.type" = "#microsoft.graph.ipNamedLocation" - DisplayName = "$NamedLocationCorpNetwork" - IsTrusted = $true - IpRanges = @( - @{ - "@odata.type" = "#microsoft.graph.iPv4CidrRange" - CidrAddress = "$PublicIp/32" - } - ) + "@odata.type" = "#microsoft.graph.ipNamedLocation" + DisplayName = "$NamedLocationCorpNetwork" + IsTrusted = $true + IpRanges = @( + @{ + "@odata.type" = "#microsoft.graph.iPv4CidrRange" + CidrAddress = "$PublicIp/32" + } + ) } $ExistingCorpNetworkNamedLocation = New-MgIdentityConditionalAccessNamedLocation -BodyParameter $params @@ -3953,14 +4003,15 @@ function Deploy-DCConditionalAccessBaselinePoC { if ($ExistingNamedLocationAllowedCountries) { Write-Verbose -Verbose -Message "The named location '$NamedLocationAllowedCountries' already exists!" - } else { + } + else { # Create named location if none existed. Write-Verbose -Verbose -Message "Could not find '$NamedLocationAllowedCountries'. Creating named location..." $params = @{ - "@odata.type" = "#microsoft.graph.countryNamedLocation" - DisplayName = "$NamedLocationAllowedCountries" - CountriesAndRegions = @( + "@odata.type" = "#microsoft.graph.countryNamedLocation" + DisplayName = "$NamedLocationAllowedCountries" + CountriesAndRegions = @( "SE" "US" ) @@ -3976,13 +4027,15 @@ function Deploy-DCConditionalAccessBaselinePoC { # Check for existing Terms of Use. if ($SkipPolicies -eq 'GLOBAL - GRANT - Terms of Use') { Write-Verbose -Verbose -Message "Skipping Terms of Use because -SkipPolicies was set!" - } else { + } + else { Write-Verbose -Verbose -Message "Checking for existing Terms of Use '$TermsOfUseName'..." - $ExistingTermsOfUse = Get-MgAgreement | where DisplayName -eq $TermsOfUseName | Select-Object -Last 1 + $ExistingTermsOfUse = Get-MgAgreement | Where-Object DisplayName -EQ $TermsOfUseName | Select-Object -Last 1 if ($ExistingTermsOfUse) { Write-Verbose -Verbose -Message "The Terms of Use '$TermsOfUseName' already exists!" - } else { + } + else { # Create Terms of Use if none existed. Write-Verbose -Verbose -Message "Could not find '$TermsOfUseName'. Creating Terms of Use..." @@ -3990,7 +4043,7 @@ function Deploy-DCConditionalAccessBaselinePoC { Write-Verbose -Verbose -Message "Downloading Terms of Use template from https://danielchronlund.com..." Invoke-WebRequest 'https://danielchronlundcloudtechblog.files.wordpress.com/2023/09/termsofuse.pdf' -OutFile 'termsofuse.pdf' - $fileContent = get-content -Raw 'termsofuse.pdf' + $fileContent = Get-Content -Raw 'termsofuse.pdf' $fileContentBytes = [System.Text.Encoding]::Default.GetBytes($fileContent) $fileContentEncoded = [System.Convert]::ToBase64String($fileContentBytes) @@ -4034,7 +4087,8 @@ function Deploy-DCConditionalAccessBaselinePoC { # Report-only mode. if (!($SkipReportOnlyMode)) { $JSONContent = $JSONContent -replace '"enabled"', '"enabledForReportingButNotEnforced"' - } else { + } + else { $JSONContent = $JSONContent -replace '"disabled"', '"enabled"' } @@ -4056,7 +4110,8 @@ function Deploy-DCConditionalAccessBaselinePoC { foreach ($Policy in $ConditionalAccessPolicies) { if ($SkipPolicies -contains $Policy.DisplayName) { Write-Verbose -Verbose -Message "Skipping '$($Policy.DisplayName)'!" - } else { + } + else { Start-Sleep -Seconds 1 Write-Verbose -Verbose -Message "Creating '$($Policy.DisplayName)'..." @@ -4324,27 +4379,30 @@ function Import-DCConditionalAccessPolicyDesign { # Prompt for confirmation: if ($SkipReportOnlyMode) { - $title = 'Confirm' + $title = 'Confirm' $question = "Do you want to import the Conditional Access policies from JSON file '$FilePath' in tenant '$(((Get-MgContext).Account.Split('@'))[1] )'? WARNING: ALL POLICIES will go live for ALL USERS! Remove -SkipReportOnlyMode to deploy in report-only mode instead." - $choices = '&Yes', '&No' + $choices = '&Yes', '&No' $decision = $Host.UI.PromptForChoice($title, $question, $choices, 1) if ($decision -eq 0) { Write-Host "" Write-Verbose -Verbose -Message "Starting deployment..." - } else { + } + else { return } - } else { - $title = 'Confirm' + } + else { + $title = 'Confirm' $question = "Do you want to import the Conditional Access policies from JSON file '$FilePath' in report-only mode in tenant '$(((Get-MgContext).Account.Split('@'))[1] )'?" - $choices = '&Yes', '&No' + $choices = '&Yes', '&No' $decision = $Host.UI.PromptForChoice($title, $question, $choices, 0) if ($decision -eq 0) { Write-Host "" Write-Verbose -Verbose -Message "Starting deployment..." - } else { + } + else { return } } @@ -4494,10 +4552,11 @@ function Set-DCConditionalAccessPoliciesPilotMode { # Parameter check: - if ($EnablePilot -and $EnableProduction) { + if ($EnablePilot -and $EnableProduction) { Write-Error -Message 'You can''t use -EnablePilot and -EnableProduction at the same time!' return - } elseif (!($EnablePilot) -and !($EnableProduction)) { + } + elseif (!($EnablePilot) -and !($EnableProduction)) { Write-Error -Message 'You must use -EnablePilot or -EnableProduction!' return } @@ -4505,26 +4564,29 @@ function Set-DCConditionalAccessPoliciesPilotMode { if ($EnableProduction) { # Prompt for confirmation: - $title = 'Confirm' + $title = 'Confirm' $question = "Do you want to switch all Conditional Access policies with prefix '$PrefixFilter' in tenant '$(((Get-MgContext).Account.Split('@'))[1] )' from pilot group '$PilotGroupName' to 'All users'?" - $choices = '&Yes', '&No' + $choices = '&Yes', '&No' $decision = $Host.UI.PromptForChoice($title, $question, $choices, 1) if ($decision -eq 0) { Write-Host "" - } else { + } + else { return } - } else { + } + else { # Prompt for confirmation: - $title = 'Confirm' + $title = 'Confirm' $question = "Do you want to switch all Conditional Access policies with prefix '$PrefixFilter' in tenant '$(((Get-MgContext).Account.Split('@'))[1] )' from 'All users' to pilot group '$PilotGroupName'?" - $choices = '&Yes', '&No' + $choices = '&Yes', '&No' $decision = $Host.UI.PromptForChoice($title, $question, $choices, 1) if ($decision -eq 0) { Write-Host "" - } else { + } + else { return } } @@ -4536,7 +4598,8 @@ function Set-DCConditionalAccessPoliciesPilotMode { if ($ExistingPilotGroup) { Write-Verbose -Verbose -Message "Found group '$PilotGroupName'!" - } else { + } + else { Write-Error -Message "Could not find group '$PilotGroupName'!" return } @@ -4569,7 +4632,8 @@ function Set-DCConditionalAccessPoliciesPilotMode { Start-Sleep -Seconds 1 } - } elseif ($EnablePilot) { + } + elseif ($EnablePilot) { if ($Policy.Conditions.Users.IncludeUsers -eq 'All') { Write-Verbose -Verbose -Message "Toggling '$($Policy.DisplayName)' to pilot group..." @@ -4577,7 +4641,7 @@ function Set-DCConditionalAccessPoliciesPilotMode { $params = @{ Conditions = @{ Users = @{ - IncludeUsers = @( + IncludeUsers = @( "None" ) IncludeGroups = @( @@ -4677,10 +4741,11 @@ function Set-DCConditionalAccessPoliciesReportOnlyMode { # Parameter check: - if ($SetToReportOnly -and $SetToEnabled) { + if ($SetToReportOnly -and $SetToEnabled) { Write-Error -Message 'You can''t use -SetToReportOnly and -SetToEnabled at the same time!' return - } elseif (!($SetToReportOnly) -and !($SetToEnabled)) { + } + elseif (!($SetToReportOnly) -and !($SetToEnabled)) { Write-Error -Message 'You must use -SetToReportOnly or -SetToEnabled!' return } @@ -4688,26 +4753,29 @@ function Set-DCConditionalAccessPoliciesReportOnlyMode { if ($SetToEnabled) { # Prompt for confirmation: - $title = 'Confirm' + $title = 'Confirm' $question = "Do you want to switch all Conditional Access policies with prefix '$PrefixFilter' in tenant '$(((Get-MgContext).Account.Split('@'))[1] )' from Report-only to Enabled?" - $choices = '&Yes', '&No' + $choices = '&Yes', '&No' $decision = $Host.UI.PromptForChoice($title, $question, $choices, 1) if ($decision -eq 0) { Write-Host "" - } else { + } + else { return } - } else { + } + else { # Prompt for confirmation: - $title = 'Confirm' + $title = 'Confirm' $question = "Do you want to switch all Conditional Access policies with prefix '$PrefixFilter' in tenant '$(((Get-MgContext).Account.Split('@'))[1] )' from Enabled to Report-only?" - $choices = '&Yes', '&No' + $choices = '&Yes', '&No' $decision = $Host.UI.PromptForChoice($title, $question, $choices, 1) if ($decision -eq 0) { Write-Host "" - } else { + } + else { return } } @@ -4734,7 +4802,8 @@ function Set-DCConditionalAccessPoliciesReportOnlyMode { Start-Sleep -Seconds 1 } - } elseif ($SetToReportOnly) { + } + elseif ($SetToReportOnly) { if ($Policy.State -eq 'Enabled') { Write-Verbose -Verbose -Message "Toggling '$($Policy.DisplayName)' to Report-only..." @@ -4824,14 +4893,15 @@ function Add-DCConditionalAccessPoliciesBreakGlassGroup { # Prompt for confirmation: - $title = 'Confirm' + $title = 'Confirm' $question = "Do you want to exclude the group '$($ExcludeGroupName)' from all Conditional Access policies with prefix '$PrefixFilter' in tenant '$(((Get-MgContext).Account.Split('@'))[1] )'?" - $choices = '&Yes', '&No' + $choices = '&Yes', '&No' $decision = $Host.UI.PromptForChoice($title, $question, $choices, 0) if ($decision -eq 0) { Write-Host "" - } else { + } + else { return } @@ -4842,7 +4912,8 @@ function Add-DCConditionalAccessPoliciesBreakGlassGroup { if ($ExistingExcludeGroup) { Write-Verbose -Verbose -Message "Found group '$ExcludeGroupName'!" - } else { + } + else { Write-Error -Message "Could not find group '$ExcludeGroupName'!" return } @@ -4992,8 +5063,8 @@ function Invoke-DCConditionalAccessSimulation { [switch]$TrustedIPAddress, [parameter(Mandatory = $false)] - [ValidateLength(2,2)] - [string]$Country = ((Get-DCPublicIP).country), + [ValidateLength(2, 2)] + [string]$Country = ((Get-DCPublicIp).country), [parameter(Mandatory = $false)] [ValidateSet('all', 'android', 'iOS', 'windows', 'windowsPhone', 'macOS', 'linux', 'spaceRocket')] @@ -5037,7 +5108,8 @@ function Invoke-DCConditionalAccessSimulation { if ($UserPrincipalName -ne 'GuestsOrExternalUsers') { $UserPrincipalName = 'All' } - } else { + } + else { # Check Microsoft Graph PowerShell module. Install-DCMicrosoftGraphPowerShellModule -Verbose @@ -5064,10 +5136,12 @@ function Invoke-DCConditionalAccessSimulation { if ($UserId) { $CustomObject | Add-Member -MemberType NoteProperty -Name "UserId" -Value $UserId - } else { + } + else { if ($UserPrincipalName -eq 'GuestsOrExternalUsers') { $CustomObject | Add-Member -MemberType NoteProperty -Name "UserId" -Value 'GuestsOrExternalUsers' - } else { + } + else { $CustomObject | Add-Member -MemberType NoteProperty -Name "UserId" -Value 'All' } } @@ -5079,7 +5153,8 @@ function Invoke-DCConditionalAccessSimulation { if ($UserId) { $Groups = (Get-MgUserTransitiveMemberOf -UserId $UserId).Id $CustomObject | Add-Member -MemberType NoteProperty -Name "Groups" -Value $Groups - } else { + } + else { $CustomObject | Add-Member -MemberType NoteProperty -Name "Groups" -Value $null } @@ -5088,12 +5163,15 @@ function Invoke-DCConditionalAccessSimulation { $AppId = $null if ($ApplicationDisplayName -eq 'All') { $AppId = 'All' - } elseif ($ApplicationDisplayName -eq 'Office 365') { + } + elseif ($ApplicationDisplayName -eq 'Office 365') { $AppId = 'Office365' - } elseif ($ApplicationDisplayName -eq 'Microsoft Admin Portals') { + } + elseif ($ApplicationDisplayName -eq 'Microsoft Admin Portals') { $AppId = 'MicrosoftAdminPortals' - } else { - $AppId = (Get-MGServicePrincipal -Filter "DisplayName eq '$ApplicationDisplayName'").AppId + } + else { + $AppId = (Get-MgServicePrincipal -Filter "DisplayName eq '$ApplicationDisplayName'").AppId } $CustomObject | Add-Member -MemberType NoteProperty -Name "Application" -Value $AppId @@ -5148,14 +5226,16 @@ function Invoke-DCConditionalAccessSimulation { try { if ($GrantControls.authenticationStrength.id) { $GrantControls.authenticationStrength = $true - } else { + } + else { $GrantControls.authenticationStrength = $false } $GrantControls = $GrantControls | ConvertTo-Json -Depth 10 $CustomObject | Add-Member -MemberType NoteProperty -Name "GrantControls" -Value $GrantControls - } catch { + } + catch { $CustomObject | Add-Member -MemberType NoteProperty -Name "GrantControls" -Value $GrantControls } @@ -5170,7 +5250,8 @@ function Invoke-DCConditionalAccessSimulation { #Enabled if ($Policy.State -eq 'enabled') { if ($VerbosePolicyEvaluation) { Write-Verbose -Verbose -Message 'Enabled: APPLIED' } - } else { + } + else { if ($VerbosePolicyEvaluation) { Write-Verbose -Verbose -Message 'Enabled: NOT APPLIED' } $PolicyMatch = $false } @@ -5183,7 +5264,8 @@ function Invoke-DCConditionalAccessSimulation { if ($Policy.Conditions.Applications.ExcludeApplications -contains $ConditionsToSimulate.Application) { if ($VerbosePolicyEvaluation) { Write-Verbose -Verbose -Message 'ExcludeApplications: NOT APPLIED' } $PolicyMatch = $false - } else { + } + else { if ($VerbosePolicyEvaluation) { Write-Verbose -Verbose -Message 'ExcludeApplications: APPLIED' } } @@ -5191,13 +5273,16 @@ function Invoke-DCConditionalAccessSimulation { #IncludeApplications if ($Policy.Conditions.Applications.IncludeApplications -eq 'All') { if ($VerbosePolicyEvaluation) { Write-Verbose -Verbose -Message 'IncludeApplications: APPLIED' } - } elseif ($Policy.Conditions.Applications.IncludeApplications -eq 'none') { + } + elseif ($Policy.Conditions.Applications.IncludeApplications -eq 'none') { if ($VerbosePolicyEvaluation) { Write-Verbose -Verbose -Message 'IncludeApplications: NOT APPLIED' } $PolicyMatch = $false - } elseif ($Policy.Conditions.Applications.IncludeApplications -notcontains $ConditionsToSimulate.Application) { + } + elseif ($Policy.Conditions.Applications.IncludeApplications -notcontains $ConditionsToSimulate.Application) { if ($VerbosePolicyEvaluation) { Write-Verbose -Verbose -Message 'IncludeApplications: NOT APPLIED' } $PolicyMatch = $false - } else { + } + else { if ($VerbosePolicyEvaluation) { Write-Verbose -Verbose -Message 'IncludeApplications: APPLIED' } } @@ -5209,10 +5294,12 @@ function Invoke-DCConditionalAccessSimulation { #ClientAppTypes if ($Policy.Conditions.ClientAppTypes -eq 'all') { if ($VerbosePolicyEvaluation) { Write-Verbose -Verbose -Message 'ClientAppTypes: APPLIED' } - } elseif ($Policy.Conditions.ClientAppTypes -notcontains $ConditionsToSimulate.ClientApp) { + } + elseif ($Policy.Conditions.ClientAppTypes -notcontains $ConditionsToSimulate.ClientApp) { if ($VerbosePolicyEvaluation) { Write-Verbose -Verbose -Message 'ClientAppTypes: NOT APPLIED' } $PolicyMatch = $false - } else { + } + else { if ($VerbosePolicyEvaluation) { Write-Verbose -Verbose -Message 'ClientAppTypes: APPLIED' } } @@ -5225,21 +5312,24 @@ function Invoke-DCConditionalAccessSimulation { if ($ConditionsToSimulate.TrustedIPAddress) { $TrustedLocation = foreach ($Location in $Policy.Conditions.Locations.ExcludeLocations) { if (!($JSONFile)) { - (Get-MgIdentityConditionalAccessNamedLocation | where id -eq $Location).AdditionalProperties.isTrusted + (Get-MgIdentityConditionalAccessNamedLocation | Where-Object id -EQ $Location).AdditionalProperties.isTrusted } } if ($TrustedLocation) { if ($VerbosePolicyEvaluation) { Write-Verbose -Verbose -Message 'ExcludeLocationsIPAddress: NOT APPLIED' } $PolicyMatch = $false - } else { + } + else { if ($JSONFile) { if ($VerbosePolicyEvaluation) { Write-Verbose -Verbose -Message 'ExcludeLocationsIPAddress: APPLIED (JSON mode assumes not excluded)' } - } else { + } + else { if ($VerbosePolicyEvaluation) { Write-Verbose -Verbose -Message 'ExcludeLocationsIPAddress: APPLIED' } } } - } else { + } + else { if ($VerbosePolicyEvaluation) { Write-Verbose -Verbose -Message 'ExcludeLocationsIPAddress: APPLIED' } } @@ -5247,18 +5337,20 @@ function Invoke-DCConditionalAccessSimulation { #ExcludeLocationsCountry $TrustedLocation = foreach ($Location in $Policy.Conditions.Locations.ExcludeLocations) { if (!($JSONFile)) { - (Get-MgIdentityConditionalAccessNamedLocation | where id -eq $Location).AdditionalProperties.countriesAndRegions + (Get-MgIdentityConditionalAccessNamedLocation | Where-Object id -EQ $Location).AdditionalProperties.countriesAndRegions } } if ($TrustedLocation -contains $ConditionsToSimulate.Country) { if ($VerbosePolicyEvaluation) { Write-Verbose -Verbose -Message 'ExcludeLocationsCountry: NOT APPLIED' } $PolicyMatch = $false - } else { + } + else { if ($JSONFile -and $Policy.Conditions.Locations.ExcludeLocations) { if ($VerbosePolicyEvaluation) { Write-Verbose -Verbose -Message 'ExcludeLocationsCountry: NOT APPLIED (JSON mode assumes excluded)' } $PolicyMatch = $false - } else { + } + else { if ($VerbosePolicyEvaluation) { Write-Verbose -Verbose -Message 'ExcludeLocationsCountry: APPLIED' } } } @@ -5268,14 +5360,17 @@ function Invoke-DCConditionalAccessSimulation { $IncludeLocationsIPAddressMatch = $true if ($Policy.Conditions.Locations.IncludeLocations -eq $null) { if ($VerbosePolicyEvaluation) { Write-Verbose -Verbose -Message 'IncludeLocationsIPAddress: APPLIED' } - } elseif ($Policy.Conditions.Locations.IncludeLocations -eq 'All') { + } + elseif ($Policy.Conditions.Locations.IncludeLocations -eq 'All') { if ($VerbosePolicyEvaluation) { Write-Verbose -Verbose -Message 'IncludeLocationsIPAddress: APPLIED' } - } elseif ($Policy.Conditions.Locations.IncludeLocations -eq 'AllTrusted' -and $ConditionsToSimulate.TrustedIPAddress) { + } + elseif ($Policy.Conditions.Locations.IncludeLocations -eq 'AllTrusted' -and $ConditionsToSimulate.TrustedIPAddress) { if ($VerbosePolicyEvaluation) { Write-Verbose -Verbose -Message 'IncludeLocationsIPAddress: APPLIED' } - } else { + } + else { $TrustedLocation = foreach ($Location in $Policy.Conditions.Locations.IncludeLocations) { if (!($JSONFile)) { - (Get-MgIdentityConditionalAccessNamedLocation | where id -eq $Location).AdditionalProperties.isTrusted + (Get-MgIdentityConditionalAccessNamedLocation | Where-Object id -EQ $Location).AdditionalProperties.isTrusted } } @@ -5283,10 +5378,12 @@ function Invoke-DCConditionalAccessSimulation { if ($TrustedLocation -and $ConditionsToSimulate.TrustedIPAddress) { if ($VerbosePolicyEvaluation) { Write-Verbose -Verbose -Message 'IncludeLocationsIPAddress: APPLIED' } - } else { + } + else { if ($JSONFile) { if ($VerbosePolicyEvaluation) { Write-Verbose -Verbose -Message 'IncludeLocationsIPAddress: APPLIED (JSON mode assumes included)' } - } else { + } + else { if ($VerbosePolicyEvaluation) { Write-Verbose -Verbose -Message 'IncludeLocationsIPAddress: NOT APPLIED' } $IncludeLocationsIPAddressMatch = $false } @@ -5298,23 +5395,28 @@ function Invoke-DCConditionalAccessSimulation { $IncludeLocationsCountryMatch = $true if ($Policy.Conditions.Locations.IncludeLocations -eq $null) { if ($VerbosePolicyEvaluation) { Write-Verbose -Verbose -Message 'IncludeLocationsCountry: APPLIED' } - } elseif ($Policy.Conditions.Locations.IncludeLocations -eq 'All') { + } + elseif ($Policy.Conditions.Locations.IncludeLocations -eq 'All') { if ($VerbosePolicyEvaluation) { Write-Verbose -Verbose -Message 'IncludeLocationsCountry: APPLIED' } - } elseif ($Policy.Conditions.Locations.IncludeLocations -eq 'AllTrusted') { + } + elseif ($Policy.Conditions.Locations.IncludeLocations -eq 'AllTrusted') { if ($VerbosePolicyEvaluation) { Write-Verbose -Verbose -Message 'IncludeLocationsCountry: APPLIED' } - } else { + } + else { $TrustedLocation = foreach ($Location in $Policy.Conditions.Locations.IncludeLocations) { if (!($JSONFile)) { - (Get-MgIdentityConditionalAccessNamedLocation | where id -eq $Location).AdditionalProperties.countriesAndRegions + (Get-MgIdentityConditionalAccessNamedLocation | Where-Object id -EQ $Location).AdditionalProperties.countriesAndRegions } } if ($TrustedLocation -contains $ConditionsToSimulate.Country) { if ($VerbosePolicyEvaluation) { Write-Verbose -Verbose -Message 'IncludeLocationsCountry: APPLIED' } - } else { + } + else { if ($JSONFile) { if ($VerbosePolicyEvaluation) { Write-Verbose -Verbose -Message 'IncludeLocationsCountry: APPLIED (JSON mode assumes included)' } - } else { + } + else { if ($VerbosePolicyEvaluation) { Write-Verbose -Verbose -Message 'IncludeLocationsCountry: NOT APPLIED' } $IncludeLocationsCountryMatch = $false } @@ -5329,13 +5431,16 @@ function Invoke-DCConditionalAccessSimulation { #ExcludePlatforms if (($Policy.Conditions.Platforms.ExcludePlatforms).Count -eq 0) { if ($VerbosePolicyEvaluation) { Write-Verbose -Verbose -Message 'ExcludePlatforms: APPLIED' } - } elseif ($Policy.Conditions.Platforms.ExcludePlatforms -eq 'all') { + } + elseif ($Policy.Conditions.Platforms.ExcludePlatforms -eq 'all') { if ($VerbosePolicyEvaluation) { Write-Verbose -Verbose -Message 'ExcludePlatforms: NOT APPLIED' } $PolicyMatch = $false - } elseif ($Policy.Conditions.Platforms.ExcludePlatforms -contains $ConditionsToSimulate.Platform) { + } + elseif ($Policy.Conditions.Platforms.ExcludePlatforms -contains $ConditionsToSimulate.Platform) { if ($VerbosePolicyEvaluation) { Write-Verbose -Verbose -Message 'ExcludePlatforms: NOT APPLIED' } $PolicyMatch = $false - } else { + } + else { if ($VerbosePolicyEvaluation) { Write-Verbose -Verbose -Message 'ExcludePlatforms: APPLIED' } } @@ -5343,11 +5448,14 @@ function Invoke-DCConditionalAccessSimulation { #IncludePlatforms if (($Policy.Conditions.Platforms.IncludePlatforms).Count -eq 0) { if ($VerbosePolicyEvaluation) { Write-Verbose -Verbose -Message 'IncludePlatforms: APPLIED' } - } elseif ($Policy.Conditions.Platforms.IncludePlatforms -eq 'all') { + } + elseif ($Policy.Conditions.Platforms.IncludePlatforms -eq 'all') { if ($VerbosePolicyEvaluation) { Write-Verbose -Verbose -Message 'IncludePlatforms: APPLIED' } - } elseif ($Policy.Conditions.Platforms.IncludePlatforms -contains $ConditionsToSimulate.Platform) { + } + elseif ($Policy.Conditions.Platforms.IncludePlatforms -contains $ConditionsToSimulate.Platform) { if ($VerbosePolicyEvaluation) { Write-Verbose -Verbose -Message 'IncludePlatforms: APPLIED' } - } else { + } + else { if ($VerbosePolicyEvaluation) { Write-Verbose -Verbose -Message 'IncludePlatforms: NOT APPLIED' } $PolicyMatch = $false } @@ -5356,7 +5464,8 @@ function Invoke-DCConditionalAccessSimulation { #SignInRiskLevels if (($Policy.Conditions.SignInRiskLevels).Count -eq 0) { if ($VerbosePolicyEvaluation) { Write-Verbose -Verbose -Message 'SignInRiskLevels: APPLIED' } - } elseif ($Policy.Conditions.SignInRiskLevels -notcontains $ConditionsToSimulate.SignInRiskLevel) { + } + elseif ($Policy.Conditions.SignInRiskLevels -notcontains $ConditionsToSimulate.SignInRiskLevel) { if ($VerbosePolicyEvaluation) { Write-Verbose -Verbose -Message 'SignInRiskLevels: NOT APPLIED' } $PolicyMatch = $false } @@ -5365,7 +5474,8 @@ function Invoke-DCConditionalAccessSimulation { #UserRiskLevels if (($Policy.Conditions.UserRiskLevels).Count -eq 0) { if ($VerbosePolicyEvaluation) { Write-Verbose -Verbose -Message 'UserRiskLevels: APPLIED' } - } elseif ($Policy.Conditions.UserRiskLevels -notcontains $ConditionsToSimulate.UserRiskLevel) { + } + elseif ($Policy.Conditions.UserRiskLevels -notcontains $ConditionsToSimulate.UserRiskLevel) { if ($VerbosePolicyEvaluation) { Write-Verbose -Verbose -Message 'UserRiskLevels: NOT APPLIED' } $PolicyMatch = $false } @@ -5376,7 +5486,8 @@ function Invoke-DCConditionalAccessSimulation { if (($Policy.Conditions.Users.ExcludeGroups).Count -eq 0) { # - } else { + } + else { foreach ($Group in $Policy.Conditions.Users.ExcludeGroups) { if ($ConditionsToSimulate.Groups -contains $Group) { $ExcludeGroupsResult = 'ExcludeGroups: NOT APPLIED' @@ -5387,7 +5498,8 @@ function Invoke-DCConditionalAccessSimulation { if ($ExcludeGroupsResult -eq 'ExcludeGroups: APPLIED') { if ($VerbosePolicyEvaluation) { Write-Verbose -Verbose -Message $ExcludeGroupsResult } - } else { + } + else { if ($VerbosePolicyEvaluation) { Write-Verbose -Verbose -Message $ExcludeGroupsResult } $PolicyMatch = $false } @@ -5398,7 +5510,8 @@ function Invoke-DCConditionalAccessSimulation { if (($Policy.Conditions.Users.IncludeGroups).Count -eq 0) { # - } else { + } + else { foreach ($Group in $Policy.Conditions.Users.IncludeGroups) { if ($ConditionsToSimulate.Groups -contains $Group) { $IncludeGroupsResult = 'IncludeGroups: APPLIED' @@ -5410,7 +5523,8 @@ function Invoke-DCConditionalAccessSimulation { if ($IncludeGroupsResult -eq 'IncludeGroups: NOT APPLIED') { if ($VerbosePolicyEvaluation) { Write-Verbose -Verbose -Message $IncludeGroupsResult } $GroupMatch = $false - } else { + } + else { if ($VerbosePolicyEvaluation) { Write-Verbose -Verbose -Message $IncludeGroupsResult } $GroupMatch = $true } @@ -5426,10 +5540,12 @@ function Invoke-DCConditionalAccessSimulation { if ($Policy.Conditions.Users.excludeGuestsOrExternalUsers.GuestOrExternalUserTypes -and $ConditionsToSimulate.UserId -eq 'GuestsOrExternalUsers') { if ($VerbosePolicyEvaluation) { Write-Verbose -Verbose -Message 'ExcludeUsers: NOT APPLIED' } $UserMatch = $false - } elseif ($Policy.Conditions.Users.ExcludeUsers -contains $ConditionsToSimulate.UserId) { + } + elseif ($Policy.Conditions.Users.ExcludeUsers -contains $ConditionsToSimulate.UserId) { if ($VerbosePolicyEvaluation) { Write-Verbose -Verbose -Message 'ExcludeUsers: NOT APPLIED' } $PolicyMatch = $false - } else { + } + else { if ($VerbosePolicyEvaluation) { Write-Verbose -Verbose -Message 'ExcludeUsers: APPLIED' } } @@ -5438,13 +5554,16 @@ function Invoke-DCConditionalAccessSimulation { if ($Policy.Conditions.Users.IncludeUsers -eq 'All') { if ($VerbosePolicyEvaluation) { Write-Verbose -Verbose -Message 'IncludeUsers: APPLIED' } $UserMatch = $true - } elseif ($Policy.Conditions.Users.includeGuestsOrExternalUsers.GuestOrExternalUserTypes -and $ConditionsToSimulate.UserId -eq 'GuestsOrExternalUsers') { + } + elseif ($Policy.Conditions.Users.includeGuestsOrExternalUsers.GuestOrExternalUserTypes -and $ConditionsToSimulate.UserId -eq 'GuestsOrExternalUsers') { if ($VerbosePolicyEvaluation) { Write-Verbose -Verbose -Message 'IncludeUsers: APPLIED' } $UserMatch = $true - } elseif ($Policy.Conditions.Users.IncludeUsers -contains $ConditionsToSimulate.UserId) { + } + elseif ($Policy.Conditions.Users.IncludeUsers -contains $ConditionsToSimulate.UserId) { if ($VerbosePolicyEvaluation) { Write-Verbose -Verbose -Message 'IncludeUsers: APPLIED' } $UserMatch = $true - } else { + } + else { if ($VerbosePolicyEvaluation) { Write-Verbose -Verbose -Message 'IncludeUsers: NOT APPLIED' } $UserMatch = $false } @@ -5454,11 +5573,13 @@ function Invoke-DCConditionalAccessSimulation { if ($GroupMatch -or $UserMatch) { if ($VerbosePolicyEvaluation) { Write-Verbose -Verbose -Message "POLICY APPLIED: TRUE" } $CustomObject | Add-Member -MemberType NoteProperty -Name "Match" -Value $true - } else { + } + else { if ($VerbosePolicyEvaluation) { Write-Verbose -Verbose -Message "POLICY APPLIED: FALSE" } $CustomObject | Add-Member -MemberType NoteProperty -Name "Match" -Value $false } - } else { + } + else { if ($VerbosePolicyEvaluation) { Write-Verbose -Verbose -Message "POLICY APPLIED: FALSE" } $CustomObject | Add-Member -MemberType NoteProperty -Name "Match" -Value $false } @@ -5475,23 +5596,24 @@ function Invoke-DCConditionalAccessSimulation { if ($IncludeNonMatchingPolicies) { $Result - } else { - $Result | where Match -eq $true + } + else { + $Result | Where-Object Match -EQ $true } if ($SummarizedOutput) { - $Enforcement = @((($Result | where Match -eq $True).GrantControls | ConvertFrom-Json -ErrorAction SilentlyContinue).BuiltInControls | Select-Object -Unique) + $Enforcement = @((($Result | Where-Object Match -EQ $True).GrantControls | ConvertFrom-Json -ErrorAction SilentlyContinue).BuiltInControls | Select-Object -Unique) - if ((($Result | where Match -eq $True).GrantControls | ConvertFrom-Json -ErrorAction SilentlyContinue).AuthenticationStrength -eq $true) { + if ((($Result | Where-Object Match -EQ $True).GrantControls | ConvertFrom-Json -ErrorAction SilentlyContinue).AuthenticationStrength -eq $true) { $Enforcement += 'authenticationStrength' } - if ((($Result | where Match -eq $True).GrantControls | ConvertFrom-Json -ErrorAction SilentlyContinue).TermsOfUse | Select-Object -Unique) { + if ((($Result | Where-Object Match -EQ $True).GrantControls | ConvertFrom-Json -ErrorAction SilentlyContinue).TermsOfUse | Select-Object -Unique) { $Enforcement += 'termsOfUse' } - $CustomControls = ((($Result | where Match -eq $True).GrantControls | ConvertFrom-Json -ErrorAction SilentlyContinue).CustomAuthenticationFactors | Select-Object -Unique) + $CustomControls = ((($Result | Where-Object Match -EQ $True).GrantControls | ConvertFrom-Json -ErrorAction SilentlyContinue).CustomAuthenticationFactors | Select-Object -Unique) if ($CustomControls) { $Enforcement += $CustomControls @@ -5507,7 +5629,7 @@ function Invoke-DCConditionalAccessSimulation { Write-Host -ForegroundColor Cyan 'Applied Conditional Access policies:' - $AppliedPolicies = foreach ($Policy in ($Result | where Match -eq $True)) { + $AppliedPolicies = foreach ($Policy in ($Result | Where-Object Match -EQ $True)) { $EnforcementPerPolicy = @(($Policy.GrantControls | ConvertFrom-Json -ErrorAction SilentlyContinue).BuiltInControls | Select-Object -Unique) if (($Policy.GrantControls | ConvertFrom-Json -ErrorAction SilentlyContinue).AuthenticationStrength -eq $true) { @@ -5548,7 +5670,8 @@ function Invoke-DCConditionalAccessSimulation { foreach ($Row in ($Enforcement -replace " ", "`n")) { if ($Row -eq 'block') { Write-Host -ForegroundColor Red $Row - } else { + } + else { Write-Host -ForegroundColor Green $Row } } @@ -5621,7 +5744,8 @@ function New-DCConditionalAccessPolicyDesignReport { # Check if the Excel module is installed. if (Get-Module -ListAvailable -Name "ImportExcel") { # Do nothing. - } else { + } + else { Write-Error -Exception "The Excel PowerShell module is not installed. Please, run 'Install-Module ImportExcel -Force' as an admin and try again." -ErrorAction Stop } @@ -5645,7 +5769,7 @@ function New-DCConditionalAccessPolicyDesignReport { $EnterpriseApps = Get-MgServicePrincipal # Fetch roles for id translation. - $EntraIDRoles = Get-MgDirectoryRoleTemplate | Select-Object DisplayName, Description, Id | Sort-Object DisplayName + $EntraIDRoles = Get-MgDirectoryRoleTemplate | Select-Object DisplayName, Description, Id | Sort-Object DisplayName # Format the result. @@ -5844,7 +5968,7 @@ function New-DCConditionalAccessPolicyDesignReport { # termsOfUse $TermsOfUses = foreach ($TermsOfUse in $Policy.grantControls.termsOfUse) { $GraphUri = "https://graph.microsoft.com/v1.0/agreements/$TermsOfUse" - (Get-MgAgreement | where Id -eq $TermsOfUse).displayName + (Get-MgAgreement | Where-Object Id -EQ $TermsOfUse).displayName } $CustomObject | Add-Member -MemberType NoteProperty -Name "termsOfUse" -Value (Out-String -InputObject $TermsOfUses)