Skip to content

Commit

Permalink
Merge pull request #53 from AzureAD/preview
Browse files Browse the repository at this point in the history
Preview
  • Loading branch information
merill authored Jul 22, 2022
2 parents b6c000d + e929d15 commit 2b9d6c8
Show file tree
Hide file tree
Showing 9 changed files with 194 additions and 21 deletions.
5 changes: 2 additions & 3 deletions src/AADRecommendations.xml
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@
<Area>Access Surface Area</Area>
<ID>AR0006</ID>
<Name>Grants for high level permissions</Name>
<Summary>Application can request high level permission trough fishing. Such permissions include those allowing "ReadWrite" actions or "Mail" operations. Regular reviews should be in place and non-necessary grants should be removed</Summary>
<Summary>Applications can request high level permission trough consent fishing. Such permissions include those allowing "ReadWrite" actions or "Mail" operations. Regular reviews should be in place and non-necessary grants should be removed</Summary>
<Recommendation>Review applications having high permissions and remove unnecessary grants</Recommendation>
</recommendation>
<recommendation>
Expand Down Expand Up @@ -286,7 +286,7 @@
<Area>Entitlement Management</Area>
<ID>AR0009</ID>
<Name>Assignment of Apps with "All users" group</Name>
<Summary>The "All users" group contains both Members and Guests. Resource owners might misunderstand this group to contain only members. As a result, special condsideration should be takeb when using this group for application assignment or grant access tor resource such as SharePoint Content or Azure resources</Summary>
<Summary>The "All users" group contains both Members and Guests. Resource owners might misunderstand this group to contain only members. As a result, special condsideration should be taken when using this group for application assignment or grant access tor resource such as SharePoint Content or Azure resources</Summary>
<Recommendation>Fix the entittlements by creating the right groups (e.g. "all members")</Recommendation>
</recommendation>
<recommendation>
Expand Down Expand Up @@ -2186,7 +2186,6 @@
</Recommendation>
<Sources>
<File>roleDefinitions.csv</File>
<File>RoleAssignmentReport.csv</File>
<File>conditionalAccessPolicies.json</File>
</Sources>
<PowerShell>
Expand Down
4 changes: 3 additions & 1 deletion src/AzureADAssessment.psd1
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ RequiredModules = @(
)

# Assemblies that must be loaded prior to importing this module
# RequiredAssemblies = @()
RequiredAssemblies = @("System.IO.Compression.FileSystem.dll")

# Script files (.ps1) that are run in the caller's environment prior to importing this module.
# ScriptsToProcess = @()
Expand Down Expand Up @@ -123,6 +123,7 @@ NestedModules = @(
'.\Import-AADAssessmentEvidence.ps1'
'.\Export-AADAssessmentReportData.ps1'
'.\analysis\AccessManagement\AuthenticationExperience\Test-AADAssessmentEmailOtp.ps1'
'.\Test-AADAssessmentPackage.ps1'
)

# Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export.
Expand Down Expand Up @@ -151,6 +152,7 @@ FunctionsToExport = @(
'Export-AADAssessmentRecommendations'
'Test-AADAssessmentEmailOtp'
'Export-AADAssessmentReportData'
'Test-AADAssessmentPackage'
)

# Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export.
Expand Down
49 changes: 45 additions & 4 deletions src/Complete-AADAssessmentReports.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -66,16 +66,57 @@ function Complete-AADAssessmentReports {

## Expand Data Package
Write-Progress -Id 0 -Activity 'Microsoft Azure AD Assessment Complete Reports' -Status 'Expand Data' -PercentComplete 0
Expand-Archive $Path -DestinationPath $OutputDirectoryData -Force -ErrorAction Stop
#Expand-Archive $Path -DestinationPath $OutputDirectoryData -Force -ErrorAction Stop
# Remove destination before extract
if (Test-Path -Path $OutputDirectoryData) {
Remove-Item $OutputDirectoryData -Recurse -Force
}
# Extract content
[System.IO.Compression.ZipFile]::ExtractToDirectory($Path,$OutputDirectoryData)
$AssessmentDetail = Get-Content $AssessmentDetailPath -Raw | ConvertFrom-Json
#Check for DataFiles
$OutputDirectoryAAD = Join-Path $OutputDirectoryData 'AAD-*' -Resolve -ErrorAction Stop
[array] $DataFiles = Get-Item -Path (Join-Path $OutputDirectoryAAD "*") -Include "*Data.xml"
$SkippedReportOutput = $DataFiles -and $DataFiles.Count -eq 9

## Check the provided archive
$archiveState = Test-AADAssessmentPackage -Path $Path -SkippedReportOutput $SkippedReportOutput
if (!$archiveState) {
Write-Warning "The provided package is incomplete. Please review how data was collected and any related errors"
Write-Warning "If reporting has been skipped this command will generate the reports"
}

# Check assessment version
$moduleVersion = $MyInvocation.MyCommand.ScriptBlock.Module.Version
[System.Version]$packageVersion = $AssessmentDetail.AssessmentVersion
if ($packageVersion.Build -eq -1) {
Write-Warning "The package was not generate with a module installed from the PowerShell Gallery"
Write-Warning "Please install the module from the gallery to generate the package:"
Write-Warning "PS > Install-Module -Name AzureADAssessment"
}
elseif ($moduleVersion.Build -eq -1) {
Write-Warning "The Azure AD Assessment module was not installed from the PowerShell Gallery"
Write-Warning "Please install the module from the gallery to complete the assessment:"
Write-Warning "PS > Install-Module -Name AzureADAssessment"
}
elseif ($moduleVersion -ne $packageVersion) {
Write-Warning "The module version differs from the provided package and the Assessment module version used to run the complete command"
Write-Warning "Please use the same module version to generate the package and complete the assessment"
Write-Warning ""
Write-Warning "package version: $packageVersion"
Write-Warning "module version: $moduleVersion"
Write-Warning ""
Write-Warning "To install a specific version of the module:"
Write-Warning "PS > Remove-Module -Name AzureADAssessment"
Write-Warning "PS > Install-Module -Name AzureADAssessment -RequiredVersion $packageVersion"
Write-Warning "PS > Import-Module -Name AzureADAssessment -RequiredVersion $packageVersion"
}

## Load Data
Write-Progress -Id 0 -Activity ('Microsoft Azure AD Assessment Complete Reports - {0}' -f $AssessmentDetail.AssessmentTenantDomain) -Status 'Load Data' -PercentComplete 10
$OutputDirectoryAAD = Join-Path $OutputDirectoryData 'AAD-*' -Resolve -ErrorAction Stop

## Generate Reports
[array] $DataFiles = Get-Item -Path (Join-Path $OutputDirectoryAAD "*") -Include "*Data.xml"
if ($DataFiles -and $DataFiles.Count -eq 9) {
if ($SkippedReportOutput) {
Write-Progress -Id 0 -Activity ('Microsoft Azure AD Assessment Complete Reports - {0}' -f $AssessmentDetail.AssessmentTenantDomain) -Status 'Output Report Data' -PercentComplete 20
Export-AADAssessmentReportData -SourceDirectory $OutputDirectoryAAD -OutputDirectory $OutputDirectoryAAD

Expand Down
2 changes: 1 addition & 1 deletion src/Export-AADAssessmentReportData.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ function Export-AADAssessmentReportData {
}

# generate the report
Set-Content -Path (Join-Path $OutputDirectory "users.csv") -Value 'id,userPrincipalName,displayName,userType,accountEnabled,onPremisesSyncEnabled,onPremisesImmutableId,mail,otherMails,AADLicense,lastInteractiveSignInDateTime,lastNonInteractiveSignInDateTime,isMfaRegistered,isMfaCapable,methodsRegistered'
Set-Content -Path (Join-Path $OutputDirectory "users.csv") -Value 'id,userPrincipalName,displayName,userType,accountEnabled,onPremisesSyncEnabled,onPremisesImmutableId,mail,otherMails,AADLicense,lastInteractiveSignInDateTime,lastNonInteractiveSignInDateTime,isMfaRegistered,isMfaCapable,methodsRegistered,defaultMfaMethod'
Get-AADAssessUserReport -Offline -UserData $LookupCache.user -RegistrationDetailsData $LookupCache.userRegistrationDetails`
| Use-Progress -Activity 'Exporting UserReport' -Property id -PassThru -WriteSummary `
| Format-Csv `
Expand Down
5 changes: 5 additions & 0 deletions src/Get-AADAssessUserReport.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,14 @@ function Get-AADAssessUserReport {
$isMfaCapable = $false
$isMfaRegistered = $false
$methodsRegistered = ""
$defaultMfaMethod = ""
if ($registerationDetails) {
$isMfaRegistered = $registerationDetails.isMfaRegistered
$isMfaCapable = $registerationDetails.isMfaCapable
$methodsRegistered = $registerationDetails.methodsRegistered -join ";"
if ($registerationDetails.defaultMfaMethod -ne "none") {
$defaultMfaMethod = $registerationDetails.defaultMfaMethod
}
} else {
Write-Warning "authentication method registration not found for $($InputObject.id)"
}
Expand All @@ -81,6 +85,7 @@ function Get-AADAssessUserReport {
"isMfaRegistered" = $isMfaRegistered
"isMfaCapable" = $isMfaCapable
"methodsRegistered" = $methodsRegistered
"defaultMfaMethod" = $defaultMfaMethod
}
}
}
Expand Down
37 changes: 31 additions & 6 deletions src/Invoke-AADAssessmentDataCollection.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -64,13 +64,13 @@ function Invoke-AADAssessmentDataCollection {
#$OutputDirectory = Join-Path $OutputDirectory "AzureADAssessment"
$OutputDirectoryData = Join-Path $OutputDirectory "AzureADAssessmentData"
$AssessmentDetailPath = Join-Path $OutputDirectoryData "AzureADAssessment.json"
$PackagePath = Join-Path $OutputDirectory "AzureADAssessmentData.zip"
$PackagePath = Join-Path $OutputDirectory "AzureADAssessmentData.aad"

### Organization Data - 0
Write-Progress -Id 0 -Activity 'Microsoft Azure AD Assessment Data Collection' -Status 'Organization Details' -PercentComplete 0
$OrganizationData = Get-MsGraphResults 'organization?$select=id,displayName,verifiedDomains,technicalNotificationMails' -ErrorAction Stop
$InitialTenantDomain = $OrganizationData.verifiedDomains | Where-Object isInitial -EQ $true | Select-Object -ExpandProperty name -First 1
$PackagePath = $PackagePath.Replace("AzureADAssessmentData.zip", "AzureADAssessmentData-$InitialTenantDomain.zip")
$PackagePath = $PackagePath.Replace("AzureADAssessmentData.aad", "AzureADAssessmentData-$InitialTenantDomain.aad")
$OutputDirectoryAAD = Join-Path $OutputDirectoryData "AAD-$InitialTenantDomain"
Assert-DirectoryExists $OutputDirectoryAAD

Expand Down Expand Up @@ -228,9 +228,10 @@ function Invoke-AADAssessmentDataCollection {
$ReferencedIdCache.servicePrincipal.Clear()

### Administrative units data - 14
Set-Content -Path (Join-Path $OutputDirectoryAAD "administrativeUnits.csv") -Value 'id,displayName,visibility'
Write-Progress -Id 0 -Activity ('Microsoft Azure AD Assessment Data Collection - {0}' -f $InitialTenantDomain) -Status 'Administrative Units' -PercentComplete 65
Get-MsGraphResults 'directory/administrativeUnits' -Select 'id,displayName,visibility' `
| Export-Csv (Join-Path $OutputDirectoryAAD "administrativeUnits.csv")
| Export-Csv (Join-Path $OutputDirectoryAAD "administrativeUnits.csv") -NoTypeInformation -Append

### Registration details data - 15
if ($licenseType -ne "Free") {
Expand Down Expand Up @@ -333,10 +334,16 @@ function Invoke-AADAssessmentDataCollection {
}

if (!$SkipPackaging) {
### Remove pre existing package (zip) if it exists
if (Test-Path -Path $PackagePath) {
Remove-Item $PackagePath -Force
}


### Package Output
Compress-Archive (Join-Path $OutputDirectoryData '\*') -DestinationPath $PackagePath -Force -ErrorAction Stop
#Compress-Archive (Join-Path $OutputDirectoryData '\*') -DestinationPath $PackagePath -Force -ErrorAction Stop
[System.IO.Compression.ZipFile]::CreateFromDirectory($OutputDirectoryData,$PackagePath)

### Clean-Up Data Files
Remove-Item $OutputDirectoryData -Recurse -Force
}

Expand All @@ -345,5 +352,23 @@ function Invoke-AADAssessmentDataCollection {

}
catch { if ($MyInvocation.CommandOrigin -eq 'Runspace') { Write-AppInsightsException $_.Exception }; throw }
finally { Complete-AppInsightsRequest $MyInvocation.MyCommand.Name -Success $? }
finally {
# check generated package and issue warning
$issue = $false
if (!(Test-Path -PathType Leaf -Path $PackagePath) -and !$SkipPackaging) {
Write-Warning "The export package has not been generated"
$issue = $true
} elseif (!$SkipPackaging) {
if (!(Test-AADAssessmentPackage -Path $PackagePath -SkippedReportOutput $SkipReportOutput)) {
Write-Warning "The generated package is missing some data"
$issue = $true
}
}
if ($issue) {
Write-Warning "If you are working with microsoft or a provider on the assessment please warn them"
Write-Warning "Please check GitHub issues and fill a new one or reply on existing ones mentionning the errors seen"
Write-warning "https://github.com/AzureAD/AzureADAssessment/issues"
}
Complete-AppInsightsRequest $MyInvocation.MyCommand.Name -Success $?
}
}
4 changes: 2 additions & 2 deletions src/New-AADAssessmentRecommendations.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ function New-AADAssessmentRecommendations {
[string] $InterviewSpreadsheetPath
)

#Start-AppInsightsRequest $MyInvocation.MyCommand.Name
Start-AppInsightsRequest $MyInvocation.MyCommand.Name

## Expand extracted data
if (-not $SkipExpand) {
Expand Down Expand Up @@ -185,7 +185,7 @@ function New-AADAssessmentRecommendations {
Write-Error "No Tenant Data found"
}

#Complete-AppInsightsRequest $MyInvocation.MyCommand.Name -Success $?
Complete-AppInsightsRequest $MyInvocation.MyCommand.Name -Success $?
}

function Set-TypeQnAResult($data, $recommendationDef, $recommendation){
Expand Down
97 changes: 97 additions & 0 deletions src/Test-AADAssessmentPackage.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
<#
.SYNOPSIS
Test that the provided Azure AD Assessment package has the necessary content
.DESCRIPTION
Test that the provided Azure AD Assessment package has the necessary content
.EXAMPLE
PS C:\>Test-AADAssessmentPackage 'C:\AzureADAssessmentData-contoso.aad'
Test that the package for contoso has the necesary content for the assessment.
.INPUTS
System.String
#>
function Test-AADAssessmentPackage {
[CmdletBinding()]
param
(
# Path to the file where the exported events will be stored
[Parameter(Mandatory = $true)]
[string] $Path,
# Reports should have been generated
[Parameter(Mandatory = $false)]
[bool] $SkippedReportOutput
)

if (!(Test-Path -path $Path)) {
Write-Warning "Assessment package not found"
return $false
}

$fullPath = Convert-Path $Path

$requiredEntries = @(
"AAD-*/administrativeUnits.csv",
"AAD-*/AppCredentialsReport.csv",
"AAD-*/applications.json",
"AAD-*/appRoleAssignments.csv",
"AAD-*/conditionalAccessPolicies.json",
"AAD-*/ConsentGrantReport.csv",
"AAD-*/emailOTPMethodPolicy.json",
"AAD-*/groups.csv",
"AAD-*/namedLocations.json",
"AAD-*/NotificationsEmailsReport.csv",
"AAD-*/oauth2PermissionGrants.csv",
"AAD-*/organization.json",
"AAD-*/RoleAssignmentReport.csv",
"AAD-*/roleDefinitions.csv",
"AAD-*/servicePrincipals.csv",
"AAD-*/servicePrincipals.json",
"AAD-*/subscribedSkus.json",
"AAD-*/userRegistrationDetails.json",
"AAD-*/users.csv",
"AzureADAssessment.json"
)

if ($SkippedReportOutput) {
$requiredEntries = @(
"AAD-*/administrativeUnits.csv",
"AAD-*/applicationData.xml",
"AAD-*/appRoleAssignmentData.xml",
"AAD-*/conditionalAccessPolicies.json",
"AAD-*/directoryRoleData.xml"
"AAD-*/emailOTPMethodPolicy.json",
"AAD-*/groupData.xml",
"AAD-*/namedLocations.json",
"AAD-*/oauth2PermissionGrantData.xml",
"AAD-*/organization.json",
"AAD-*/roleAssignmentSchedulesData.xml",
"AAD-*/roleDefinitions.csv",
"AAD-*/roleEligibilitySchedulesData.xml",
"AAD-*/servicePrincipalData.xml",
"AAD-*/subscribedSkus.json",
"AAD-*/userData.xml",
"AAD-*/userRegistrationDetails.json",
"AzureADAssessment.json"
)
}

$entries = [IO.Compression.ZipFile]::OpenRead($fullPath).Entries

$effectiveEntries = $entries | Where-Object { $_.Length -gt 0}

$validPackage = $true
foreach($requiredEntry in $requiredEntries) {
$found = $false
foreach ($effectiveEntry in $effectiveEntries) {
if (($effectiveEntry.FullName -replace "\\","/") -like $requiredEntry) {
$found = $true
}
}
if (!$found) {
Write-Warning "Required entry '$requiredEntry' not found or empty"
$validPackage = $false
}
}

# retrun package vaility
return $validPackage
}
12 changes: 8 additions & 4 deletions src/internal/Expand-MsGraphRelationship.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,11 @@ function Expand-MsGraphRelationship {
[array] $Results = $InputObjects[0..($BatchSize - 1)] | Get-MsGraphResults $uri -DisableUniqueIdDeduplication -GroupOutputByRequest
}
for ($i = 0; $i -lt $InputObjects.Count; $i++) {
[array] $refValues = $Results[$i]
$refValues = @()
if ($i -lt $Results.Count) {
[array] $refValues = $Results[$i]
}
if ($References) { $refValues = $refValues | Expand-ODataId | Select-Object -Property "*" -ExcludeProperty '@odata.id' }
if ($null -eq $refValues) { $refValues = @() }
$InputObjects[$i] | Add-Member -Name $PropertyName -MemberType NoteProperty -Value $refValues -PassThru -ErrorAction Ignore
}
$InputObjects.RemoveRange(0, $BatchSize)
Expand All @@ -72,9 +74,11 @@ function Expand-MsGraphRelationship {
[array] $Results = $InputObjects | Get-MsGraphResults $uri -DisableUniqueIdDeduplication -GroupOutputByRequest
}
for ($i = 0; $i -lt $InputObjects.Count; $i++) {
[array] $refValues = $Results[$i]
$refValues = @()
if ($Results.Count -gt $i) {
[array] $refValues = $Results[$i]
}
if ($References) { $refValues = $refValues | Expand-ODataId | Select-Object -Property "*" -ExcludeProperty '@odata.id' }
if ($null -eq $refValues) { $refValues = @() }
$InputObjects[$i] | Add-Member -Name $PropertyName -MemberType NoteProperty -Value $refValues -PassThru -ErrorAction Ignore
}
}
Expand Down

0 comments on commit 2b9d6c8

Please sign in to comment.