Skip to content

Commit 2b9d6c8

Browse files
authored
Merge pull request #53 from AzureAD/preview
Preview
2 parents b6c000d + e929d15 commit 2b9d6c8

9 files changed

+194
-21
lines changed

src/AADRecommendations.xml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@
203203
<Area>Access Surface Area</Area>
204204
<ID>AR0006</ID>
205205
<Name>Grants for high level permissions</Name>
206-
<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>
206+
<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>
207207
<Recommendation>Review applications having high permissions and remove unnecessary grants</Recommendation>
208208
</recommendation>
209209
<recommendation>
@@ -286,7 +286,7 @@
286286
<Area>Entitlement Management</Area>
287287
<ID>AR0009</ID>
288288
<Name>Assignment of Apps with "All users" group</Name>
289-
<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>
289+
<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>
290290
<Recommendation>Fix the entittlements by creating the right groups (e.g. "all members")</Recommendation>
291291
</recommendation>
292292
<recommendation>
@@ -2186,7 +2186,6 @@
21862186
</Recommendation>
21872187
<Sources>
21882188
<File>roleDefinitions.csv</File>
2189-
<File>RoleAssignmentReport.csv</File>
21902189
<File>conditionalAccessPolicies.json</File>
21912190
</Sources>
21922191
<PowerShell>

src/AzureADAssessment.psd1

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ RequiredModules = @(
5656
)
5757

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

6161
# Script files (.ps1) that are run in the caller's environment prior to importing this module.
6262
# ScriptsToProcess = @()
@@ -123,6 +123,7 @@ NestedModules = @(
123123
'.\Import-AADAssessmentEvidence.ps1'
124124
'.\Export-AADAssessmentReportData.ps1'
125125
'.\analysis\AccessManagement\AuthenticationExperience\Test-AADAssessmentEmailOtp.ps1'
126+
'.\Test-AADAssessmentPackage.ps1'
126127
)
127128

128129
# 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.
@@ -151,6 +152,7 @@ FunctionsToExport = @(
151152
'Export-AADAssessmentRecommendations'
152153
'Test-AADAssessmentEmailOtp'
153154
'Export-AADAssessmentReportData'
155+
'Test-AADAssessmentPackage'
154156
)
155157

156158
# 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.

