Workloads/Get-UserData.ps1
|
# Get-UserData.ps1 # Collects users, guests, hybrid identity, global admins, MFA status, # enterprise applications, app registrations, and groups. # Part of the M365-QuickAssess module -- not exported. function Get-UserData { param ( $Assessment ) # ------------------------------------------------------------------- # Users # ------------------------------------------------------------------- try { Write-Log "Collecting user data" $users = Get-MgUser -All -Property UserType,OnPremisesSyncEnabled -ErrorAction Stop $totalUsers = $users.Count $guestUsers = ( $users | Where-Object { $_.UserType -eq "Guest" } ).Count $syncedUsers = ( $users | Where-Object { $_.OnPremisesSyncEnabled -eq $true } ).Count $Assessment.Summary.UserCount = $totalUsers $Assessment.Summary.GuestUserCount = $guestUsers $Assessment.Summary.IsHybridIdentity = ( $syncedUsers -gt 0 ) $Assessment.Summary.HasDirectorySync = ( $syncedUsers -gt 0 ) Write-Log "Users: Total=$totalUsers Guests=$guestUsers Synced=$syncedUsers" # ------------------------------------------------------------------- # Finding: High guest user count # ------------------------------------------------------------------- if ( $totalUsers -gt 0 ) { $guestPercent = [math]::Round( ( $guestUsers / $totalUsers ) * 100, 0 ) if ( $guestPercent -ge 20 ) { $Assessment.Findings += New-Finding ` -Type "HighGuestUserCount" ` -Summary "$guestUsers guest users detected ($guestPercent% of tenant)" ` -Category "Identity" ` -Severity "Medium" ` -Impact "High guest populations require careful handling during migration -- guest accounts do not migrate with standard tooling." ` -Recommendation "Review guest accounts and determine which require access in the target tenant." } } } catch { Write-Log "User data collection failed: $( $_.Exception.Message )" "ERROR" } # ------------------------------------------------------------------- # Global Admins # ------------------------------------------------------------------- try { Write-Log "Collecting global admin count" $roles = Get-MgDirectoryRole -All -ErrorAction Stop $globalAdminRole = $roles | Where-Object { $_.DisplayName -eq "Global Administrator" } if ( $globalAdminRole ) { $globalAdmins = Get-MgDirectoryRoleMember -DirectoryRoleId $globalAdminRole.Id -All $adminCount = ( $globalAdmins | Measure-Object ).Count $Assessment.Summary.GlobalAdminCount = $adminCount Write-Log "Global Admins: $adminCount" # ------------------------------------------------------------------- # Finding: Too many global admins # ------------------------------------------------------------------- if ( $adminCount -gt 5 ) { $Assessment.Findings += New-Finding ` -Type "ExcessiveGlobalAdmins" ` -Summary "$adminCount Global Administrators detected" ` -Category "Security" ` -Severity "High" ` -Impact "Excessive Global Admin accounts increase the attack surface and risk of privilege abuse." ` -Recommendation "Reduce Global Admin count to 2-4 accounts. Use role-specific admin roles where possible." } } else { $Assessment.Summary.GlobalAdminCount = 0 } } catch { Write-Log "Global admin count failed: $( $_.Exception.Message )" "WARN" } # ------------------------------------------------------------------- # MFA Registration # ------------------------------------------------------------------- try { Write-Log "Collecting MFA registration data" $mfa = Get-MgReportAuthenticationMethodUserRegistrationDetail -All -ErrorAction Stop $mfaUsers = ( $mfa | Where-Object { $_.IsMfaRegistered -eq $true } ).Count if ( $totalUsers -gt 0 ) { $mfaPercent = [math]::Round( ( $mfaUsers / $totalUsers ) * 100, 0 ) $Assessment.Summary.MFAEnabledPercent = $mfaPercent Write-Log "MFA: $mfaUsers / $totalUsers registered ($mfaPercent%)" # ------------------------------------------------------------------- # Finding: Low MFA adoption # ------------------------------------------------------------------- if ( $mfaPercent -lt 50 ) { $Assessment.Findings += New-Finding ` -Type "LowMFAAdoption" ` -Summary "Only $mfaPercent% of users have MFA registered" ` -Category "Security" ` -Severity "High" ` -Impact "Low MFA adoption significantly increases account compromise risk during and after migration." ` -Recommendation "Enforce MFA registration for all users before migration begins." } elseif ( $mfaPercent -lt 80 ) { $Assessment.Findings += New-Finding ` -Type "ModerateMFAAdoption" ` -Summary "$mfaPercent% of users have MFA registered" ` -Category "Security" ` -Severity "Medium" ` -Impact "A significant portion of users are not MFA registered." ` -Recommendation "Review users without MFA and enforce registration prior to migration." } } } catch { if ( $_.Exception.Message -match 'NonPremiumTenant|PremiumLicense|Authentication_RequestFromNonPremiumTenant' ) { Write-Log "MFA registration details unavailable -- tenant does not have Entra ID P1/P2" "WARN" $Assessment.Summary.MFAEnabledPercent = $null $Assessment.Findings += New-Finding ` -Type "MFADataUnavailable" ` -Summary "MFA registration data could not be collected -- Entra ID P1/P2 license required" ` -Category "Security" ` -Severity "Info" ` -Impact "MFA registration status cannot be assessed without an Entra ID P1 or P2 license." ` -Recommendation "Manually verify MFA adoption in the source tenant before migration." } else { Write-Log "MFA registration data failed: $( $_.Exception.Message )" "WARN" } } # ------------------------------------------------------------------- # Enterprise Applications # ------------------------------------------------------------------- try { Write-Log "Collecting application data" $servicePrincipals = Get-MgServicePrincipal -All -ErrorAction Stop # ------------------------------------------------------------------- # Custom apps -- owned by this tenant # ------------------------------------------------------------------- $customerApps = $servicePrincipals | Where-Object { $_.AccountEnabled -eq $true -and $_.ServicePrincipalType -eq "Application" -and $_.AppOwnerOrganizationId -eq $script:Context.TenantId } $customerCount = ( $customerApps | Measure-Object ).Count # ------------------------------------------------------------------- # Managed Identities -- tenant-specific, good Azure signal # ------------------------------------------------------------------- $managedIdentities = $servicePrincipals | Where-Object { $_.ServicePrincipalType -eq "ManagedIdentity" } $managedIdentityCount = ( $managedIdentities | Measure-Object ).Count # ------------------------------------------------------------------- # App Registrations # ------------------------------------------------------------------- $applications = Get-MgApplication -All -ErrorAction Stop $appCount = ( $applications | Measure-Object ).Count # ------------------------------------------------------------------- # Populate Schema # ------------------------------------------------------------------- $Assessment.Applications.HasEnterpriseApplications = ( $customerCount -gt 0 ) $Assessment.Applications.CustomerOwnedApps = $customerCount $Assessment.Applications.ManagedIdentities = $managedIdentityCount $Assessment.Applications.HasAppRegistrations = ( $appCount -gt 0 ) $Assessment.Applications.AppRegistrations = $appCount Write-Log "Applications: CustomerOwned=$customerCount ManagedIdentities=$managedIdentityCount AppRegistrations=$appCount" # ------------------------------------------------------------------- # Findings # ------------------------------------------------------------------- $Assessment.Findings += New-Finding ` -Type "EnterpriseApplicationsSummary" ` -Summary "$customerCount custom applications detected in tenant" ` -Category "Configuration" ` -Severity "Info" if ( $customerCount -gt 0 ) { $Assessment.Findings += New-Finding ` -Type "CustomerApplications" ` -Summary "$customerCount customer-owned applications detected" ` -Category "Configuration" ` -Severity "Medium" ` -Details ( $customerApps | Select-Object -ExpandProperty DisplayName ) ` -Impact "Custom applications will require re-registration and reconfiguration in the target tenant." ` -Recommendation "Inventory all custom app registrations, their permissions, and any dependent services before migration." } if ( $managedIdentityCount -gt 0 ) { $Assessment.Findings += New-Finding ` -Type "ManagedIdentitiesDetected" ` -Summary "$managedIdentityCount managed identities detected" ` -Category "Configuration" ` -Severity "Info" ` -Impact "Managed identities are tied to Azure resources and will need to be recreated in the target tenant." ` -Recommendation "Review managed identities and associated Azure resources as part of migration planning." } } catch { Write-Log "Application data collection failed: $( $_.Exception.Message )" "ERROR" } # ------------------------------------------------------------------- # Groups # ------------------------------------------------------------------- try { Write-Log "Collecting group data" $groups = Get-MgGroup -All -Property GroupTypes,SecurityEnabled,MailEnabled,MembershipRule,DisplayName -ErrorAction Stop $totalGroups = $groups.Count $m365Groups = ( $groups | Where-Object { $_.GroupTypes -contains "Unified" } ).Count $securityGroups = ( $groups | Where-Object { $_.SecurityEnabled -eq $true -and $_.MailEnabled -eq $false } ).Count $mailEnabledSecurityGroups = ( $groups | Where-Object { $_.SecurityEnabled -eq $true -and $_.MailEnabled -eq $true } ).Count $distributionLists = ( $groups | Where-Object { $_.SecurityEnabled -eq $false -and $_.MailEnabled -eq $true } ).Count $dynamicGroups = $groups | Where-Object { -not [string]::IsNullOrEmpty( $_.MembershipRule ) } $dynamicCount = $dynamicGroups.Count $dynamicM365 = ( $dynamicGroups | Where-Object { $_.GroupTypes -contains "Unified" } ).Count $dynamicSecurity = ( $dynamicGroups | Where-Object { $_.SecurityEnabled -eq $true } ).Count $Assessment.Groups.TotalGroups = $totalGroups $Assessment.Groups.M365Groups = $m365Groups $Assessment.Groups.SecurityGroups = $securityGroups $Assessment.Groups.MailEnabledSecurityGroups = $mailEnabledSecurityGroups $Assessment.Groups.DistributionLists = $distributionLists $Assessment.Groups.DynamicGroupsTotal = $dynamicCount $Assessment.Groups.DynamicM365Groups = $dynamicM365 $Assessment.Groups.DynamicSecurityGroups = $dynamicSecurity Write-Log "Groups: Total=$totalGroups M365=$m365Groups Security=$securityGroups DL=$distributionLists Dynamic=$dynamicCount" # ------------------------------------------------------------------- # Finding: Dynamic Groups # ------------------------------------------------------------------- if ( $dynamicCount -gt 0 ) { $dynamicDetails = $dynamicGroups | ForEach-Object { "$( $_.DisplayName ) | Rule: $( $_.MembershipRule )" } $Assessment.Findings += New-Finding ` -Type "DynamicGroups" ` -Summary "$dynamicCount dynamic groups detected -- manual recreation required" ` -Category "Identity" ` -Severity "High" ` -Details $dynamicDetails ` -Impact "Dynamic group memberships are not directly migrated and must be recreated manually in the target tenant." ` -Recommendation "Document all dynamic group membership rules and plan recreation in the target tenant." } } catch { Write-Log "Group data collection failed: $( $_.Exception.Message )" "ERROR" } } |