EasyLife365.Collaboration.psm1

#region _classes
class EL {
    [string]$Id
    [string]$DisplayName
    [object]$Metadata
    [System.Nullable[guid]]$TemplateId
    [System.Nullable[guid]]$PolicyId
    [object]$Policy
    
    EL ([object] $el) {
        $this.Id = $el.Id
        $this.DisplayName = $el.DisplayName
        $this.Metadata = $el.Extensions | Get-EasyMetaData
        $this.Policy = $el.Extensions | Get-EasyPolicyData
        if($this.Metadata.tId){
            $this.TemplateId = $this.Metadata.tId
        }
        if($this.Policy.pId){
            $this.PolicyId = $this.Policy.pId
        }
    }
    
    [void] NewTemplateId([guid]$tId,$type) {
        $this.Metadata = [PSCustomObject]@{ 
            tId = $tId 
        } 
        $metadataHash = ($this.Metadata | ConvertTo-EasyHashtable)

        if($type -eq 'ELGuestUser'){
            Write-Verbose "New-MgUserExtension userId: $($this.id) metadata: $($metadataHash | ConvertTo-Json -Compress)"
            New-MgUserExtension -UserId $this.Id -Id $script:elMetadataExtension -AdditionalProperties $metadataHash -ErrorAction Stop
        } elseif($type -eq 'ELGroup'){
            Write-Verbose "New-MgUserExtension groupId: $($this.id) metadata: $($metadataHash | ConvertTo-Json -Compress)"
            New-MgGroupExtension -GroupId $this.Id -Id $script:elMetadataExtension -AdditionalProperties $metadataHash -ErrorAction Stop
        }
    }

    [void] SetTemplateId([guid]$tId,$type) {
        $this.Metadata.tId = $tId
        $metadataHash = ($this.Metadata | ConvertTo-EasyHashtable)

        if($type -eq 'ELGuestUser'){
            Write-Verbose "Update-MgUserExtension userId: $($this.id) metadata: $($metadataHash | ConvertTo-Json -Compress)"
            Update-MgUserExtension -UserId $this.Id -ExtensionId $script:elMetadataExtension -AdditionalProperties $metadataHash -ErrorAction Stop
        } elseif($type -eq 'ELGroup'){
            Write-Verbose "Update-MgUserExtension groupId: $($this.id) metadata: $($metadataHash | ConvertTo-Json -Compress)"
            Update-MgGroupExtension -GroupId $this.Id -ExtensionId $script:elMetadataExtension -AdditionalProperties $metadataHash -ErrorAction Stop
        }
    }

    [void] NewPolicyId([guid]$polId,$type) {
        $this.PolicyId = $polId

        if($type -eq 'ELGuestUser'){
            Write-Verbose "New-MgUserExtension userId: $($this.id) pId: $($this.PolicyId)"
            New-MgUserExtension -UserId $this.Id -Id $script:elPolicyExtension -AdditionalProperties @{
                pId = $this.PolicyId
            } -ErrorAction Stop
        } elseif($type -eq 'ELGroup'){
            Write-Verbose "New-MgGroupExtension groupId: $($this.id) pId: $($this.PolicyId)"
            New-MgGroupExtension -GroupId $this.Id -Id $script:elPolicyExtension -AdditionalProperties @{
                pId = $this.PolicyId
            } -ErrorAction Stop
        }
    }

    [void] SetPolicyId([guid]$polId,$type) {
        $this.PolicyId = $polId

        if($type -eq 'ELGuestUser'){
            Write-Verbose "Update-MgUserExtension userId: $($this.id) pId: $($this.PolicyId)"
            Update-MgUserExtension -UserId $this.Id -ExtensionId $script:elPolicyExtension -AdditionalProperties @{
                pId = $this.PolicyId
            } -ErrorAction Stop
        } elseif($type -eq 'ELGroup'){
            Write-Verbose "Update-MgGroupExtension groupId: $($this.id) pId: $($this.PolicyId)"
            Update-MgGroupExtension -GroupId $this.Id -ExtensionId $script:elPolicyExtension -AdditionalProperties @{
                pId = $this.PolicyId
            } -ErrorAction Stop
        }
    }

    [void] ClearPolicyId($type) {
        $this.PolicyId = $null

        if($type -eq 'ELGuestUser'){
            $this.AccountDisabledState = $null
            $this.AccountDisabledDate = $null
            $this.AccountDisabledStepChange = $null
            $this.AccountDisabledStep = $null
            $this.NoOwnersEscalation = $null
            $this.MinimumOwnerStepChange = $null
            $this.MinimumOwnerStep = $null
            $this.MinimumOwnerState = $null
            $this.ConfirmationStepChange = $null
            $this.ConfirmationStep = $null
            $this.ConfirmationState = $null
            $this.ConfirmationDate = $null
            $this.InactivityStepChange = $null
            $this.InactivityStep = $null
            $this.InactivityState = $null
            $this.InactivityDate = $null
            $this.InvitationStepChange = $null
            $this.InvitationStep = $null
            $this.InvitationState = $null
            $this.InvitationDate = $null
            $this.NamingState = $null
            $this.ClassificationState = $null
            $this.ClassificationDate = $null
            $this.ClassificationStepChange = $null
            $this.ClassificationStep = $null

            Write-Verbose "Remove-MgUserExtension userId: $($this.id) extensionId: cloud.easyLife.policy "
            Remove-MgUserExtension -UserId $this.Id -ExtensionId $script:elPolicyExtension -ErrorAction Stop
        } elseif($type -eq 'ELGroup'){
            $this.AccessReviewPolicyState = $null
            $this.ConfirmationPolicyState = $null
            $this.ExpirationPolicyState = $null
            $this.OwnerPolicyState = $null
            $this.NoOwnerEscalation = $null
            $this.IsArchived = $null

            Write-Verbose "Remove-MgGroupExtension groupId: $($this.id) extensionId: cloud.easyLife.policy "
            Remove-MgGroupExtension -GroupId $this.Id -ExtensionId $script:elPolicyExtension -ErrorAction Stop
        }
    }

    [void] SetMetadata([hashtable]$metadata,$type) {
        $metadataHash = ($this.Metadata | ConvertTo-EasyHashtable)
        foreach($key in $metadata.Keys){
            if($metadataHash.contains($key)){
                $metadataHash[$key] = $metadata[$key]
            } else {
                $metadataHash.Add($key,$metadata[$key])
            }
        }
        $this.Metadata = $metadataHash
        if($type -eq 'ELGuestUser'){
            Write-Verbose "Update-MgUserExtension userId: $($this.id) metadata: $($metadataHash | ConvertTo-Json -Compress)"
            Update-MgUserExtension -UserId $this.Id -ExtensionId $script:elMetadataExtension -AdditionalProperties $metadataHash -ErrorAction Stop
        } elseif($type -eq 'ELGroup'){
            Write-Verbose "Update-MgUserExtension groupId: $($this.id) metadata: $($metadataHash | ConvertTo-Json -Compress)"
            Update-MgGroupExtension -GroupId $this.Id -ExtensionId $script:elMetadataExtension -AdditionalProperties $metadataHash -ErrorAction Stop
        }
    }

    [string] ToString() {
        return $this.DisplayName
    }
}

