Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Created Global Adminstrator Control #51

Merged
merged 4 commits into from
Dec 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified psmodules/GR-Common.zip
Binary file not shown.
Binary file modified psmodules/GR-ComplianceChecks.zip
Binary file not shown.
4 changes: 2 additions & 2 deletions setup/IaC/modules/automationaccount.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ resource module13 'modules' = if (newDeployment || updatePSModules) {
properties: {
contentLink: {
uri: '${ModuleBaseURL}/GR-Common.zip'
version: '1.1.2'
version: '1.1.12'
}
}
}
Expand Down Expand Up @@ -212,7 +212,7 @@ resource module14 'modules' = if (newDeployment || updatePSModules) {
properties: {
contentLink: {
uri: '${ModuleBaseURL}/GR-ComplianceChecks.zip'
version: '1.0.0'
version: '1.4.5'
}
}
}
Expand Down
28 changes: 28 additions & 0 deletions setup/modules.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,34 @@
}
]
},
{
"ModuleName": "Check-GlobalAdministratorAccntsMFA",
"Control": "Guardrails1",
"ModuleType": "Builtin",
"Status": "Enabled",
"Required": "True",
"Script": "Check-GAAuthenticationMethods -StorageAccountName $vars.storageaccountname -ContainerName $vars.containerName -ResourceGroupName $ResourceGroupName -SubscriptionID $SubID -DocumentName $vars.DocumentName -ControlName $msgTable.CtrName1 -ItemName $msgTable.gaAccntsMFACheck -MsgTable $msgTable -ReportTime $ReportTime -itsgcode $vars.itsgcode",
"variables": [
{
"Name": "storageAccountName",
"Value": "StorageAccountName"
},
{
"Name": "containerName",
"Value": "ContainerName"
}
],
"localVariables": [
{
"Name": "DocumentName",
"Value": "GlobalAdministratorsUPN.txt"
},
{
"Name": "itsgcode",
"Value": "IA2(1)"
}
]
},
{
"ModuleName": "Check-DocumentExistsInStorage",
"Control": "Guardrails1",
Expand Down
9 changes: 9 additions & 0 deletions src/GuardRails-Localization/GR-ComplianceChecks-Msgs.psd1
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ bgValidLicenseAssigned = has a valid Microsoft Entra ID P2 assigned
bgAccountHasManager = BG Account {0} has a Manager
bgAccountNoManager = BG Account {0} doesn't have a Manager
bgBothHaveManager = Both BreakGlass accounts have manager
gaAccntsMFACheck = Global Administrators Accounts MFA check

# GuardRail #2
MSEntIDLicenseTypeFound = Found correct license type
Expand Down Expand Up @@ -183,5 +184,13 @@ enableMktPlace = Enable Azure Private MarketPlace as per: https://docs.microsoft
# GR-Common
procedureFileFound = File {0} found in Container.
procedureFileNotFound = Could not find document for {0}, please create and upload a file with the name '{1}' in Container '{2}' on Storage Account '{3}' to confirm you have completed the Item in the control.
procedureFileDataInvalid = The global administrator file(s) contain(s) invalid User Principal Names (UPNs). Ensure that UPNs start with a hyphen, and type each of them on a new line.
globalAdminFileFound = File {0} found in Container.
globalAdminFileNotFound = Could not find document for {0}, please create and upload a file with the name '{1}' in Container '{2}' on Storage Account '{3}' to confirm you have completed the Item in the control.
globalAdminFileEmpty = Empty file {0} found in Container.
globalAdminNotExist = Global Administrator accounts not found or declared in file {0}.
globalAdminMFAPassAndMin2Accnts = Two or more global administrator accounts have been identified, and multi-factor authentication (MFA) is enabled for all of them.
globalAdminMinAccnts = There must be at least two global administrator accounts.
globalAdminAccntsMFADisabled = Not all the global administrator accounts have multi-factor authentication (MFA) enabled

'@
2 changes: 1 addition & 1 deletion src/GuardRails-Localization/GR-ComplianceChecks.psd1
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
RootModule = 'GR-ComplianceChecks'

# Version number of this module.
ModuleVersion = '1.4.4'
ModuleVersion = '1.4.5'

# Supported PSEditions
# CompatiblePSEditions = @()
Expand Down
2 changes: 1 addition & 1 deletion src/Guardrails-Common/GR-Common.psd1
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
RootModule = 'GR-Common'

# Version number of this module.
ModuleVersion = '1.1.11'
ModuleVersion = '1.1.12'

# Supported PSEditions
# CompatiblePSEditions = @()
Expand Down
156 changes: 156 additions & 0 deletions src/Guardrails-Common/GR-Common.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,150 @@ function Add-LogAnalyticsResults {
-logType $LogType `
-TimeStampField Get-Date
}

function Check-GAAuthenticationMethods {
param (
[string] $StorageAccountName,
[string] $ContainerName,
[string] $ResourceGroupName,
[string] $SubscriptionID,
[string[]] $DocumentName,
[string] $ControlName,
[string]$ItemName,
[hashtable] $msgTable,
[string]$itsgcode,
[Parameter(Mandatory = $true)]
[string]
$ReportTime
)
[PSCustomObject] $ErrorList = New-Object System.Collections.ArrayList
[bool] $IsCompliant = $false
[string] $Comments = $null

try {
Select-AzSubscription -Subscription $SubscriptionID | out-null
}
catch{
$ErrorList.Add("Failed to run 'Select-Azsubscription' with error: $_")
throw "Error: Failed to run 'Select-Azsubscription' with error: $_"
}
try {
$StorageAccount = Get-Azstorageaccount -ResourceGroupName $ResourceGroupName -Name $StorageAccountName -ErrorAction Stop
}
catch {
$ErrorList.Add("Could not find storage account '$storageAccountName' in resoruce group '$resourceGroupName' of `
subscription '$subscriptionId'; verify that the storage account exists and that you have permissions to it. Error: $_")

Write-Error "Could not find storage account '$storageAccountName' in resoruce group '$resourceGroupName' of `
subscription '$subscriptionId'; verify that the storage account exists and that you have permissions to it. Error: $_"
}

$docFileEmpty = $false
$docFileNotAvailable = $false
$mfaEnabled = $false
$globalAdminFound = $false
$globalAdminCount = 0
$mfaCounter = 0
$commentsArray = @()
$globalAdminUPNs = @()

ForEach ($docName in $DocumentName) {

$blobs = Get-AzStorageBlob -Container $ContainerName -Context $StorageAccount.Context -Blob $docName -ErrorAction SilentlyContinue

If ($null -eq $blobs) {
# a blob with the name $DocumentName was located in the specified storage account
$commentsArray += $msgTable.procedureFileNotFound -f $ItemName, $docName, $ContainerName, $StorageAccountName
}
else {
# get blob content if blob exists
$blobContent = (Get-AzStorageBlobContent -Container $ContainerName -Blob $docName -Context $StorageAccount.Context -ErrorAction SilentlyContinue).ICloudBlob.DownloadText()

if ($blobContent -eq '' -or $blobContent -eq ' ') {
docFileEmpty = $true
$commentsArray += $msgTable.globalAdminFileEmpty -f $docName
}
elseif ($blobContent -eq 'N/A' -or $blobContent -eq 'n/a') {
docFileNotAvailable = $true
$commentsArray += $msgTable.globalAdminNotExist -f $docName
}
else {
$globalAdminFound = $true
$globalAdminUPNs = $blobContent -split '-' | Where-Object { $_ -ne '' }
}
}
}

if ($globalAdminFound) {

#Clean up the data and remove any invalid email formats
$filteredUPNs = Clean-GAData -GAUPNs $globalAdminUPNs

$globalAdminCount = $filteredUPNs.Count

ForEach ($globalAdminAccount in $filteredUPNs) {
$urlPath = '/users/' + $globalAdminAccount + '/authentication/methods'

try {
$response = Invoke-GraphQuery -urlPath $urlPath -ErrorAction Stop
}
catch {
$ErrorList.Add("Failed to call Microsoft Graph REST API at URL '$urlPath'; returned error message: $_" )
Write-Error "Error: Failed to call Microsoft Graph REST API at URL '$urlPath'; returned error message: $_"
}

$data = $response.Content
$authenticationmethods = $data.value

# To check if MFA is setup for a user, we're looking for either :
# #microsoft.graph.microsoftAuthenticatorAuthenticationMethod or
# #microsoft.graph.phoneAuthenticationMethod
Write-Host $authenticationmethods

foreach ($authmeth in $authenticationmethods) {
if (($($authmeth.'@odata.type') -eq "#microsoft.graph.phoneAuthenticationMethod") -or `
($($authmeth.'@odata.type') -eq "#microsoft.graph.microsoftAuthenticatorAuthenticationMethod")) {
$mfaCounter += 1 #Need to keep track of each GA mfa in counter and compare it to count
}
}
}
}

if($mfaCounter -eq $globalAdminCount) {
$mfaEnabled = $true
}
else{
$commentsArray += $msgTable.globalAdminAccntsMFADisabled
}

if ($globalAdminCount -lt 2 -and ($docFileEmpty -and $docFileNotAvailable)) {
$commentsArray += $msgTable.globalAdminMinAccnts
}

if ($globalAdminCount -ge 2 -and $mfaEnabled) {
$commentsArray += $msgTable.globalAdminMFAPassAndMin2Accnts
$IsCompliant = $mfaEnabled
}
$Comments = $commentsArray -join ";"

$PsObject = [PSCustomObject]@{
ComplianceStatus = $IsCompliant
ControlName = $ControlName
ItemName = $ItemName
DocumentName = $DocumentName
Comments = $Comments
ReportTime = $ReportTime
itsgcode = $itsgcode
}
$moduleOutput = [PSCustomObject]@{
ComplianceResults = $PsObject
Errors = $ErrorList
AdditionalResults = $AdditionalResults
}
return $moduleOutput

}

function Check-DocumentExistsInStorage {
[Alias('Check-DocumentsExistInStorage')]
param (
Expand Down Expand Up @@ -511,6 +655,18 @@ function Hide-Email {
}
}

function Clean-GAData {
param (
[string[]] $GAUPNs
)

$FilteredUPNs = $GAUPNs | Where-Object { $_ -match '\S' -and $_ -like "*@*" } | ForEach-Object { $_ -replace '\s' }

if ($FilteredUPNs) {
return $FilteredUPNs
}
}

function Invoke-GraphQuery {
param(
# URL path (ex: /users)
Expand Down
Loading