Private/Utils.ps1
|
<# .SYNOPSIS Converts a StoreId to a RestId. .DESCRIPTION This internal helper function takes a folder StoreId and converts it to a RestId that can be used with the Microsoft Graph API. .PARAMETER StoreId The StoreId of the folder. .PARAMETER UserId The Id of the user who owns the folder. .NOTES This function is not intended to be called directly. #> function Convert-StoreIdToRestId { [CmdletBinding()] param( [string]$StoreId, [string]$UserId ) # convert from base64 to bytes $folderIdBytes = [Convert]::FromBase64String($StoreId) # convert byte array to string, remove '-' and ignore first byte $folderIdHexString = [System.BitConverter]::ToString($folderIdBytes).Replace('-','') $folderIdHexStringLength = $folderIdHexString.Length # get hex entry id string by removing first and last byte $entryIdHexString = $folderIdHexString.SubString(2,($folderIdHexStringLength-4)) # convert to byte array - two chars represents one byte $entryIdBytes = [byte[]]::new($entryIdHexString.Length / 2) for($i=0; $i -lt $entryIdHexString.Length; $i+=2){ $entryIdTwoChars = $entryIdHexString.Substring($i, 2) $entryIdBytes[$i/2] = [convert]::ToByte($entryIdTwoChars, 16) } # convert bytes to base64 string $entryIdBase64 = [Convert]::ToBase64String($entryIdBytes) # count how many '=' contained in base64 entry id $equalCharCount = $entryIdBase64.Length - $entryIdBase64.Replace('=','').Length # trim '=', replace '/' with '-', replace '+' with '_' and add number of '=' at the end $EntryId = $entryIdBase64.TrimEnd('=').Replace('/','_').Replace('+','-')+$equalCharCount # Now convert the entryId to be a RestId using the translateExchangeIds API $Body = @{} [array]$InputId = $EntryId $Body.Add("inputIds", $InputId) $Body.Add("sourceIdType", "entryId") $Body.Add("targetIdType", "restid") $Data = Invoke-MgTranslateUserExchangeId -UserId $UserId -BodyParameter $Body return $Data.targetId } <# .SYNOPSIS Gets a list of administrative users. .DESCRIPTION This internal helper function gets a list of users with the 'Global Administrator' or 'User Administrator' roles. .NOTES This function is not intended to be called directly. #> function Get-O365Admin { [CmdletBinding()] param() $AdminRoleHolders = [System.Collections.Generic.List[Object]]::new() [array]$AdminRoles = Get-MgDirectoryRole | Select-Object DisplayName, Id | Sort-Object DisplayName $AdminRoles = $AdminRoles | Where-Object {$_.DisplayName -eq "Global Administrator" -or $_.DisplayName -eq "User Administrator"} ForEach ($Role in $AdminRoles) { [array]$RoleMembers = Get-MgDirectoryRoleMember -DirectoryRoleId $Role.Id | Where-Object {$_.AdditionalProperties."@odata.type" -eq "#microsoft.graph.user"} ForEach ($Member in $RoleMembers) { $UserDetails = Get-MgUser -UserId $Member.Id $ReportLine = [PSCustomObject] @{ User = $UserDetails.UserPrincipalName Id = $UserDetails.Id Role = $Role.DisplayName RoleId = $Role.Id Mail = $UserDetails.Mail } $AdminRoleHolders.Add($ReportLine) } } return $AdminRoleHolders | Sort-Object User -Unique } <# .SYNOPSIS Recursively gets all files and folders from a user's OneDrive. .DESCRIPTION This internal helper function recursively gets all files and folders from a user's OneDrive. .PARAMETER UserId The Id of the user. .PARAMETER FolderId The Id of the folder to start from. .PARAMETER RelativePath The relative path of the folder. .NOTES This function is not intended to be called directly. #> function Get-O365UserOneDriveFilesRecursive { [CmdletBinding()] param( [string]$UserId, [string]$FolderId, [string]$RelativePath = '' ) $Items = Get-MgUserDriveItemChild -UserId $UserId -DriveId $UserId -DriveItemId $FolderId foreach ($Item in $Items) { if ($Item.Folder) { $NewPath = Join-Path -Path $RelativePath -ChildPath $Item.Name Get-O365UserOneDriveFilesRecursive -UserId $UserId -FolderId $Item.Id -RelativePath $NewPath } else { $Item | Add-Member -MemberType NoteProperty -Name "RelativePath" -Value $RelativePath -PassThru } } } <# .SYNOPSIS Downloads a file from OneDrive. .DESCRIPTION This internal helper function downloads a file from OneDrive to a local path. .PARAMETER UserId The Id of the user. .PARAMETER DriveItemId The Id of the file to download. .PARAMETER LocalPath The local path to save the file to. .PARAMETER RelativePath The relative path of the file. .NOTES This function is not intended to be called directly. #> function Download-O365File { [CmdletBinding()] param( [string]$UserId, [string]$DriveItemId, [string]$LocalPath, [string]$RelativePath ) $File = Get-MgUserDriveItem -UserId $UserId -DriveId $UserId -DriveItemId $DriveItemId $DownloadUrl = $File.AdditionalProperties."@microsoft.graph.downloadUrl" $FullDirectoryPath = Join-Path -Path $LocalPath -ChildPath $RelativePath if (-not (Test-Path -Path $FullDirectoryPath)) { New-Item -ItemType Directory -Path $FullDirectoryPath } $OutputPath = Join-Path -Path $FullDirectoryPath -ChildPath $File.Name Invoke-RestMethod -Uri $DownloadUrl -OutFile $OutputPath } <# .SYNOPSIS Removes a user from all Microsoft 365 groups. .DESCRIPTION This internal helper function removes a user from all Microsoft 365 groups they are a member of. .PARAMETER UserId The Id of the user. .NOTES This function is not intended to be called directly. #> function Remove-O365UserFromGroups { [CmdletBinding()] param( [string]$UserId ) $Groups = Get-MgUserMemberOf -UserId $UserId foreach ($Group in $Groups) { if ($Group.AdditionalProperties."@odata.type" -eq "#microsoft.graph.group") { Write-Verbose "Removing user from group: $($Group.DisplayName)" Remove-MgGroupMemberByRef -GroupId $Group.Id -DirectoryObjectId $UserId } } } <# .SYNOPSIS Gets admins who haven't signed in for a specified number of days. .DESCRIPTION This internal helper function gets a list of administrative users who have not signed in for a specified number of days. .PARAMETER Days The number of days to check for stale sign-ins. .NOTES This function is not intended to be called directly. #> function Get-O365StaleAdminSignIns { [CmdletBinding()] param( [int]$Days = 30 ) $Admins = Get-O365Admin $StaleAdmins = [System.Collections.Generic.List[Object]]::new() foreach ($Admin in $Admins) { $lastSignIn = (Get-MgUser -UserId $Admin.Id -Property 'signInActivity').SignInActivity.LastSignInDateTime if ($lastSignIn -and $lastSignIn -lt (Get-Date).AddDays(-$Days)) { $StaleAdmins.Add($Admin) } } return $StaleAdmins } <# .SYNOPSIS Gets guest users who haven't signed in for a specified number of days. .DESCRIPTION This internal helper function gets a list of guest users who have not signed in for a specified number of days. .PARAMETER Days The number of days to check for stale sign-ins. .NOTES This function is not intended to be called directly. #> function Get-O365InactiveGuestUsers { [CmdletBinding()] param( [int]$Days = 30 ) $Guests = Get-MgUser -Filter "userType eq 'Guest'" -All -Property 'signInActivity' $InactiveGuests = [System.Collections.Generic.List[Object]]::new() foreach ($Guest in $Guests) { $lastSignIn = $Guest.SignInActivity.LastSignInDateTime if ($lastSignIn -and $lastSignIn -lt (Get-Date).AddDays(-$Days)) { $InactiveGuests.Add($Guest) } } return $InactiveGuests } <# .SYNOPSIS Gets users who have not registered for MFA. .DESCRIPTION This internal helper function gets a list of users who have not registered for MFA. .NOTES This function is not intended to be called directly. #> function Get-O365UsersWithoutMFA { [CmdletBinding()] param() $Users = Get-MgUser -All -Property 'displayName,userPrincipalName' $UsersWithoutMFA = [System.Collections.Generic.List[Object]]::new() foreach ($User in $Users) { $MfaMethods = Get-MgUserAuthenticationMethod -UserId $User.Id if ($MfaMethods.Count -eq 0) { $UsersWithoutMFA.Add($User) } } return $UsersWithoutMFA } <# .SYNOPSIS Gets mailboxes with forwarding rules to external domains. .DESCRIPTION This internal helper function gets a list of mailboxes that have forwarding rules to external domains. .NOTES This function is not intended to be called directly. #> function Get-O365MailboxForwardingRules { [CmdletBinding()] param() $Mailboxes = Get-Mailbox -ResultSize Unlimited $ForwardingMailboxes = [System.Collections.Generic.List[Object]]::new() $AcceptedDomains = Get-AcceptedDomain | Select-Object -ExpandProperty DomainName foreach ($Mailbox in $Mailboxes) { if ($Mailbox.ForwardingSmtpAddress) { $Domain = $Mailbox.ForwardingSmtpAddress.Split('@')[1] if ($Domain -notin $AcceptedDomains) { $ForwardingMailboxes.Add($Mailbox) } } } return $ForwardingMailboxes } <# .SYNOPSIS Gets service principals with no sign-ins. .DESCRIPTION This internal helper function gets a list of service principals that have not been signed into. .NOTES This function is not intended to be called directly. #> function Get-O356UnsignedInServicePrincipals { [CmdletBinding()] param() $ServicePrincipals = Get-MgServicePrincipal -All $UnsignedInServicePrincipals = [System.Collections.Generic.List[Object]]::new() foreach ($SP in $ServicePrincipals) { $SignIn = Get-MgAuditLogSignIn -Filter "servicePrincipalId eq '$($SP.Id)'" -Top 1 if (-not $SignIn) { $UnsignedInServicePrincipals.Add($SP) } } return $UnsignedInServicePrincipals } <# .SYNOPSIS Adds a user to the same groups as a template user. .DESCRIPTION This internal helper function adds a user to the same Microsoft 365 groups as a template user. .PARAMETER UserId The Id of the user to add to the groups. .PARAMETER TemplateUserId The Id of the template user. .NOTES This function is not intended to be called directly. #> function Add-O365UserToGroupsFromTemplate { [CmdletBinding()] param( [string]$UserId, [string]$TemplateUserId ) $Groups = Get-MgUserMemberOf -UserId $TemplateUserId foreach ($Group in $Groups) { if ($Group.AdditionalProperties."@odata.type" -eq "#microsoft.graph.group") { Write-Verbose "Adding user to group: $($Group.DisplayName)" Add-MgGroupMember -GroupId $Group.Id -DirectoryObjectId $UserId } } } <# .SYNOPSIS Gets users with a specific license who have not signed in for a certain number of days. .DESCRIPTION This internal helper function gets a list of users with a specific license who have not signed in for a certain number of days. .PARAMETER SkuId The SKU ID of the license to check. .PARAMETER Days The number of days to check for stale sign-ins. .NOTES This function is not intended to be called directly. #> function Get-O365UnderutilizedLicense { [CmdletBinding()] param( [string]$SkuId, [int]$Days = 30 ) $Users = Get-MgUser -Filter "assignedLicenses/any(x:x/skuId eq $SkuId)" -All -Property 'displayName,userPrincipalName,signInActivity' $UnderutilizedUsers = [System.Collections.Generic.List[Object]]::new() foreach ($User in $Users) { $lastSignIn = $User.SignInActivity.LastSignInDateTime if ($lastSignIn -and $lastSignIn -lt (Get-Date).AddDays(-$Days)) { $UnderutilizedUsers.Add($User) } } return $UnderutilizedUsers } <# .SYNOPSIS Gets all SharePoint Online sites. .DESCRIPTION This internal helper function gets all SharePoint Online sites. .NOTES This function is not intended to be called directly. #> function Get-SPOAllSites { [CmdletBinding()] param() return Get-SPOSite -Limit All } <# .SYNOPSIS Gets all externally shared items for a given site. .DESCRIPTION This internal helper function gets all externally shared files and folders for a given site. .PARAMETER SiteUrl The URL of the site to check. .NOTES This function is not intended to be called directly. #> function Get-SPOExternallySharedItems { [CmdletBinding()] param( [string]$SiteUrl ) Connect-PnPOnline -Url $SiteUrl -Interactive $ExternallySharedItems = [System.Collections.Generic.List[Object]]::new() $Lists = Get-PnPList foreach ($List in $Lists) { $Items = Get-PnPListItem -List $List.Title foreach ($Item in $Items) { if ($Item.HasUniqueRoleAssignments) { $RoleAssignments = Get-PnPProperty -ClientObject $Item -Property RoleAssignments foreach ($RoleAssignment in $RoleAssignments) { $Member = Get-PnPProperty -ClientObject $RoleAssignment -Property Member if ($Member.PrincipalType -eq 'User' -and $Member.LoginName -match '#ext#') { $ExternallySharedItems.Add($Item) } } } } } return $ExternallySharedItems } <# .SYNOPSIS Gets the current global tenant settings (External Sharing, Guest Access). .DESCRIPTION This internal helper function retrieves the current global tenant settings related to external sharing and guest access. .NOTES This function is not intended to be called directly. #> function Get-O365GlobalTenantSettings { [CmdletBinding()] param() # Placeholder for actual cmdlet calls # In a real scenario, this would involve cmdlets like Get-MsolDirSyncFeatures, Get-SPOTenant, Get-AzureADPolicy, etc. # For now, we will return a dummy object. return [PSCustomObject]@{ ExternalSharingEnabled = $true # Placeholder GuestAccessAllowed = $true # Placeholder } } <# .SYNOPSIS Sets the global tenant settings (External Sharing, Guest Access). .DESCRIPTION This internal helper function sets the global tenant settings related to external sharing and guest access. .PARAMETER ExternalSharingEnabled Set to $true to enable external sharing, $false to disable. .PARAMETER GuestAccessAllowed Set to $true to allow guest access, $false to deny. .NOTES This function is not intended to be called directly. #> function Set-O365GlobalTenantSettings { [CmdletBinding()] param( [bool]$ExternalSharingEnabled, [bool]$GuestAccessAllowed ) Write-Verbose "Setting ExternalSharingEnabled to $ExternalSharingEnabled" Write-Verbose "Setting GuestAccessAllowed to $GuestAccessAllowed" # Placeholder for actual cmdlet calls # For example: Set-SPOTenant -ExternalSharing $ExternalSharingEnabled; Set-MsolDirSyncFeatures -Feature GuestAccess -Enable $GuestAccessAllowed } <# .SYNOPSIS Gets the current MFA status for all users. .DESCRIPTION This internal helper function retrieves the current MFA status for all users in the tenant. .NOTES This function is not intended to be called directly. #> function Get-O365UserMFAStatus { [CmdletBinding()] param() # Placeholder for actual cmdlet calls # In a real scenario, this would involve Get-MsolUser or checking authentication methods via Graph. # For now, we return a dummy object indicating some users are not MFA enabled. return [PSCustomObject]@{ AllUsersMFAEnabled = $false # Placeholder ExceptionUsers = @('user1@contoso.com', 'user2@contoso.com') # Placeholder } } <# .SYNOPSIS Sets the MFA status for users. .DESCRIPTION This internal helper function sets the MFA status for users. .PARAMETER AllUsersMFAEnabled Set to $true to enable MFA for all users, $false to disable. .PARAMETER ExceptionUsers An array of user UPNs to exclude from the MFA enforcement. .NOTES Directly enabling/disabling MFA for all users via PowerShell is complex and often controlled by Conditional Access policies or security defaults. This function is a placeholder and does not implement direct remediation for "AllUsersMFAEnabled" to avoid complexity and deprecated methods. #> function Set-O365UserMFAStatus { [CmdletBinding()] param( [bool]$AllUsersMFAEnabled, [string[]]$ExceptionUsers ) Write-Verbose "Setting AllUsersMFAEnabled to $AllUsersMFAEnabled (Placeholder)" Write-Verbose "ExceptionUsers: $ExceptionUsers (Placeholder)" # Placeholder for actual cmdlet calls } <# .SYNOPSIS Gets the current mailbox forwarding settings for all mailboxes. .DESCRIPTION This internal helper function retrieves the current mailbox forwarding settings for all mailboxes. .NOTES This function is not intended to be called directly. #> function Get-O365MailboxForwardingSettings { [CmdletBinding()] param() # Placeholder return [PSCustomObject]@{ ExternalForwardingAllowed = $false # Placeholder InternalOnlyForwarding = $true # Placeholder } } <# .SYNOPSIS Sets the mailbox forwarding settings. .DESCRIPTION This internal helper function sets the mailbox forwarding settings. .PARAMETER ExternalForwardingAllowed Set to $true to allow external forwarding, $false to deny. .PARAMETER InternalOnlyForwarding Set to $true to allow internal-only forwarding, $false to deny. .NOTES This function is not intended to be called directly. #> function Set-O365MailboxForwardingSettings { [CmdletBinding()] param( [bool]$ExternalForwardingAllowed, [bool]$InternalOnlyForwarding ) Write-Verbose "Setting ExternalForwardingAllowed to $ExternalForwardingAllowed (Placeholder)" Write-Verbose "Setting InternalOnlyForwarding to $InternalOnlyForwarding (Placeholder)" # Placeholder for actual cmdlet calls } <# .SYNOPSIS Gets the current Teams governance settings. .DESCRIPTION This internal helper function retrieves the current Teams governance settings, such as guest access to create/update channels. .NOTES This function is not intended to be called directly. #> function Get-O365TeamsGovernanceSettings { [CmdletBinding()] param() # Placeholder for actual cmdlet calls # For now, we will return a dummy object. return [PSCustomObject]@{ AllowGuestCreateUpdateChannels = $false # Placeholder } } <# .SYNOPSIS Sets the Teams governance settings. .DESCRIPTION This internal helper function sets the Teams governance settings, such as guest access to create/update channels. .PARAMETER AllowGuestCreateUpdateChannels Set to $true to allow guest users to create/update channels, $false to deny. .NOTES This function is not intended to be called directly. #> function Set-O365TeamsGovernanceSettings { [CmdletBinding()] param( [bool]$AllowGuestCreateUpdateChannels ) Write-Verbose "Setting AllowGuestCreateUpdateChannels to $AllowGuestCreateUpdateChannels (Placeholder)" # Placeholder for actual cmdlet calls } |