class ELGuestUser : EL {
    [string]$PrimaryOwner
    [string]$SecondaryOwner
    [string]$Mail
    [string]$CompanyName
    [string]$GivenName
    [string]$Surname
    [System.Nullable[datetime]]$createdDateTime
    [bool]$IsCompliant
    [string]$ResourceType
    [string]$AccountDisabledState
    [System.Nullable[datetime]]$AccountDisabledDate
    [System.Nullable[datetime]]$AccountDisabledStepChange
    [string]$AccountDisabledStep
    [boolean]$NoOwnersEscalation
    [System.Nullable[datetime]]$MinimumOwnerStepChange
    [string]$MinimumOwnerStep
    [string]$MinimumOwnerState
    [System.Nullable[datetime]]$ConfirmationStepChange
    [string]$ConfirmationStep
    [string]$ConfirmationState
    [System.Nullable[datetime]]$ConfirmationDate
    [System.Nullable[datetime]]$InactivityStepChange
    [string]$InactivityStep
    [string]$InactivityState
    [System.Nullable[datetime]]$InactivityDate
    [System.Nullable[datetime]]$InvitationStepChange
    [string]$InvitationStep
    [string]$InvitationState
    [System.Nullable[datetime]]$InvitationDate
    [string]$NamingState
    [string]$ClassificationState
    [System.Nullable[datetime]]$ClassificationDate
    [System.Nullable[datetime]]$ClassificationStepChange
    [string]$ClassificationStep
    [string]$ExtensionAttribute1
    [string]$ExtensionAttribute2
    [string]$ExtensionAttribute3
    [string]$ExtensionAttribute4
    [string]$ExtensionAttribute5
    [string]$ExtensionAttribute6
    [string]$ExtensionAttribute7
    [string]$ExtensionAttribute8
    [string]$ExtensionAttribute9
    [string]$ExtensionAttribute10

    ELGuestUser ([object] $elGuestUser, [bool]$fastMode ) : base($elGuestUser) {
        $this.Mail = $elGuestUser.Mail
        $this.CompanyName = $elGuestUser.CompanyName
        $this.GivenName = $elGuestUser.GivenName
        $this.Surname = $elGuestUser.Surname
        $this.createdDateTime = $elGuestUser.createdDateTime
        $pOwner = $elGuestUser.AdditionalProperties.$script:elUserExtension.primaryOwner
        $sOwner = $elGuestUser.AdditionalProperties.$script:elUserExtension.secondaryOwner
        if($pOwner){
            if($fastMode) {
                $this.PrimaryOwner = $pOwner
            } else {
                $this.PrimaryOwner = (Get-MgUser -UserId $pOwner -Select UserPrincipalName).UserPrincipalName
            }
        }
        if($sOwner){
            if($fastMode) {
                $this.SecondaryOwner = $sOwner
            } else {
                $this.SecondaryOwner = (Get-MgUser -UserId $sOwner -Select UserPrincipalName).UserPrincipalName
            }
        }
        $this.IsCompliant = $elGuestUser.AdditionalProperties.$script:elUserExtension.isCompliant
        $this.ResourceType = $elGuestUser.AdditionalProperties.$script:elUserExtension.resourceType
        $this.templateId = $elGuestUser.AdditionalProperties.$script:elUserExtension.templateId
        $this.policyId = $elGuestUser.AdditionalProperties.$script:elUserExtension.policyId
        $this.extensionAttribute1 = $elGuestUser.AdditionalProperties.$script:elUserExtension.extensionAttribute1
        $this.extensionAttribute2 = $elGuestUser.AdditionalProperties.$script:elUserExtension.extensionAttribute2
        $this.extensionAttribute3 = $elGuestUser.AdditionalProperties.$script:elUserExtension.extensionAttribute3
        $this.extensionAttribute4 = $elGuestUser.AdditionalProperties.$script:elUserExtension.extensionAttribute4
        $this.extensionAttribute5 = $elGuestUser.AdditionalProperties.$script:elUserExtension.extensionAttribute5
        $this.extensionAttribute6 = $elGuestUser.AdditionalProperties.$script:elUserExtension.extensionAttribute6
        $this.extensionAttribute7 = $elGuestUser.AdditionalProperties.$script:elUserExtension.extensionAttribute7
        $this.extensionAttribute8 = $elGuestUser.AdditionalProperties.$script:elUserExtension.extensionAttribute8
        $this.extensionAttribute9 = $elGuestUser.AdditionalProperties.$script:elUserExtension.extensionAttribute9
        $this.extensionAttribute10 = $elGuestUser.AdditionalProperties.$script:elUserExtension.extensionAttribute10
        $this.AccountDisabledState = $this.Policy.adSta
        $this.AccountDisabledDate = $this.Policy.adCh
        $this.AccountDisabledStep = $this.Policy.adSt
        $this.AccountDisabledStepChange = $this.Policy.adStCh
        $this.NoOwnersEscalation = $this.Policy.noEsca
        $this.MinimumOwnerState = $this.Policy.owSta
        $this.MinimumOwnerStep = $this.Policy.owSt
        $this.MinimumOwnerStepChange = $this.Policy.owStCh
        $this.ConfirmationState = $this.Policy.coSta
        $this.ConfirmationDate = $this.Policy.coCh
        $this.ConfirmationStep = $this.Policy.coSt
        $this.ConfirmationStepChange = $this.Policy.coStCh
        $this.InactivityState = $this.Policy.inSta
        $this.InactivityDate = $this.Policy.inCh
        $this.InactivityStep = $this.Policy.inSt
        $this.InactivityStepChange = $this.Policy.inStCh
        $this.InvitationState = $this.Policy.invSta
        $this.InvitationDate = $this.Policy.invCh
        $this.InvitationStep = $this.Policy.invSt
        $this.InvitationStepChange = $this.Policy.invStCh
        $this.NamingState = $this.Policy.naSta
        $this.ClassificationState = $this.Policy.clSta
        $this.ClassificationDate = $this.Policy.clCh
        $this.ClassificationStep = $this.Policy.clSt
        $this.ClassificationStepChange = $this.Policy.clStCh
    }
}

class ELGroup : EL {
    [string]$Description
    [string]$Mail
    [System.Nullable[datetime]]$CreatedDateTime
    [string]$MailNickname
    [string]$Visibility
    [string[]]$GroupTypes
    [string[]]$Owner
    [boolean]$NoOwnersEscalation
    [System.Nullable[datetime]]$ConfirmationStepChange
    [string]$ConfirmationStep
    [string]$ConfirmationState
    [System.Nullable[datetime]]$ConfirmationDate
    [System.Nullable[datetime]]$ConfirmationFeedbackDate
    [System.Nullable[datetime]]$ExpirationStepChange
    [string]$ExpirationStep
    [string]$ExpirationState
    [System.Nullable[datetime]]$ExpirationDate
    [System.Nullable[datetime]]$ExpirationFeedbackDate
    [System.Nullable[datetime]]$MinimumOwnerStepChange
    [string]$MinimumOwnerStep
    [string]$MinimumOwnerState
    [System.Nullable[datetime]]$AccessReviewStepChange
    [string]$AccessReviewStep
    [string]$AccessReviewState
    [System.Nullable[datetime]]$AccessReviewDate
    [System.Nullable[datetime]]$AccessReviewFeedbackDate


    ELGroup ([object] $ELGroup, [bool]$fastMode) : base($ELGroup) {
        $this.Description = $ELGroup.Description
        $this.Mail = $ELGroup.Mail
        $this.Visibility = $ELGroup.Visibility
        $this.MailNickname = $ELGroup.MailNickname
        $this.CreatedDateTime = $ELGroup.CreatedDateTime
        $this.GroupTypes = $ELGroup.GroupTypes

        if(-not($fastMode)){
            $this.Owner = $ELGroup.Owners.AdditionalProperties.userPrincipalName
        }

        $this.NoOwnersEscalation = $this.Policy.noEsca 
        $this.ConfirmationStepChange = $this.Policy.coStCh 
        $this.ConfirmationStep = $this.Policy.coSt 
        $this.ConfirmationState = $this.Policy.coSta 
        $this.ConfirmationDate = $this.Policy.coCh 
        $this.ConfirmationFeedbackDate = $this.Policy.coFeDa 
        $this.ExpirationStepChange = $this.Policy.exStCh 
        $this.ExpirationStep = $this.Policy.exSt 
        $this.ExpirationState = $this.Policy.exSta 
        $this.ExpirationDate = $this.Policy.exCh 
        $this.ExpirationFeedbackDate = $this.Policy.exFeDa 
        $this.MinimumOwnerStepChange = $this.Policy.owStCh 
        $this.MinimumOwnerStep = $this.Policy.owSt 
        $this.MinimumOwnerState = $this.Policy.owSta 
        $this.AccessReviewStepChange = $this.Policy.arStCh 
        $this.AccessReviewStep = $this.Policy.arSt 
        $this.AccessReviewState = $this.Policy.arSta 
        $this.AccessReviewDate = $this.Policy.arCh 
        $this.AccessReviewFeedbackDate = $this.Policy.arFeDa 
    }
}

