diff --git a/src/Export-MsIdAppConsentGrantReport.ps1 b/src/Export-MsIdAppConsentGrantReport.ps1 index 4197619..b434dfa 100644 --- a/src/Export-MsIdAppConsentGrantReport.ps1 +++ b/src/Export-MsIdAppConsentGrantReport.ps1 @@ -54,10 +54,8 @@ function Export-MsIdAppConsentGrantReport { $ThrottleLimit = 20 ) - $script:ObjectByObjectId = @{} # Cache for all directory objects - $script:KnownMSTenantIds = @("f8cdef31-a31e-4b4a-93e4-5f571e91255a", "72f988bf-86f1-41af-91ab-2d7cd011db47") - - function Main() { + begin{ + ## Initialize Critical Dependencies if ("ExcelWorkbook" -eq $ReportOutputType) { # Determine if the ImportExcel module is installed since the parameter was included if ($null -eq (Get-Module -Name ImportExcel -ListAvailable)) { @@ -69,6 +67,16 @@ function Export-MsIdAppConsentGrantReport { Connect-MgGraph -Scopes Directory.Read.All } + $CriticalError = $null + if (!(Test-MgCommandPrerequisites 'Get-MgServicePrincipal', 'Get-MgDirectoryObjectById' -ErrorVariable CriticalError)) { return } + } + + process{ + if ($CriticalError) { return } + $script:ObjectByObjectId = @{} # Cache for all directory objects + $script:KnownMSTenantIds = @("f8cdef31-a31e-4b4a-93e4-5f571e91255a", "72f988bf-86f1-41af-91ab-2d7cd011db47") + + function Main() { $appConsents = GetAppConsentGrants if ($null -ne $appConsents) { @@ -97,11 +105,30 @@ function Export-MsIdAppConsentGrantReport { Write-Verbose "Retrieving ServicePrincipal objects..." WriteMainProgress ServicePrincipal -Status "This can take some time..." -ForceRefresh - $count = Get-MgServicePrincipalCount -ConsistencyLevel eventual + try { + $count = Get-MgServicePrincipalCount -ConsistencyLevel eventual + } + catch [System.Management.Automation.CommandNotFoundException] { + Write-Verbose "$_.Exception.Message" + Write-Error "The Get-MgServicePrincipalCount cmdlet is not available. Please install the latest version of the Microsoft.Graph module." + } + catch { + Write-Verbose "$_.Exception.Message" + } WriteMainProgress ServicePrincipal -ChildPercent 5 -Status "Retrieving $count service principals. This can take some time..." -ForceRefresh Start-Sleep -Milliseconds 500 #Allow message to update $servicePrincipalProps = "id,appId,appOwnerOrganizationId,displayName,appRoles,appRoleAssignmentRequired" - $script:ServicePrincipals = Get-MgServicePrincipal -ExpandProperty "appRoleAssignments" -Select $servicePrincipalProps -All -PageSize 999 + try { + $script:ServicePrincipals = Get-MgServicePrincipal -ExpandProperty "appRoleAssignments" -Select $servicePrincipalProps -All -PageSize 999 + } + catch [System.Management.Automation.CommandNotFoundException] { + Write-Verbose "$_.Exception.Message" + Write-Error "The Get-MgServicePrincipal cmdlet is not available. Please install the latest version of the Microsoft.Graph module." + } + catch { + Write-Verbose "$_.Exception.Message" + } + $appPerms = GetApplicationPermissions @@ -127,7 +154,11 @@ function Export-MsIdAppConsentGrantReport { $object = (Get-MgDirectoryObjectById -Ids $ObjectId) CacheObject -Object $object } + catch [System.Management.Automation.CommandNotFoundException] { + Write-Verbose "$_.Exception.Message" + } catch { + Write-Verbose "$_.Exception.Message" Write-Verbose "Object not found." } } @@ -234,6 +265,11 @@ function Export-MsIdAppConsentGrantReport { $dictFailed.TryAdd($servicePrincipalId, "Failed to add service principal $servicePrincipalId") | Out-Null } } + catch [System.Management.Automation.CommandNotFoundException] { + Write-Verbose "$_.Exception.Message" + Write-Verbose "The Get-MgServicePrincipalOauth2PermissionGrant cmdlet is not available. Please install the latest version of the Microsoft.Graph module." + $dictFailed.TryAdd($servicePrincipalId, $_) | Out-Null + } catch { $dictFailed.TryAdd($servicePrincipalId, $_) | Out-Null } @@ -613,4 +649,5 @@ function Export-MsIdAppConsentGrantReport { # Call main function Main + } } diff --git a/src/MSIdentityTools.psd1 b/src/MSIdentityTools.psd1 index 5ba9b25..c0dd2bb 100644 --- a/src/MSIdentityTools.psd1 +++ b/src/MSIdentityTools.psd1 @@ -25,7 +25,7 @@ CompanyName = 'Microsoft Corporation' # Copyright statement for this module - Copyright = '(c) 2023 Microsoft Corporation. All rights reserved.' + Copyright = '(c) 2024 Microsoft Corporation. All rights reserved.' # Description of the functionality provided by this module Description = 'Tools for managing, troubleshooting, and reporting on various aspects of Microsoft Identity products and services, primarily Microsoft Entra ID.' @@ -131,7 +131,7 @@ '.\New-MsIdSamlRequest.ps1' '.\New-MsIdTemporaryUserPassword.ps1' '.\New-MsIdWsTrustRequest.ps1' - '.\Remove-MsidUserAuthenticationMethod.ps1' + '.\Remove-MsIdUserAuthenticationMethod.ps1' '.\Reset-MsIdExternalUser.ps1' '.\Resolve-MsIdAzureIpAddress.ps1' '.\Revoke-MsIdServicePrincipalConsent.ps1' @@ -191,7 +191,7 @@ 'New-MsIdClientSecret' 'New-MsIdSamlRequest' 'New-MsIdTemporaryUserPassword' - 'Remove-MsidUserAuthenticationMethod' + 'Remove-MsIdUserAuthenticationMethod' 'Reset-MsIdExternalUser' 'Resolve-MsIdTenant' 'Revoke-MsIdServicePrincipalConsent' diff --git a/src/Remove-MsidUserAuthenticationMethod.ps1 b/src/Remove-MsidUserAuthenticationMethod.ps1 index 7923df6..0468cbc 100644 --- a/src/Remove-MsidUserAuthenticationMethod.ps1 +++ b/src/Remove-MsidUserAuthenticationMethod.ps1 @@ -11,11 +11,11 @@ .EXAMPLE Connect-MgGraph -Scopes UserAuthenticationMethod.ReadWrite.All - - Remove-MsidUserAuthenticationMethod -UserId john@contoso.com - + Remove-MsIdUserAuthenticationMethod -UserId john@contoso.com + + This example deletes all the authentication methods for the user #> -function Remove-MsidUserAuthenticationMethod { +function Remove-MsIdUserAuthenticationMethod { [CmdletBinding(HelpUri = 'https://azuread.github.io/MSIdentityTools/commands/Remove-MsidUserAuthenticationMethod')] param ( # The user UPN or ID to delete the authentication methods for. diff --git a/src/Test-MsIdCBATrustStoreConfiguration.ps1 b/src/Test-MsIdCBATrustStoreConfiguration.ps1 index 14ebac6..94d6398 100644 --- a/src/Test-MsIdCBATrustStoreConfiguration.ps1 +++ b/src/Test-MsIdCBATrustStoreConfiguration.ps1 @@ -24,6 +24,8 @@ .EXAMPLE Test-MsIdCBATrustStoreConfiguration + + Run tests against the current tenant's Certificate Trust Store .LINK https://aka.ms/aadcba diff --git a/src/internal/Test-MgCommandPrerequisites.ps1 b/src/internal/Test-MgCommandPrerequisites.ps1 index 341518d..7826193 100644 --- a/src/internal/Test-MgCommandPrerequisites.ps1 +++ b/src/internal/Test-MgCommandPrerequisites.ps1 @@ -45,10 +45,13 @@ function Test-MgCommandPrerequisites { ## Get Graph Command Details [hashtable] $MgCommandLookup = @{} foreach ($CommandName in $Name) { - [array] $MgCommands = Find-MgGraphCommand -Command $CommandName -ApiVersion $ApiVersion - - if ($MgCommands.Count -gt 1) { + [array] $MgCommands = Find-MgGraphCommand -Command $CommandName -ApiVersion $ApiVersion -ErrorAction Break + + if ($MgCommands.Count -eq 1) { + $MgCommand = $MgCommands[0] + } + elseif ($MgCommands.Count -gt 1) { $MgCommand = $MgCommands[0] ## Resolve from multiple results [array] $MgCommandsWithPermissions = $MgCommands | Where-Object Permissions -NE $null @@ -65,7 +68,12 @@ function Test-MgCommandPrerequisites { } } - $MgCommandLookup[$MgCommand.Command] = $MgCommand + if ($MgCommand) { + $MgCommandLookup[$MgCommand.Command] = $MgCommand + } + else { + Write-Error "Unable to resolve a specific command for '$CommandName'." + } } ## Import Required Modules diff --git a/tests/Test-MgCommandPrerequisites.tests.ps1 b/tests/Test-MgCommandPrerequisites.tests.ps1 index ee1f418..c41d86d 100644 --- a/tests/Test-MgCommandPrerequisites.tests.ps1 +++ b/tests/Test-MgCommandPrerequisites.tests.ps1 @@ -66,8 +66,8 @@ Describe 'Test-MgCommandPrerequisites' { Context 'Name: ' -ForEach @( @{ Name = 'Get-MgUser'; Expected = $true } - @{ Name = 'Get-MgUser'; ApiVersion = 'Beta'; Expected = $true } - @{ Name = 'Get-MgUser'; ApiVersion = 'Beta'; MinimumVersion = '1.0'; Expected = $true } + @{ Name = 'Get-MgUser'; ApiVersion = 'V1.0'; Expected = $true } + @{ Name = 'Get-MgUser'; ApiVersion = 'V1.0'; MinimumVersion = '1.0'; Expected = $true } ) { BeforeAll { InModuleScope $PSModule.Name -ArgumentList $_ { @@ -102,6 +102,46 @@ Describe 'Test-MgCommandPrerequisites' { } } + Context 'Find-MgGraphCommand: Returns Single Command' { + BeforeAll { + Mock -ModuleName $PSModule.Name Find-MgGraphCommand { + New-Object Microsoft.Graph.PowerShell.Authentication.Models.GraphCommand -Property @{ + Command = 'Get-MgDirectoryObjectById' + Module = 'Users' + APIVersion = 'v1.0' + Method = 'POST' + URI = '/directoryObjects/getByIds' + Permissions = @( + ) + } + } -ParameterFilter { $Command -eq 'Get-MgUser' } -Verifiable + } + + It 'Positional Parameter' { + InModuleScope $PSModule.Name -ArgumentList $TestCases[0] { + $Output = Test-MgCommandPrerequisites 'Get-MgUser' -ErrorVariable actualErrors + $Output | Should -BeOfType [bool] + $Output | Should -BeExactly $true + Should -Invoke Find-MgGraphCommand -ParameterFilter { + $Command -eq 'Get-MgUser' + } + $actualErrors | Should -HaveCount 0 + } + } + + It 'Pipeline Input' { + InModuleScope $PSModule.Name -ArgumentList $TestCases[0] { + $Output = 'Get-MgUser' | Test-MgCommandPrerequisites -ErrorVariable actualErrors + $Output | Should -BeOfType [bool] + $Output | Should -BeExactly $true + Should -Invoke Find-MgGraphCommand -ParameterFilter { + $Command -eq 'Get-MgUser' + } + $actualErrors | Should -HaveCount 0 + } + } + } + Context 'Multiple Input' { It 'Positional Parameter' {