src/Complete-AADAssessmentReports.ps1

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -66,16 +66,57 @@ function Complete-AADAssessmentReports {
6666

6767
## Expand Data Package
6868
Write-Progress -Id 0 -Activity 'Microsoft Azure AD Assessment Complete Reports' -Status 'Expand Data' -PercentComplete 0
69-
Expand-Archive $Path -DestinationPath $OutputDirectoryData -Force -ErrorAction Stop
69+
#Expand-Archive $Path -DestinationPath $OutputDirectoryData -Force -ErrorAction Stop
70+
# Remove destination before extract
71+
if (Test-Path -Path $OutputDirectoryData) {
72+
Remove-Item $OutputDirectoryData -Recurse -Force
73+
}
74+
# Extract content
75+
[System.IO.Compression.ZipFile]::ExtractToDirectory($Path,$OutputDirectoryData)
7076
$AssessmentDetail = Get-Content $AssessmentDetailPath -Raw | ConvertFrom-Json
77+
#Check for DataFiles
78+
$OutputDirectoryAAD = Join-Path $OutputDirectoryData 'AAD-*' -Resolve -ErrorAction Stop
79+
[array] $DataFiles = Get-Item -Path (Join-Path $OutputDirectoryAAD "*") -Include "*Data.xml"
80+
$SkippedReportOutput = $DataFiles -and $DataFiles.Count -eq 9
81+
82+
## Check the provided archive
83+
$archiveState = Test-AADAssessmentPackage -Path $Path -SkippedReportOutput $SkippedReportOutput
84+
if (!$archiveState) {
85+
Write-Warning "The provided package is incomplete. Please review how data was collected and any related errors"
86+
Write-Warning "If reporting has been skipped this command will generate the reports"
87+
}
88+
89+
# Check assessment version
90+
$moduleVersion = $MyInvocation.MyCommand.ScriptBlock.Module.Version
91+
[System.Version]$packageVersion = $AssessmentDetail.AssessmentVersion
92+
if ($packageVersion.Build -eq -1) {
93+
Write-Warning "The package was not generate with a module installed from the PowerShell Gallery"
94+
Write-Warning "Please install the module from the gallery to generate the package:"
95+
Write-Warning "PS > Install-Module -Name AzureADAssessment"
96+
}
97+
elseif ($moduleVersion.Build -eq -1) {
98+
Write-Warning "The Azure AD Assessment module was not installed from the PowerShell Gallery"
99+
Write-Warning "Please install the module from the gallery to complete the assessment:"
100+
Write-Warning "PS > Install-Module -Name AzureADAssessment"
101+
}
102+
elseif ($moduleVersion -ne $packageVersion) {
103+
Write-Warning "The module version differs from the provided package and the Assessment module version used to run the complete command"
104+
Write-Warning "Please use the same module version to generate the package and complete the assessment"
105+
Write-Warning ""
106+
Write-Warning "package version: $packageVersion"
107+
Write-Warning "module version: $moduleVersion"
108+
Write-Warning ""
109+
Write-Warning "To install a specific version of the module:"
110+
Write-Warning "PS > Remove-Module -Name AzureADAssessment"
111+
Write-Warning "PS > Install-Module -Name AzureADAssessment -RequiredVersion $packageVersion"
112+
Write-Warning "PS > Import-Module -Name AzureADAssessment -RequiredVersion $packageVersion"
113+
}
71114

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

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

src/Export-AADAssessmentReportData.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ function Export-AADAssessmentReportData {
148148
}
149149

150150
# generate the report
151-
Set-Content -Path (Join-Path $OutputDirectory "users.csv") -Value 'id,userPrincipalName,displayName,userType,accountEnabled,onPremisesSyncEnabled,onPremisesImmutableId,mail,otherMails,AADLicense,lastInteractiveSignInDateTime,lastNonInteractiveSignInDateTime,isMfaRegistered,isMfaCapable,methodsRegistered'
151+
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'
152152
Get-AADAssessUserReport -Offline -UserData $LookupCache.user -RegistrationDetailsData $LookupCache.userRegistrationDetails`
153153
| Use-Progress -Activity 'Exporting UserReport' -Property id -PassThru -WriteSummary `
154154
| Format-Csv `

src/Get-AADAssessUserReport.ps1

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,14 @@ function Get-AADAssessUserReport {
5757
$isMfaCapable = $false
5858
$isMfaRegistered = $false
5959
$methodsRegistered = ""
60+
$defaultMfaMethod = ""
6061
if ($registerationDetails) {
6162
$isMfaRegistered = $registerationDetails.isMfaRegistered
6263
$isMfaCapable = $registerationDetails.isMfaCapable
6364
$methodsRegistered = $registerationDetails.methodsRegistered -join ";"
65+
if ($registerationDetails.defaultMfaMethod -ne "none") {
66+
$defaultMfaMethod = $registerationDetails.defaultMfaMethod
67+
}
6468
} else {
6569
Write-Warning "authentication method registration not found for $($InputObject.id)"
6670
}
@@ -81,6 +85,7 @@ function Get-AADAssessUserReport {
8185
"isMfaRegistered" = $isMfaRegistered
8286
"isMfaCapable" = $isMfaCapable
8387
"methodsRegistered" = $methodsRegistered
88+
"defaultMfaMethod" = $defaultMfaMethod
8489
}
8590
}
8691
}

src/Invoke-AADAssessmentDataCollection.ps1

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -64,13 +64,13 @@ function Invoke-AADAssessmentDataCollection {
6464
#$OutputDirectory = Join-Path $OutputDirectory "AzureADAssessment"
6565
$OutputDirectoryData = Join-Path $OutputDirectory "AzureADAssessmentData"
6666
$AssessmentDetailPath = Join-Path $OutputDirectoryData "AzureADAssessment.json"
67-
$PackagePath = Join-Path $OutputDirectory "AzureADAssessmentData.zip"
67+
$PackagePath = Join-Path $OutputDirectory "AzureADAssessmentData.aad"
6868

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

@@ -228,9 +228,10 @@ function Invoke-AADAssessmentDataCollection {
228228
$ReferencedIdCache.servicePrincipal.Clear()
229229

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

235236
### Registration details data - 15
236237
if ($licenseType -ne "Free") {
@@ -333,10 +334,16 @@ function Invoke-AADAssessmentDataCollection {
333334
}
334335

335336
if (!$SkipPackaging) {
337+
### Remove pre existing package (zip) if it exists
338+
if (Test-Path -Path $PackagePath) {
339+
Remove-Item $PackagePath -Force
340+
}
341+
342+
336343
### Package Output
337-
Compress-Archive (Join-Path $OutputDirectoryData '\*') -DestinationPath $PackagePath -Force -ErrorAction Stop
344+
#Compress-Archive (Join-Path $OutputDirectoryData '\*') -DestinationPath $PackagePath -Force -ErrorAction Stop
345+
[System.IO.Compression.ZipFile]::CreateFromDirectory($OutputDirectoryData,$PackagePath)
338346

339-
### Clean-Up Data Files
340347
Remove-Item $OutputDirectoryData -Recurse -Force
341348
}
342349

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

346353
}
347354
catch { if ($MyInvocation.CommandOrigin -eq 'Runspace') { Write-AppInsightsException $_.Exception }; throw }
348-
finally { Complete-AppInsightsRequest $MyInvocation.MyCommand.Name -Success $? }
355+
finally {
356+
# check generated package and issue warning
357+
$issue = $false
358+
if (!(Test-Path -PathType Leaf -Path $PackagePath) -and !$SkipPackaging) {
359+
Write-Warning "The export package has not been generated"
360+
$issue = $true
361+
} elseif (!$SkipPackaging) {
362+
if (!(Test-AADAssessmentPackage -Path $PackagePath -SkippedReportOutput $SkipReportOutput)) {
363+
Write-Warning "The generated package is missing some data"
364+
$issue = $true
365+
}
366+
}
367+
if ($issue) {
368+
Write-Warning "If you are working with microsoft or a provider on the assessment please warn them"
369+
Write-Warning "Please check GitHub issues and fill a new one or reply on existing ones mentionning the errors seen"
370+
Write-warning "https://github.com/AzureAD/AzureADAssessment/issues"
371+
}
372+
Complete-AppInsightsRequest $MyInvocation.MyCommand.Name -Success $?
373+
}
349374
}

src/New-AADAssessmentRecommendations.ps1

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ function New-AADAssessmentRecommendations {
2727
[string] $InterviewSpreadsheetPath
2828
)
2929

30-
#Start-AppInsightsRequest $MyInvocation.MyCommand.Name
30+
Start-AppInsightsRequest $MyInvocation.MyCommand.Name
3131

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

188-
#Complete-AppInsightsRequest $MyInvocation.MyCommand.Name -Success $?
188+
Complete-AppInsightsRequest $MyInvocation.MyCommand.Name -Success $?
189189
}
190190

191191
function Set-TypeQnAResult($data, $recommendationDef, $recommendation){

src/Test-AADAssessmentPackage.ps1

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
<#
2+
.SYNOPSIS
3+
Test that the provided Azure AD Assessment package has the necessary content
4+
.DESCRIPTION
5+
Test that the provided Azure AD Assessment package has the necessary content
6+
.EXAMPLE
7+
PS C:\>Test-AADAssessmentPackage 'C:\AzureADAssessmentData-contoso.aad'
8+
Test that the package for contoso has the necesary content for the assessment.
9+
.INPUTS
10+
System.String
11+
#>
12+
function Test-AADAssessmentPackage {
13+
[CmdletBinding()]
14+
param
15+
(
16+
# Path to the file where the exported events will be stored
17+
[Parameter(Mandatory = $true)]
18+
[string] $Path,
19+
# Reports should have been generated
20+
[Parameter(Mandatory = $false)]
21+
[bool] $SkippedReportOutput
22+
)
23+
24+
if (!(Test-Path -path $Path)) {
25+
Write-Warning "Assessment package not found"
26+
return $false
27+
}
28+
29+
$fullPath = Convert-Path $Path
30+
31+
$requiredEntries = @(
32+
"AAD-*/administrativeUnits.csv",
33+
"AAD-*/AppCredentialsReport.csv",
34+
"AAD-*/applications.json",
35+
"AAD-*/appRoleAssignments.csv",
36+
"AAD-*/conditionalAccessPolicies.json",
37+
"AAD-*/ConsentGrantReport.csv",
38+
"AAD-*/emailOTPMethodPolicy.json",
39+
"AAD-*/groups.csv",
40+
"AAD-*/namedLocations.json",
41+
"AAD-*/NotificationsEmailsReport.csv",
42+
"AAD-*/oauth2PermissionGrants.csv",
43+
"AAD-*/organization.json",
44+
"AAD-*/RoleAssignmentReport.csv",
45+
"AAD-*/roleDefinitions.csv",
46+
"AAD-*/servicePrincipals.csv",
47+
"AAD-*/servicePrincipals.json",
48+
"AAD-*/subscribedSkus.json",
49+
"AAD-*/userRegistrationDetails.json",
50+
"AAD-*/users.csv",
51+
"AzureADAssessment.json"
52+
)
53+
54+
if ($SkippedReportOutput) {
55+
$requiredEntries = @(
56+
"AAD-*/administrativeUnits.csv",
57+
"AAD-*/applicationData.xml",
58+
"AAD-*/appRoleAssignmentData.xml",
59+
"AAD-*/conditionalAccessPolicies.json",
60+
"AAD-*/directoryRoleData.xml"
61+
"AAD-*/emailOTPMethodPolicy.json",
62+
"AAD-*/groupData.xml",
63+
"AAD-*/namedLocations.json",
64+
"AAD-*/oauth2PermissionGrantData.xml",
65+
"AAD-*/organization.json",
66+
"AAD-*/roleAssignmentSchedulesData.xml",
67+
"AAD-*/roleDefinitions.csv",
68+
"AAD-*/roleEligibilitySchedulesData.xml",
69+
"AAD-*/servicePrincipalData.xml",
70+
"AAD-*/subscribedSkus.json",
71+
"AAD-*/userData.xml",
72+
"AAD-*/userRegistrationDetails.json",
73+
"AzureADAssessment.json"
74+
)
75+
}
76+
77+
$entries = [IO.Compression.ZipFile]::OpenRead($fullPath).Entries
78+
79+
$effectiveEntries = $entries | Where-Object { $_.Length -gt 0}
80+
81+
$validPackage = $true
82+
foreach($requiredEntry in $requiredEntries) {
83+
$found = $false
84+
foreach ($effectiveEntry in $effectiveEntries) {
85+
if (($effectiveEntry.FullName -replace "\\","/") -like $requiredEntry) {
86+
$found = $true
87+
}
88+
}
89+
if (!$found) {
90+
Write-Warning "Required entry '$requiredEntry' not found or empty"
91+
$validPackage = $false
92+
}
93+
}
94+
95+
# retrun package vaility
96+
return $validPackage
97+
}

src/internal/Expand-MsGraphRelationship.ps1

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,11 @@ function Expand-MsGraphRelationship {
5353
[array] $Results = $InputObjects[0..($BatchSize - 1)] | Get-MsGraphResults $uri -DisableUniqueIdDeduplication -GroupOutputByRequest
5454
}
5555
for ($i = 0; $i -lt $InputObjects.Count; $i++) {
56-
[array] $refValues = $Results[$i]
56+
$refValues = @()
57+
if ($i -lt $Results.Count) {
58+
[array] $refValues = $Results[$i]
59+
}
5760
if ($References) { $refValues = $refValues | Expand-ODataId | Select-Object -Property "*" -ExcludeProperty '@odata.id' }
58-
if ($null -eq $refValues) { $refValues = @() }
5961
$InputObjects[$i] | Add-Member -Name $PropertyName -MemberType NoteProperty -Value $refValues -PassThru -ErrorAction Ignore
6062
}
6163
$InputObjects.RemoveRange(0, $BatchSize)
@@ -72,9 +74,11 @@ function Expand-MsGraphRelationship {
7274
[array] $Results = $InputObjects | Get-MsGraphResults $uri -DisableUniqueIdDeduplication -GroupOutputByRequest
7375
}
7476
for ($i = 0; $i -lt $InputObjects.Count; $i++) {
75-
[array] $refValues = $Results[$i]
77+
$refValues = @()
78+
if ($Results.Count -gt $i) {
79+
[array] $refValues = $Results[$i]
80+
}
7681
if ($References) { $refValues = $refValues | Expand-ODataId | Select-Object -Property "*" -ExcludeProperty '@odata.id' }
77-
if ($null -eq $refValues) { $refValues = @() }
7882
$InputObjects[$i] | Add-Member -Name $PropertyName -MemberType NoteProperty -Value $refValues -PassThru -ErrorAction Ignore
7983
}
8084
}

0 commit comments

Comments
 (0)