class ELTeam : ELGroup {
    [bool]$IsArchived
    [string]$WebUrl

    ELTeam ([object] $ELGroup, [object] $ELTeam, [bool]$fastMode) : base($ELGroup,$fastMode) {
        $this.Description = $ELGroup.Description
        $this.Mail = $ELGroup.Mail
        $this.Visibility = $ELGroup.Visibility
        $this.MailNickname = $ELGroup.MailNickname
        $this.CreatedDateTime = $ELGroup.CreatedDateTime
        $this.GroupTypes = $ELGroup.GroupTypes
        $this.IsArchived = $ELTeam.isArchived
        $this.WebUrl = $ELTeam.WebUrl
        $this.NoOwnersEscalation = $this.Policy.noEsca 
        $this.ConfirmationStepChange = $this.Policy.coStCh 
        $this.ConfirmationStep = $this.Policy.coSt 
        $this.ConfirmationState = $this.Policy.coSta 
        $this.ConfirmationDate = $this.Policy.coCh 
        $this.ConfirmationFeedbackDate = $this.Policy.coFeDa 
        $this.ExpirationStepChange = $this.Policy.exStCh 
        $this.ExpirationStep = $this.Policy.exSt 
        $this.ExpirationState = $this.Policy.exSta 
        $this.ExpirationDate = $this.Policy.exCh 
        $this.ExpirationFeedbackDate = $this.Policy.exFeDa 
        $this.MinimumOwnerStepChange = $this.Policy.owStCh 
        $this.MinimumOwnerStep = $this.Policy.owSt 
        $this.MinimumOwnerState = $this.Policy.owSta 
        $this.AccessReviewStepChange = $this.Policy.arStCh 
        $this.AccessReviewStep = $this.Policy.arSt 
        $this.AccessReviewState = $this.Policy.arSta 
        $this.AccessReviewDate = $this.Policy.arCh 
        $this.AccessReviewFeedbackDate = $this.Policy.arFeDa 
    }
}


class ELTelemetry {
    [string]$name = 'Microsoft.ApplicationInsights.Event'
    [System.Nullable[datetime]]$time = [datetime]::UtcNow
    [guid]$iKey = $elAiInstaceKey
    [hashtable]$tags = @{'ai.cloud.roleInstance' = (Get-MgContext).TenantId}
    [hashtable]$data

    ELTelemetry() {
    }

    ELTelemetry([hashtable]$d) {
        $this.data = $d
    }
    
    ELTelemetry([guid]$k,[hashtable]$t,[hashtable]$d) {
        $this.iKey = $k
        $this.tags = $t
        $this.data = $d
    }

    [string] ToJson(){
        return ($this | ConvertTo-Json -Compress -Depth 100)
    }
}
#endregion _classes

#region _constants

New-Variable -Name elPolicyExtension -Option Constant -Value 'cloud.easyLife.policy'
New-Variable -Name elMetadataExtension -Option Constant -Value 'cloud.easyLife.metadata'
New-Variable -Name elPowerShellClientId -Option Constant -Value '14b899c6-0b3a-4f59-8807-e9df5fb0fb1e'
New-Variable -Name elAiInstaceKey -Option Constant -Value '71103f09-7433-40bb-b664-e851ce5bf6b1'
New-Variable -Name elRequiredScopes -Option Constant -Value @{
    'Get' = @('User.Read.All', 'Group.Read.All','Team.ReadBasic.All')
    'Set' = @('User.ReadWrite.All', 'Group.ReadWrite.All','Team.ReadBasic.All')
}
New-Variable -Name elUserExtension -Option Constant -Value 'easylife365_user'
#endregion _constants

#region ConvertTo-EasyHashtable
function ConvertTo-EasyHashtable { 
    param ( 
        [Parameter(Mandatory,ValueFromPipeline)] 
        [object]$inputObject 
    )
    $output = @{}; 
    $inputObject | Get-Member -MemberType *Property | ForEach-Object {
        $output.($_.name) = $inputObject.($_.name); 
    }
    return  $output;
}
#endregion ConvertTo-EasyHashtable

#region Get-EasyAppRoleId
function Get-EasyAppRoleId {
    [CmdletBinding()]
    param (
        [object]$graphObject,
        [string[]]$AppRoles
    )
    process {
        $AppRoles | ForEach-Object {
            $scope = $_
            $graphObject.AppRoles.where{$_.Value -eq $scope} | Select-Object Id,Value
        }
    }
}
#endregion Get-EasyAppRoleId

#region Get-EasyLatestModuleVersion
function Get-EasyLatestModuleVersion {
    [CmdletBinding()]
    param (
        [Parameter()]
        [string]$name = 'easylife365.collaboration'
    )
    process {
        (Invoke-RestMethod "https://www.powershellgallery.com/api/v2/FindPackagesById()?id='$name'")[-1].properties.version
    }
}
#endregion Get-EasyLatestModuleVersion

#region Get-EasyMetaData
function Get-EasyMetaData {
    [CmdletBinding()]
    param (
        [Parameter(ValueFromPipeline)]
        [object]
        $inputObject
    )
    process {
        $objParam = @{
            TypeName = 'pscustomobject'
            Property = $inputObject.where{$_.id -eq $script:elMetadataExtension}.AdditionalProperties
        }
        if($objParam.Property){
            New-Object @objParam | Select-Object -ExcludeProperty '@odata.type','extensionName'
        }
    }
}
#endregion Get-EasyMetaData

#region Get-EasyPolicyData
function Get-EasyPolicyData {
    [CmdletBinding()]
    param (
        [Parameter(ValueFromPipeline)]
        [object]
        $inputObject
    )
    process {
        $objParam = @{
            TypeName = 'pscustomobject'
            Property = $inputObject.where{$_.id -eq $script:elPolicyExtension}.AdditionalProperties
        }
        if($objParam.Property){
            New-Object @objParam | Select-Object -ExcludeProperty '*@odata.type','extensionName'
        }
    }
}
#endregion Get-EasyPolicyData

#region Get-EasyTeamsActivity

function Get-EasyTeamsActivity {
    [CmdletBinding()]
    param (
        [Parameter()]
        [ValidateSet('D7', 'D30', 'D90', 'D180')]
        $Period = 'D30',
        [Parameter()]
        [ValidateSet('ActivityDetail','ActivityCounts','ActivityDistributionCounts')]
        $Report = 'ActivityDetail'
    )
    process {
        $uri = "https://graph.microsoft.com/beta/reports/getTeamsTeam{0}(period='{1}')?`$format=application/json" -f $Report, $Period
        $res = Invoke-MgGraphRequest -Uri $uri
        $res.value
    }
}
#endregion Get-EasyTeamsActivity

#region Get-EasyUserId
function Get-EasyUserId {
    [CmdletBinding()]
    param (
        $userId
    )
    process {
        try {
            (Get-MgUser -UserId $userId -ErrorAction Stop).Id
        }
        catch {
            Write-Warning "Could not find user with id: $userId"
        }
    }
}
#endregion Get-EasyUserId

#region New-EasyTelemetryData
function New-EasyTelemetryData {
    param(
        [Parameter()]
        [ValidateSet('Event','Exception')]
        $type
    )
    switch($type){
        'Event' {
            @{
                baseType = "EventData"
                baseData = @{
                    ver = 2
                    name = $name
                    properties = @{
                        moduleVersion = $moduleVersion
                        commandLine = $commandLine
                        duration = $duration
                    }
                }
            }
        }
        'Exception' {
            @{
                baseType = "ExceptionData"
                baseData = @{
                    ver = 2
                    handledAt = "UserCode"
                    properties = @{
                        moduleVersion = $moduleVersion
                        commandLine = $commandLine
                        duration = $duration
                    }
                    exceptions = @()
                }
            }
        }
    }
}
#endregion New-EasyTelemetryData

#region Select-EasyGroupProperty
function Select-EasyGroupProperty {
    [CmdletBinding()]
    param (
        [Parameter(ValueFromPipeline)]
        [object]$InputObject
    )
    process {
        $policyProperty = $InputObject.Extensions.where{$_.id -eq "cloud.easyLife.policy"}.AdditionalProperties
        $Policy = New-Object -TypeName pscustomobject -Property $policyProperty

        $selectProperty = 'id','displayName','description','mail','groupTypes',@{
            Name = 'policyId'
            Expression = {$Policy.pId}
        },@{
            Name = 'ownerPolicyState'
            Expression= {$Policy.owSta}
        },@{
            Name = 'accessReviewPolicyState'
            Expression= {$Policy.arSta}
        },@{
            Name = 'expirationPolicyState'
            Expression= {$Policy.exSta}
        },@{
            Name = 'confirmationPolicyState'
            Expression= {$Policy.coSta}
        },@{
            Name = 'isArchived'
            Expression= {$Policy.isArchived}
        },@{
            Name = 'noOwnerEscalation'
            Expression= {$Policy.noEsca}
        },@{
            Name = 'Metadata'
            Expression = { @($InputObject.Extensions | Get-EasyMetaData)[0] }
        }
        $InputObject | Select-Object -Property $selectProperty
    }
}
#endregion Select-EasyGroupProperty

#region Send-EasyTelemetryEvent
function Send-EasyTelemetryEvent {
    [CmdletBinding()]
    param(
        [string]
        $IngestionEndpoint = 'https://westeurope-5.in.applicationinsights.azure.com',
        [ELTelemetry]
        $Body,
        [switch]
        $PassThru
    )
    process {
        # The rest request fails with PS5.1
        if($EasyLife365DisableTelemetry -eq $true -or $PSEdition -eq 'Desktop'){
            Write-Verbose "Sending telemetry is disabled on this system."
        } else {
            $Body.time = [datetime]::UtcNow
            $null = Invoke-RestMethod -Uri "$IngestionEndpoint/v2/track" -Method 'POST' -UseBasicParsing -Body ($Body.ToJson())
            if($PassThru){
                $Body
            }
        }
    }
}
#endregion Send-EasyTelemetryEvent

#region Set-EasyGroup
function Set-EasyGroup {
    <#
    .SYNOPSIS
        Set EasyLife properties for group objects through the Graph API.
    .DESCRIPTION
        This function uses Set-MgGroup and Set-MgGroupExtension to set EasyLife properties, such as PolicyId and TemplateId, of groups-based objects through the Graph API.
    .NOTES
        The alias Set-EasyTeam can be used.
    .LINK
        https://docs.easylife365.cloud
    .EXAMPLE
        Get-EasyGroup -Id 0b158de6-5fe8-49d9-81bb-22c23860d3a4 | Set-EasyGroup -PolicyId 2570b1bb-50bc-41a9-9d38-85888e8a04a1
        
        This example sets the EasyLife policy to 2570b1bb-50bc-41a9-9d38-85888e8a04a1 for the Group with the Id 0b158de6-5fe8-49d9-81bb-22c23860d3a4.
        To get the id of a policy, open the policy in the EasyLife cockpit and copy the guid from the URL.
    .EXAMPLE
        Set-EasyGroup -Id 0b158de6-5fe8-49d9-81bb-22c23860d3a4 -TemplateId 51436a74-87aa-43d8-8139-928a26a33e80
        
        This example sets the EasyLife policy to 51436a74-87aa-43d8-8139-928a26a33e80 for the Group with the Id 0b158de6-5fe8-49d9-81bb-22c23860d3a4
        To get the id of a template, open the template in the EasyLife cockpit and copy the guid from the URL.
    #>

    [CmdletBinding(DefaultParameterSetName='byGroupId',SupportsShouldProcess)]
    param (
        [Parameter(Mandatory,ParameterSetName='byGroupId')]
        [ValidateNotNullOrEmpty()]
        [string]$GroupId,
        [Parameter(ParameterSetName='inputObject',ValueFromPipeline)]
        [ValidateNotNullOrEmpty()]
        [object]$InputObject,
        [string]$DisplayName,
        [guid]$PolicyId,
        [guid]$TemplateId,
        [hashtable]$Metadata,
        [switch]$PassThru
    )
    process {
        if(-not($InputObject)){
            $InputObject = Get-EasyGroup -Id $GroupId
        }
        $bodyParameter = @{}
        switch($PSBoundParameters.Keys){
            'DisplayName' {
                $bodyParameter.DisplayName = $DisplayName
            }
            'PolicyId' {
                $InputObject | Set-EasyPolicyId -PolicyId $PolicyId
            }
            'TemplateId' {
                $InputObject | Set-EasyTemplateId -TemplateId $TemplateId
            }
            'Metadata' {
                $InputObject | Set-EasyMetadata -Metadata $Metadata
            }
        }
        if($bodyParameter.Keys -ne '') {
            if($PSCmdlet.ShouldProcess("Updating Group $($InputObject.DisplayName) with $($bodyParameter| ConvertTo-Json -Compress)",$null,$null)) {
                Update-MgGroup -GroupId $InputObject.Id -BodyParameter $bodyParameter
            }
        }
        if($PassThru){
            $InputObject
        }
    }
}
# New-Alias -Name Set-EasyTeam -Value Set-EasyGroup
#endregion Set-EasyGroup

#region Set-EasyGuestUser
function Set-EasyGuestUser {
    <#
    .SYNOPSIS
        Set EasyLife properties of (guest) user accounts through the Graph API.
    .DESCRIPTION
        This function uses Set-MgUser and Set-MgUserExtension to set EasyLife properties such as owner, metadata, and policy. This function does not differentiate between users and guest users, use with caution.
    .NOTES
        None.
    .LINK
        https://docs.easylife365.cloud
    .EXAMPLE
        Get-EasyGuestUser -Id 0cefd2d0-ae5a-4ca3-98e9-e8df5418226c | Set-EasyGuestUser -PolicyId 87954648-18f5-4091-93fb-db0d38eca208
        
        This example sets the EasyLife policy to 87954648-18f5-4091-93fb-db0d38eca208 for the user with the Id 0cefd2d0-ae5a-4ca3-98e9-e8df5418226c.
        To get the id of a policy, open the policy in the EasyLife cockpit and copy the guid from the URL.
    .EXAMPLE
       Set-EasyGuestUser -Id 0cefd2d0-ae5a-4ca3-98e9-e8df5418226c -TemplateId 33373f13-8669-40de-b6da-b4a2b0a55e83
        
        This example sets the EasyLife template to 33373f13-8669-40de-b6da-b4a2b0a55e83 for the user with the Id 0cefd2d0-ae5a-4ca3-98e9-e8df5418226c.
        To get the id of a tempate, open the template in the EasyLife cockpit and copy the guid from the URL.
    #>

    [CmdletBinding(DefaultParameterSetName='byUserId',SupportsShouldProcess)]
    param (
        [Parameter(Mandatory,ParameterSetName='byUserId')]
        [ValidateNotNullOrEmpty()]
        [string]$UserId,
        [Parameter(ParameterSetName='inputObject',ValueFromPipeline)]
        [ValidateNotNullOrEmpty()]
        [object]$InputObject,
        [string]$DisplayName,
        [guid]$PolicyId,
        [guid]$TemplateId,
        [hashtable]$Metadata,
        [switch]$PassThru
    )
    process {
        if(-not($InputObject)){
            $InputObject = Get-EasyGuestUser -Id $UserId
        }
        $bodyParameter = @{}
        switch($PSBoundParameters.Keys){
            'DisplayName' {
                $bodyParameter.DisplayName = $DisplayName
            }
            'PolicyId' {
                $InputObject | Set-EasyPolicyId -PolicyId $PolicyId
            }
            'TemplateId' {
                $InputObject | Set-EasyTemplateId -TemplateId $TemplateId
            }
            'Metadata' {
                $InputObject | Set-EasyMetadata -Metadata $Metadata
            }
        }
        if($bodyParameter.Keys -ne '') {
            if($PSCmdlet.ShouldProcess("Updating User $($InputObject.DisplayName) with $($bodyParameter| ConvertTo-Json -Compress)",$null,$null)) {
                Update-MgUser -UserId $InputObject.Id -BodyParameter $bodyParameter
            }
        }
        if($PassThru){
            $InputObject
        }
    }
}
#endregion Set-EasyGuestUser

#region Set-EasyMetadata
function Set-EasyMetadata {
    [CmdletBinding(DefaultParameterSetName='user',SupportsShouldProcess)]
    param (
        [Parameter(Mandatory,ParameterSetName='user')]
        [ValidateNotNullOrEmpty()]
        $UserId,
        [Parameter(Mandatory,ParameterSetName='group')]
        [ValidateNotNullOrEmpty()]
        $GroupId,
        [Parameter(Mandatory,ValueFromPipeline,ParameterSetName='inputObject')]
        [ValidateNotNullOrEmpty()]
        $InputObject,
        [Parameter(Mandatory)]
        [hashtable]
        $Metadata
    )
    begin {
        $sw,$diag = Start-EasyDiagnostics -Name $MyInvocation.InvocationName
    }
    process {
        switch($PSBoundParameters.Keys){
            'UserId'{
                $InputObject = Get-EasyGuestUser -Id $UserId
            }
            'GroupId'{
                $InputObject = Get-EasyGroup -Id $GroupId
            }
        }
        $type = $InputObject.GetType().Name
        Write-Verbose "InputObject is of type $type"
        if($InputObject.Metadata -eq $null){
            if($PSCmdlet.ShouldProcess("Add metadata extension to object $($InputObject.DisplayName)",$null,$null)){
                try {
                    $InputObject.NewTemplateId([guid]::Empty,$type)
                } catch {
                    Send-EasyTelemetryEvent -Type Exception -Message $_.Exception  -PassThru
                    $diag.success = $false
                }
            }
        } else {
            if($PSCmdlet.ShouldProcess("Set metadata for object $($InputObject.DisplayName)",$null,$null)){
                try{ 
                    $InputObject.SetMetadata($metadata,$type)
                } catch {
                    Send-EasyTelemetryEvent -Type Exception -Message $_.Exception -PassThru
                    $diag.success = $false
                }
            }
        }
    }
    end {
        $sw.Stop()
        $diag.Duration = $sw.ElapsedMilliseconds
        Send-EasyTelemetryEvent -Type Request -Request $diag -CommandLine $MyInvocation.Line
    }
}
#endregion Set-EasyMetadata

#region Set-EasyPolicyId
function Set-EasyPolicyId {
    [CmdletBinding(DefaultParameterSetName='user',SupportsShouldProcess)]
    param (
        [Parameter(Mandatory,ParameterSetName='user')]
        [ValidateNotNullOrEmpty()]
        $UserId,
        [Parameter(Mandatory,ParameterSetName='group')]
        [ValidateNotNullOrEmpty()]
        $GroupId,
        [Parameter(Mandatory,ValueFromPipeline,ParameterSetName='inputObject')]
        [ValidateNotNullOrEmpty()]
        $InputObject,
        [Parameter(Mandatory)]
        [guid]
        $PolicyId
    )
    begin {
        $sw,$diag = Start-EasyDiagnostics -Name $MyInvocation.InvocationName
    }
    process {
        switch($PSBoundParameters.Keys){
            'UserId'{
                $InputObject = Get-EasyGuestUser -Id $UserId
            }
            'GroupId'{
                $InputObject = Get-EasyGroup -Id $GroupId
            }
        }
        $type = $InputObject.GetType().Name
        Write-Verbose "InputObject is of type $type"

        if($InputObject.PolicyId -eq [guid]::Empty){
            # input object does not have a policy, create policy attribute and set policy
            if($PSCmdlet.ShouldProcess("Add policyId extension with $PolicyId to object $($InputObject.DisplayName)",$null,$null)){
                try {
                    $InputObject.newPolicyId($PolicyId,$type)
                } catch {
                    Send-EasyTelemetryEvent -Type Exception -Message $_.Exception -PassThru
                    $diag.success = $false
                }   
            }
        } else {
            if($PolicyId -eq [guid]::Empty){
                # input object has a policy, assume user wants to clear it, remove policy attribute
                if($PSCmdlet.ShouldProcess("Set policyId to $PolicyId for object $($InputObject.DisplayName)",$null,$null)){
                    try {
                        $InputObject.ClearPolicyId($type)
                    } catch {
                        Send-EasyTelemetryEvent -Type Exception -Message $_.Exception -PassThru
                        $diag.success = $false
                    }   
                }
            } else {
                # input object already has a policy, update policyId
                if($PSCmdlet.ShouldProcess("Set policyId to $PolicyId for object $($InputObject.DisplayName)",$null,$null)){
                    try {
                        $InputObject.setPolicyId($PolicyId,$type)
                    } catch {
                        Send-EasyTelemetryEvent -Type Exception -Message $_.Exception -PassThru
                        $diag.success = $false
                    }
                    
                }
            }
        }
    }
    end {
        $sw.Stop()
        $diag.Duration = $sw.ElapsedMilliseconds
        Send-EasyTelemetryEvent -Type Request -Request $diag -CommandLine $MyInvocation.Line
    }
}
#endregion Set-EasyPolicyId

#region Set-EasyTemplateId
function Set-EasyTemplateId {
    [CmdletBinding(DefaultParameterSetName='user',SupportsShouldProcess)]
    param (
        [Parameter(Mandatory,ParameterSetName='user')]
        [ValidateNotNullOrEmpty()]
        $UserId,
        [Parameter(Mandatory,ParameterSetName='group')]
        [ValidateNotNullOrEmpty()]
        $GroupId,
        [Parameter(Mandatory,ValueFromPipeline,ParameterSetName='inputObject')]
        [ValidateNotNullOrEmpty()]
        $InputObject,
        [Parameter(Mandatory)]
        [guid]
        $TemplateId
    )
    begin {
        $sw,$diag = Start-EasyDiagnostics -Name $MyInvocation.InvocationName
    }
    process {
        switch($PSBoundParameters.Keys){
            'UserId'{
                $InputObject = Get-EasyGuestUser -Id $UserId
            }
            'GroupId'{
                $InputObject = Get-EasyGroup -Id $GroupId
            }
        }
        $type = $InputObject.GetType().Name
        Write-Verbose "InputObject is of type $type"
        if($InputObject.Metadata -eq $null){
            if($PSCmdlet.ShouldProcess("Add metadata extension to object $($InputObject.DisplayName)",$null,$null)){
                $InputObject.newTemplateId($TemplateId,$type)
            }
        } else {
            if($PSCmdlet.ShouldProcess("Set templateId to $TemplateId for object $($InputObject.DisplayName)",$null,$null)){
                $InputObject.setTemplateId($TemplateId,$type)
            }
        }
    }
    end {
        $sw.Stop()
        $diag.data.baseData.properties.duration = $sw.ElapsedMilliseconds
        $diag.data.baseData.properties.commandLine = $MyInvocation.Line
        Send-EasyTelemetryEvent -Body $diag
    }
}
#endregion Set-EasyTemplateId

#region Start-EasyDiagnostics

function Start-EasyDiagnostics {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory)]
        [string]$Name
    )
    process {
        [System.Diagnostics.Stopwatch]::StartNew()
        $diag = [ELTelemetry]::new()
        $diag.data = New-EasyTelemetryData -type Event
        $diag.data.baseData.name = $Name
        $diag.data.baseData.properties.moduleVersion = $MyInvocation.MyCommand.Module.Version.ToString()
        $diag
    }
}
#endregion Start-EasyDiagnostics

#region Test-EasyLife365ModuleVersion

function Test-EasyLife365ModuleVersion {
    [CmdletBinding()]
    param()
    process {
        $importedModule = Get-Module EasyLife365.Collaboration
        $latestVersion = Get-EasyLatestModuleVersion
        $importedVersion = $importedModule.Version
        Write-Verbose "You have imported module version [$importedVersion] the latest version available on the PowerShell Gallery is [$latestVersion]"
        if($latestVersion -gt $importedVersion){ 
            $messageData = [System.Management.Automation.HostInformationMessage]@{
                Message = "`r`nYou are using an outdated version of the module, please update to version [$latestVersion] by typing: Update-Module EasyLife365.Collaboration`r`n"; 
                ForegroundColor = 'Yellow'
                BackgroundColor = $Host.UI.RawUI.BackgroundColor
            }
            Write-Information -MessageData $messageData -InformationAction Continue 
        }
    } 
}
#endregion Test-EasyLife365ModuleVersion

#region Connect-EasyLife365
function Connect-EasyLife365 {
    <#
    .SYNOPSIS
        Connect to EasyLife 365.
    .DESCRIPTION
        This function uses the Microsoft.Graph.Authentication module to connect to the Graph API with the scopes: User.ReadWrite.All, Group.ReadWrite.All.
    .NOTES
        Currently, this fuction only connects to the Graph API. Connection to the EasyLife API is not yet available.
        You need to have permissions to consent the use of Microsoft Graph PowerShell in your Azure AD.
    .LINK
        https://docs.easylife365.cloud/docs/add-ons/powershell/connect-easylife365/
    .INPUTS
        None

        You cannot pipe values to this cmdlet.
    .OUTPUTS
        None

        This function does not write object to the pipeline. It writes an informational message to the Information output stream.
    .PARAMETER Identity
        Use this parameter to connect with a managed identity in Azure Automation or Azure Function Apps.
    .EXAMPLE
        Connect-EasyLife365
        
        This example connects to the Graph API using the scopes User.ReadWrite.All, Group.ReadWrite.All, Team.ReadBasic.All.
    .EXAMPLE
        Connect-EasyLife365 -Scopes 'User.Read.All', 'Group.Read.All', 'Team.ReadBasic.All'
        
        This example connects to the Graph API using the scopes User.Read.All, Group.Read.All, Team.ReadBasic.All.
    .EXAMPLE
        Connect-EasyLife365 -ClientId '00df8f0a-c19b-4c56-83dc-c1aaf622fe64' -Scopes 'User.Read.All', 'Group.Read.All', 'Team.ReadBasic.All'
        
        This example connects to the Graph API using the AAD application with ID '00df8f0a-c19b-4c56-83dc-c1aaf622fe64' and the scopes User.Read.All, Group.Read.All, Team.ReadBasic.All.
    .EXAMPLE
        Connect-EasyLife365 -Identity
        
        This example connects to the Graph API using a managed identity. This can be used in Azure Automation Accounts or Azure Function Apps.
        Please note that the required permissions must be granted to the managed identity's service principal before.
        You can use Grant-EasyAppPermissions to grant the required permissions to your service principal.
    .EXAMPLE
        Connect-MgGraph -Scopes User.ReadWrite.All, Group.ReadWrite.All, Team.ReadBasic.All
        
        This example uses Connect-MgGraph to connect to the Graph API. You can use existing Graph contexts as long as they contain the required scopes.
    #>

    [CmdletBinding(DefaultParameterSetName='byClientIdAndScope', HelpUri = 'https://docs.easylife365.cloud/docs/add-ons/powershell/connect-easylife365/')]
    param (
        [Parameter(ParameterSetName='byIdentity')]
        [switch]
        $Identity,
        [Parameter(ParameterSetName='byClientIdAndScope')]
        [string]$ClientId = $elPowerShellClientId,
        [Parameter(ParameterSetName='byClientIdAndScope')]
        [string[]]$Scopes = @('User.ReadWrite.All', 'Group.ReadWrite.All', 'Team.ReadBasic.All')
    )
    begin {
        $sw,$diag = Start-EasyDiagnostics -Name $MyInvocation.InvocationName
    }
    process {
        # v2 of the graph api will have an -identity switch for connect-mggraph, until then we need to work around like this
        if($Identity){
            $null = Connect-AzAccount -Identity
            $token = (Get-AzAccessToken -ResourceTypeName MSGraph).Token
            $graphParam = @{ 
                AccessToken = $token 
            }
        } else {
            $graphParam = @{
                ClientId = $ClientId
                Scopes = $Scopes
            }
        }
        Write-Verbose "Invoking Connect-MgGraph with $($graphParam | ConvertTo-Json -Compress)"
        Connect-MgGraph @graphParam | Out-Null
        if($?){
            $ctx = Get-MgContext
            Write-Information "`r`nWelcome to EasyLife $($ctx.Account), you are connected to the tenant $($ctx.TenantId) via $($ctx.AppName) with ClientId $($ctx.ClientId).`r`n" -InformationAction Continue
        }
        Test-EasyLife365ModuleVersion
        $sw.Stop()
        $diag.data.baseData.properties.duration = $sw.ElapsedMilliseconds
        $diag.data.baseData.properties.commandLine = $MyInvocation.Line
        Send-EasyTelemetryEvent -Body $diag
    }
}
#endregion Connect-EasyLife365

#region Get-EasyGroup
function Get-EasyGroup {
    <#
    .SYNOPSIS
        Get EasyLife Unified Groups from the Graph API.
    .DESCRIPTION
        This function uses Get-MgGroup to retrieve Unified Groups from the Graph API. It expands properties relevant to EasyLife such as policy states and metadata.
    .NOTES
        None.
    .LINK
        https://docs.easylife365.cloud/docs/add-ons/powershell/get-easygroup/
    .INPUTS
        None

        You cannot pipe values to this cmdlet.
    .OUTPUTS
        [ELGroup]

        This function returns object of the type ELGroup.
    .PARAMETER Id
        Use this parameter to retrieve a group with the specified Id.
    .PARAMETER All
        Use this parameter to retrieve all unified groups.
    .PARAMETER Top
        Use this parameter to retrieve the specified number of groups.
    .PARAMETER DisplayName
        Use this parameter to retrieve groups that have a DisplayName that starts with the given value.
    .PARAMETER PropertySet
        Use this parameter to select a set of attributes to be retrieved from the Graph API. This can be useful for automation or when dealing with a large number of objects.
        Currently, the only available set it Minimum.
    .EXAMPLE
        Get-EasyGroup
        
        This example returns up to 100 groups and their properties.
    .EXAMPLE
        Get-EasyGroup -All
        
        This example returns all groups and their properties.
    .EXAMPLE
        Get-EasyGroup -Top 3
        
        This example returns 3 groups and their properties.
    .EXAMPLE
        Get-EasyGroup -DisplayName Project
        
        This example returns all groups that have a DisplayName that starts with Project.
    .EXAMPLE
        Get-EasyGroup -All -PropertySet Minimum
        
        This example returns all groups with a minimum set of attributes that can be retrieved quickly.
        The resulting objects will not have the Owners property populated.
    #>

    [CmdletBinding(DefaultParameterSetName='byDisplayName', HelpUri = 'https://docs.easylife365.cloud/docs/add-ons/powershell/get-easygroup/')]
    [OutputType([ELGroup])]
    param (
        [Parameter(ParameterSetName='byId')]
        [ValidateNotNullOrEmpty()]
        [string]$Id,
        [Parameter(ParameterSetName='byDisplayName')]
        [ValidateNotNullOrEmpty()]
        [string]$DisplayName,
        [Parameter(ParameterSetName='byDisplayName')]
        [Parameter(ParameterSetName='all')]
        [int]$Top,
        [Parameter(ParameterSetName='all')]
        [switch]$All,
        [Parameter()]
        [ValidateSet('Minimum')]
        $PropertySet
    )
    begin {
        $sw,$diag = Start-EasyDiagnostics -Name $MyInvocation.InvocationName
    }
    process{
        $mgGroupParam = @{
            Filter = "groupTypes/any(c:c eq 'Unified')" 
            Select = 'id','displayName','description','mail','groupTypes','extensions','createdDateTime','mailNickname','visibility'
            ExpandProperty = "extensions,Owners(`$select=userPrincipalName)"
        }
        switch($PSBoundParameters.Keys){
            'Id' {
                $mgGroupParam.Remove('Filter')
                $mgGroupParam.Add('GroupId',$Id)
            }
            'DisplayName' {
                $mgGroupParam.Filter += " and startsWith(DisplayName,'$DisplayName')"
            }
            'Top' {
                $mgGroupParam.add('Top',$Top)
            }
            'All' {
                $mgGroupParam.add('All',([switch]::Present))
            }
            'PropertySet' {
                switch($PropertySet){
                    'Minimum' {
                        $mgGroupParam.ExpandProperty = "extensions"
                    }
                }
            }
        }
        Write-Verbose "Invoking Get-MgGroup with $($mgGroupParam | ConvertTo-Json -Compress)"
        Get-MgGroup @mgGroupParam | ForEach-Object {
            [ELGroup]::new($_,[bool]$SkipOwners)
        }
    }
    end {
        $sw.Stop()
        $diag.data.baseData.properties.duration = $sw.ElapsedMilliseconds
        $diag.data.baseData.properties.commandLine = $MyInvocation.Line
        Send-EasyTelemetryEvent -Body $diag
    }
}
#endregion Get-EasyGroup

#region Get-EasyGuestUser
function Get-EasyGuestUser {
    <#
    .SYNOPSIS
        Get EasyLife guest user accounts from the Graph API.
    .DESCRIPTION
        This function uses Get-MgUser to retrieve guest user accounts from the Graph API. It expands properties relevant to EasyLife such as owners and metadata.
    .NOTES
        None.
    .LINK
        https://docs.easylife365.cloud/docs/add-ons/powershell/get-easyguestuser/
    .INPUTS
        None

        You cannot pipe values to this cmdlet.
    .OUTPUTS
        [ELGuestUser]

        This function returns object of the type ELGuestUser.
    .EXAMPLE
        Get-EasyGuestUser
        
        This example returns up to 100 guest user accounts and their properties.
    .EXAMPLE
        Get-EasyGuestUser -All
        
        This example returns all guest user accounts and their properties.
    .EXAMPLE
        Get-EasyGuestUser -Top 3
        
        This example returns 3 guest user accounts and their properties.
    .EXAMPLE
        Get-EasyGuestUser -DisplayName Thomas
        
        This example returns all guest user accounts that have a DisplayName that starts with Thomas.
    .EXAMPLE
        Get-EasyGuestUser -All -PropertySet Minimum
        
        This example returns all guest user accounts with a minimum set of attributes that can be retrieved quickly.
        The resulting objects will contain the owner's object id instead of the owner's user principal name.
    #>

    [CmdletBinding(DefaultParameterSetName='byDisplayName', HelpUri = 'https://docs.easylife365.cloud/docs/add-ons/powershell/get-easyguestuser/')]
    [OutputType([ELGuestUser])]
    param (
        [Parameter(ParameterSetName='byId')]
        [ValidateNotNullOrEmpty()]
        [string]$Id,
        [Parameter(ParameterSetName='byDisplayName')]
        [ValidateNotNullOrEmpty()]
        [string]$DisplayName,
        [Parameter(ParameterSetName='byDisplayName')]
        [Parameter(ParameterSetName='all')]
        [int]$Top,
        [Parameter(ParameterSetName='all')]
        [switch]$All,
        [Parameter()]
        [ValidateSet('Minimum')]
        $PropertySet
    )
    begin {
        $sw,$diag = Start-EasyDiagnostics -Name $MyInvocation.InvocationName
        
        $mgUserParam = @{
            Filter = "userType eq 'Guest'" 
            ExpandProperty = "extensions"
            Select = 'id','displayName','GivenName','Surname','CompanyName','mail','accountEnabled',$script:elUserExtension,'externalUserState','externalUserStateChangeDateTime','createdDateTime'
        }

        switch($PSBoundParameters.Keys){
            'Id' {
                $mgUserParam.Remove('Filter')
                $mgUserParam.Add('UserId',$Id)
            }
            'DisplayName' {
                $mgUserParam.Filter += " and startsWith(DisplayName,'$DisplayName')"
            }
            'Top' {
                $mgUserParam.add('Top',$Top)
            }
            'All' {
                $mgUserParam.add('All',([switch]::Present))
            }
            'PropertySet' {
                switch($PropertySet){
                    'Minimum' {
                        $fastMode = $true
                    }
                    Default {
                        $fastMode = $false
                    }
                }
            }
        }
    }
    process {
        Write-Verbose "Invoking Get-MgUser with $($mgUserParam | ConvertTo-Json -Compress)"
        Get-MgUser @mgUserParam | ForEach-Object {
            [ELGuestUser]::new($_,$fastMode)
        }
    }
    end {
        $sw.stop()
        $diag.data.baseData.properties.duration = $sw.ElapsedMilliseconds
        $diag.data.baseData.properties.commandLine = $MyInvocation.Line
        Send-EasyTelemetryEvent -Body $diag
    }
}

#endregion Get-EasyGuestUser

#region Get-EasyTeam
function Get-EasyTeam {
    <#
    .SYNOPSIS
        Get EasyLife Teams from the Graph API.
    .DESCRIPTION
        This function uses Get-MgGroup and Get-MgTeam to retrieve Teams from the Graph API. It expands properties relevant to EasyLife such as policy states and metadata.
    .NOTES
        This function makes two requests to the Graph API for each Team, hence it is slower than Get-EasyGroup.
    .LINK
        https://docs.easylife365.cloud/docs/add-ons/powershell/get-easyTeam/
    .INPUTS
        None

        You cannot pipe values to this cmdlet.
    .OUTPUTS
        [ELTeam]

        This function returns object of the type ELTeam.
    .PARAMETER Id
        Use this parameter to retrieve a teams with the specified Id.
    .PARAMETER All
        Use this parameter to retrieve all unified teams.
    .PARAMETER Top
        Use this parameter to retrieve the specified number of teams.
    .PARAMETER DisplayName
        Use this parameter to retrieve teams that have a DisplayName that starts with the given value.
    .EXAMPLE
        Get-EasyTeam
        
        This example returns up to 100 Teams and their properties.
    .EXAMPLE
        Get-EasyTeam -All
        
        This example returns all Teams and their properties.
    .EXAMPLE
        Get-EasyTeam -Top 3
        
        This example returns 3 Teams and their properties.
    .EXAMPLE
        Get-EasyTeam -DisplayName Project
        
        This example returns all Teams that have a DisplayName that starts with Project.
    .EXAMPLE
        Get-EasyTeam -All -PropertySet Minimum
        
        This example returns all Teams with a minimum set of attributes that can be retrieved quickly.
        The resulting objects will not have the Owners and WebUrl properties populated.
    #>

    [CmdletBinding(DefaultParameterSetName='byDisplayName', HelpUri = 'https://docs.easylife365.cloud/docs/add-ons/powershell/get-easyTeam/')]
    [OutputType([ELTeam])]
    param (
        [Parameter(ParameterSetName='byId')]
        [ValidateNotNullOrEmpty()]
        [string]$Id,
        [Parameter(ParameterSetName='byDisplayName')]
        [ValidateNotNullOrEmpty()]
        [string]$DisplayName,
        [Parameter(ParameterSetName='byDisplayName')]
        [Parameter(ParameterSetName='all')]
        [int]$Top,
        [Parameter(ParameterSetName='all')]
        [switch]$All,
        [Parameter()]
        [ValidateSet('Minimum')]
        $PropertySet
    )
    begin {
        $sw,$diag = Start-EasyDiagnostics -Name $MyInvocation.InvocationName
    }
    process{
        $mgGroupParam = @{
            Filter = "groupTypes/any(c:c eq 'Unified') and resourceProvisioningOptions/Any(x:x eq 'Team')"
            Select = 'id','displayName','description','mail','groupTypes','extensions','createdDateTime','mailNickname','visibility'
            ExpandProperty = "extensions,Owners(`$select=userPrincipalName)"
        }
        switch($PSBoundParameters.Keys){
            'Id' {
                $mgGroupParam.Remove('Filter')
                $mgGroupParam.Add('GroupId',$Id)
            }
            'DisplayName' {
                $mgGroupParam.Filter += " and startsWith(DisplayName,'$DisplayName')"
            }
            'Top' {
                $mgGroupParam.add('Top',$Top)
            }
            'All' {
                $mgGroupParam.add('All',([switch]::Present))
            }
            'PropertySet' {
                switch($PropertySet){
                    'Minimum' {
                        $mgGroupParam.ExpandProperty = "extensions(`$filter = startsWith(id, 'cloud.easyLife'))"
                        $fastMode = $true
                    }
                    Default {
                        $fastMode = $false
                    }
                }
            }
        }
        Write-Verbose "Invoking Get-MgGroup with $($mgGroupParam | ConvertTo-Json -Compress)"
        Get-MgGroup @mgGroupParam | ForEach-Object {
            $mgGroup = $_
            $mgTeam = if($fastMode) {$null} else {Get-MgTeam -TeamId $_.id} 
            [ELTeam]::new($mgGroup, $mgTeam, $fastMode)
        }
    }
    end {
        $sw.Stop()
        $diag.data.baseData.properties.duration = $sw.ElapsedMilliseconds
        $diag.data.baseData.properties.commandLine = $MyInvocation.Line
        Send-EasyTelemetryEvent -Body $diag
    }
}




#endregion Get-EasyTeam

#region Grant-EasyAppPermissions

function Grant-EasyAppPermissions {
    <#
    .SYNOPSIS
        Grant reqiured Graph API permissions to service principals.
    .DESCRIPTION
        This function uses New-MgServicePrincipalAppRoleAssignedTo to assign all required application permissions for the EasyLife365.Collaboration module to a service principal in your Azure Active Directory.
        You can use this function to grant required permissions to managed identities of Azure Automation Accounts for Function Apps that run the EasyLife365.Collabotration powershell module and use
        Connect-EasyLife365 -Identity to sign in.
    .NOTES
        This function requires the scopes AppRoleAssignment.ReadWrite.All and Application.Read.All only during setup. These application permissions will not be granted to the service principal.
        You can find the ServicePrincipalId in the Identity tab of the Automation Account or Function App in the Azure Portal.
    .LINK
        https://docs.easylife365.cloud/docs/add-ons/powershell/grant-easyapppermissions/
    .INPUTS
        None

        You cannot pipe values to this cmdlet.
    .OUTPUTS
        [MicrosoftGraphAppRoleAssignment]

        This function returns object of the type MicrosoftGraphAppRoleAssignment.
    .PARAMETER ServicePrincipalId
        Use this parameter to specify the object id of the service principal that will be granted permissions.
    .PARAMETER AppRoles
        Use this parameter to specify which application permissions shall be granted to the service principal.
        Use 'Read' to grant User.Read.All, Group.Read.All, Team.ReadBasic.All.
        Use 'Write' to grant User.ReadWrite.All, Group.ReadWrite.All, Team.ReadBasic.All.
        Use a custom set of permissions by specifying the name of each application permission like this: 'Group.ReadWrite.All', 'Team.ReadBasic.All'.
    .EXAMPLE
        Grant-EasyAppPermissions -ServicePrincipalId da9ea79a-55d4-463f-b1a2-4b5ab1060909
        
        This example grants the application permissions User.Read.All, Group.Read.All, Team.ReadBasic.All to the service principal with the id da9ea79a-55d4-463f-b1a2-4b5ab1060909.
    .EXAMPLE
        Grant-EasyAppPermissions -ServicePrincipalId da9ea79a-55d4-463f-b1a2-4b5ab1060909 -AppRoles Write
        
        This example grants the application permissions User.ReadWrite.All, Group.ReadWrite.All, Team.ReadBasic.All to the service principal with the id da9ea79a-55d4-463f-b1a2-4b5ab1060909.
    .EXAMPLE
        Grant-EasyAppPermissions -ServicePrincipalId da9ea79a-55d4-463f-b1a2-4b5ab1060909 -AppRoles 'User.ReadWrite.All', 'Group.ReadWrite.All', 'Team.ReadBasic.All', 'Application.Read.All
        
        This example grants a custom set of application permissions to the service principal with the id da9ea79a-55d4-463f-b1a2-4b5ab1060909.
    #>

    [CmdletBinding(SupportsShouldProcess,HelpUri='https://docs.easylife365.cloud/docs/add-ons/powershell/grant-easyapppermissions/')]
    param (
        [Parameter(Mandatory)]
        [guid]$ServicePrincipalId,
        [string[]]$AppRoles = "Read"
    )
    process {
        $modules = Get-Module Microsoft.Graph.Authentication,Microsoft.Graph.Applications -ListAvailable
        if($modules.Count -ge 2){
            Write-Information "Found Graph Modules, importing" -InformationAction Continue
            Import-Module Microsoft.Graph.Authentication,Microsoft.Graph.Applications
        } else {
            Write-Warning "Could not find Graph Modules, please install with the following command:`r`nInstall-Module Microsoft.Graph.Authentication,Microsoft.Graph.Applications"
            return
        }
        # we need to have the scope Application.Read.All otherwise Get-MgServicePrincipal will fail
        $ctx = Get-MgContext
        $ctxScopes = $ctx.Scopes
        if($ctxScopes -notcontains 'AppRoleAssignment.ReadWrite.All' -and $ctxScopes -notcontains 'Application.Read.All') {
            Write-Warning "Graph Context does not have all required scopes, please connect with the following command:`r`nConnect-MgGraph -Scopes AppRoleAssignment.ReadWrite.All, Application.Read.All"
            return
        } else {
            Write-Information "Using existing Graph context with account $($ctx.Account)" -InformationAction Continue
        }
        # get microsoft graph enterprise application, it holds all the roles and we need its object id
        $graphObject = Get-MgServicePrincipal -Filter "DisplayName eq 'Microsoft Graph'"
        switch($AppRoles){
            'Read' {
                $appRolesWithId = Get-EasyAppRoleId -graphObject $graphObject -AppRoles $elRequiredScopes.Get
            }
            'Write' { 
                $appRolesWithId = Get-EasyAppRoleId -graphObject $graphObject -AppRoles $elRequiredScopes.Set
            }
            Default {
                $appRolesWithId = Get-EasyAppRoleId -graphObject $graphObject -AppRoles $AppRoles
            }
        }
        # once we have the ids of the application permissions, we grant them to the service principal
        $appRolesWithId | ForEach-Object {
            Write-Verbose "Granting permission [$($_.Value)] to service principal [$servicePrincipalId]"
            $params = @{
                ServicePrincipalId = $servicePrincipalId
                PrincipalId = $servicePrincipalId
                ResourceId = $graphObject.Id
                AppRoleId = $_.Id
            }
            if ($PSCmdlet.ShouldProcess($servicePrincipalId, "Add permission $($_.Id)")) {
                New-MgServicePrincipalAppRoleAssignedTo @params | Select-Object PrincipalDisplayName,PrincipalId,AppRoleId
            }
        }
    }
}
#endregion Grant-EasyAppPermissions