NewFunctions.ps1


#Functions to create, test, and finalize before rolling into prodution module.

#region remove PS 2.0

Function Remove-PowerShell2 {
    # Check to make sure removal of components did not blank the PowerShellEngine registry key, and add the values back if it was.
    If (-not(Get-Item -Path "HKLM:\SOFTWARE\Microsoft\PowerShell\1\PowerShellEngine" -ErrorAction SilentlyContinue)) {
           Write-Host " Recreating missing PowerShell registry keys resulting from removing old Windows Features."
           New-Item -Path "HKLM:\SOFTWARE\Microsoft\PowerShell\1" -Name PowerShellEngine | Out-Null
           New-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\PowerShell\1\PowerShellEngine" -Name ApplicationBase -Value "C:\Windows\System32\WindowsPowerShell\v1.0" -PropertyType String | Out-Null
           New-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\PowerShell\1\PowerShellEngine" -Name PSCompatibleVersion -Value "1.0, 2.0" -PropertyType String | Out-Null
           New-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\PowerShell\1\PowerShellEngine" -Name RuntimeVersion -Value "v2.0.50727" -PropertyType String | Out-Null
           New-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\PowerShell\1\PowerShellEngine" -Name ConsoleHostAssemblyName -Value "Microsoft.PowerShell.ConsoleHost, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, ProcessorArchitecture=msil" -PropertyType String | Out-Null
           New-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\PowerShell\1\PowerShellEngine" -Name ConsoleHostModuleName -Value "C:\Windows\System32\WindowsPowerShell\v1.0\Microsoft.PowerShell.ConsoleHost.dll" -PropertyType String | Out-Null
           New-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\PowerShell\1\PowerShellEngine" -Name PowerShellVersion -Value "2.0" -PropertyType String | Out-Null
    }
    
    }

#endregion remove PS 2.0

#region Set shared mailbox permissions and create groups to be set
Function Set-SharedMailboxPermissions {
  <#
  .SYNOPSIS
      Setups permissions of a shared mailbox, hides from GAL, and creates 2 new additional security groups as assigned values.
  .DESCRIPTION
      Long description
  .EXAMPLE
      Set-SharedMailboxPermissions
 
      This function will prompt the end user to enter an already existent shared mailbox.
  .INPUTS
      The input is the value of the alias of the already created shared mailbox to setup with additional security groups.
  .OUTPUTS
      Displays the values of the set shared mailbox and permissions assigned.
  #>


  [cmdletbinding()]
  param ($domain = '@contoso.com')

  <#
      Prompt the user for the shared mailbox name that was migrated from on-premises
      Take that name and make it a variable to create the two security groups with secure access to the shared mailbox.
  #>

      $SMBX = Read-Host -Prompt "Please enter the Alias of the Shared Mailbox to create groups and apply permissions to:"
      Write-Verbose $SMBX

  <#
      Create the Full Access security group using the shared mailbox name appended by '-FullAccess'
  #>

      $SMBXFA=New-DistributionGroup -Name $SMBX-FullAccess -PrimarySmtpAddress $SMBX-FullAccess$domain -Type Security
      Write-Verbose "`$SMBXFA array contains the value: $SMBXFA"
      Write-Verbose "`$domain array contains the value: $domain"

  <#
      Create the Send As security group using the shared mailbox name appended by '-SendAs'
  #>

      $SMBXSA=New-DistributionGroup -Name $Global:SMBX-SendAs -PrimarySmtpAddress $Global:SMBX-SendAs@ch2m.onmicrosoft.com -Type Security
      Write-Verbose "`$SMBXSA array contains the value: $SMBXSA"

  <#
      These next two lines don't work as I want them to; the variable is null as best I can tell
      The goal of the next two lines is to set both security groups as hiddenfromtheaddresslist
  #>

      Set-DistributionGroup -Identity ($SMBXFA).name -HiddenFromAddressListsEnabled:$true
      Set-DistributionGroup -Identity ($SMBXSA).Name -HiddenFromAddressListsEnabled:$true

  <#
      Set the two security groups and give the permissions to the shared mailbox
      Since the variables $SMBXFA and $SMBXFA are null the rest of this fails I think
      (I can't test it because the variables are broke)
  #>

      Add-MailboxPermission -Identity $SMBX -User ($SMBXFA).Name -AccessRights FullAccess
      Add-RecipientPermission -Identity $SMBX -User ($SMBXSA).Name -AccessRights SendAs

  <#
      Set email using SendAs shows up in the Shared Mailbox Sent Item Folder
  #>

      Set-mailbox $SMBX -MessageCopyForSentAsEnabled:$true

  <#
      Display results:
      - Full Access Permission on the Shared Mailbox
      - Send As Permission on the Shared Mailbox
  #>

      Get-MailboxPermission $SMBX  | Format-Table -AutoSize
      Get-RecipientPermission $SMBX | Format-Table -AutoSize
  }

  Set-SharedMailboxPermissions
#endregion Set shared mailbox permissions and create groups to be set



#region Get-DGMatch

function Get-DistributionGroupMatching {
<#
.Synopsis
   Get Distribution Groups that match an @Domain name.
.DESCRIPTION
   Present data that will crawl all the DLs and find any that have any recipients with an @domain email address value.
.EXAMPLE
   Get-DistributionGroupMatching -Domain Contoso.com
 
   Obtains Distribution Groups that contain the domain like Contoso.com
.EXAMPLE
   Get-DistributionGroupMatching -Domain BlueYonderAirlines.com
 
   Obtains Distribution Groups that contain the domain like BlueYonderAirlines.com
.INPUTS
   Domain value must be specified.
.OUTPUTS
   Present results from parameter statement.
.NOTES
   Written by Mike Hendrickson, updated by Mike O'Neill
.ROLE
   Must be connected to either an Exchange on premises server or Exchange online to obtain distribution group values.
#>

   [CmdletBinding()]
   Param([Parameter(Mandatory=$true)]$Domain)

            $dgs = Get-DistributionGroup -ResultSize Unlimited
            ForEach ($DG in $dgs)
            {
                  Get-DistributionGroupMember -ResultSize Unlimited -Identity $DG.Name | Where-Object {$_.EmailAddresses -like "*$Domain*"}
                  $DG.Name
                  if ($DG.Count -gt 0) {$DG | Format-Table name,id}
                  #else {Write-Host "No distribution groups found with members that contain $Domain value." -ForegroundColor Cyan}
            }
}

Get-DistributionGroupMatching -Domain mcdeo.com



#endregion Get-DGMatch


#region SP group membership listing
Function Get-SPOAllSitePermissions {
    <#
    .SYNOPSIS
       Obtains and presents SharePoint online site permissions.
    .DESCRIPTION
       Long description
    .EXAMPLE
       Get-SPOAllSitePermissions
     
       Presents all of the SPO sites and their permissions with a default output path of: C:\Temp\AllSitePermissions.csv
    .EXAMPLE
       Get-SPOAllSitePermissions $true -OutputPath $env:temp\SPPermissions.csv
     
       Presents all of the SPO sites and their group membership permissions in the path of: $env:temp\SPPermissions.csv.
    .EXAMPLE
        #Pass "$true" for second parameter to get the group users need to confirm if needed and/or to work.
     
    .OUTPUTS
       Output file, default C:\Temp\AllSitePermissions.csv
    .NOTES
       Original script from:
       Start-Process https://gallery.technet.microsoft.com/office/SharePoint-Online-c9ec4f64
    .COMPONENT
       The component this cmdlet belongs SharePoint online using the SPO module.
       Ensure you are logged on and have access to the SPO tenant environment.
    .ROLE
       The role this cmdlet belongs to SharePoint online.
    #>

    
    param($url="https://mcdeo-admin.sharepoint.com",
        $includeGroupUsers,
        $OutputFile = "C:\Temp\AllSitePermissions.csv")
    
    $header = "Site,HasUniquePerm?,Group Name,Group Owner,Login Name,Roles,Principal Type,"
    $header += if($includeGroupUsers) { "Group User LoginName,Group User DisplayName" } else { "" }
    Set-Content $OutputFile $header
    
    $Creds = Get-Credential
    $admin = $creds.UserName
    $pass = $creds.Password
    
    function Load-CSOMProperties {
    <#
    .SYNOPSIS
        Facilitates the loading of specific properties of a Microsoft.SharePoint.Client.ClientObject object or Microsoft.SharePoint.Client.ClientObjectCollection object.
    .DESCRIPTION
        Replicates what you would do with a lambda expression in C#.
        For example, "ctx.Load(list, l => list.Title, l => list.Id)" becomes
        "Load-CSOMProperties -object $list -propertyNames @('Title', 'Id')".
    .EXAMPLE
        Load-CSOMProperties -parentObject $web -collectionObject $web.Fields -propertyNames @("InternalName", "Id") -parentPropertyName "Fields" -executeQuery
        $web.Fields | select InternalName, Id
    .EXAMPLE
       Load-CSOMProperties -object $web -propertyNames @("Title", "Url", "AllProperties") -executeQuery
       $web | select Title, Url, AllProperties
    #>

        [CmdletBinding(DefaultParameterSetName='ClientObject')]
        param (
            # The Microsoft.SharePoint.Client.ClientObject to populate.
            [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position = 0, ParameterSetName = "ClientObject")]
            [Microsoft.SharePoint.Client.ClientObject]
            $object,
    
            # The Microsoft.SharePoint.Client.ClientObject that contains the collection object.
            [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position = 0, ParameterSetName = "ClientObjectCollection")]
            [Microsoft.SharePoint.Client.ClientObject]
            $parentObject,
    
            # The Microsoft.SharePoint.Client.ClientObjectCollection to populate.
            [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position = 1, ParameterSetName = "ClientObjectCollection")]
            [Microsoft.SharePoint.Client.ClientObjectCollection]
            $collectionObject,
    
            # The object properties to populate
            [Parameter(Mandatory = $true, Position = 1, ParameterSetName = "ClientObject")]
            [Parameter(Mandatory = $true, Position = 2, ParameterSetName = "ClientObjectCollection")]
            [string[]]
            $propertyNames,
    
            # The parent object's property name corresponding to the collection object to retrieve (this is required to build the correct lamda expression).
            [Parameter(Mandatory = $true, Position = 3, ParameterSetName = "ClientObjectCollection")]
            [string]
            $parentPropertyName,
    
            # If specified, execute the ClientContext.ExecuteQuery() method.
            [Parameter(Mandatory = $false, Position = 4)]
            [switch]
            $executeQuery
        )
    
            if ($PsCmdlet.ParameterSetName -eq "ClientObject") {
                $type = $object.GetType()
            } else {
                $type = $collectionObject.GetType() 
                if ($collectionObject -is [Microsoft.SharePoint.Client.ClientObjectCollection]) {
                    $type = $collectionObject.GetType().BaseType.GenericTypeArguments[0]
                }
            }
    
            $exprType = [System.Linq.Expressions.Expression]
            $parameterExprType = [System.Linq.Expressions.ParameterExpression].MakeArrayType()
            $lambdaMethod = $exprType.GetMethods() | ? { $_.Name -eq "Lambda" -and $_.IsGenericMethod -and $_.GetParameters().Length -eq 2 -and $_.GetParameters()[1].ParameterType -eq $parameterExprType }
            $lambdaMethodGeneric = Invoke-Expression "`$lambdaMethod.MakeGenericMethod([System.Func``2[$($type.FullName),System.Object]])"
            $expressions = @()
    
            foreach ($propertyName in $propertyNames) {
                $param1 = [System.Linq.Expressions.Expression]::Parameter($type, "p")
                try {
                    $name1 = [System.Linq.Expressions.Expression]::Property($param1, $propertyName)
                } catch {
                    Write-Error "Instance property '$propertyName' is not defined for type $type"
                    return
                }
                $body1 = [System.Linq.Expressions.Expression]::Convert($name1, [System.Object])
                $expression1 = $lambdaMethodGeneric.Invoke($null, [System.Object[]] @($body1, [System.Linq.Expressions.ParameterExpression[]] @($param1)))
     
                if ($collectionObject -ne $null) {
                    $expression1 = [System.Linq.Expressions.Expression]::Quote($expression1)
                }
                $expressions += @($expression1)
            }
    
    
            if ($PsCmdlet.ParameterSetName -eq "ClientObject") {
                $object.Context.Load($object, $expressions)
                if ($executeQuery) { $object.Context.ExecuteQuery() }
            } else {
                $newArrayInitParam1 = Invoke-Expression "[System.Linq.Expressions.Expression``1[System.Func````2[$($type.FullName),System.Object]]]"
                $newArrayInit = [System.Linq.Expressions.Expression]::NewArrayInit($newArrayInitParam1, $expressions)
    
                $collectionParam = [System.Linq.Expressions.Expression]::Parameter($parentObject.GetType(), "cp")
                $collectionProperty = [System.Linq.Expressions.Expression]::Property($collectionParam, $parentPropertyName)
    
                $expressionArray = @($collectionProperty, $newArrayInit)
                $includeMethod = [Microsoft.SharePoint.Client.ClientObjectQueryableExtension].GetMethod("Include")
                $includeMethodGeneric = Invoke-Expression "`$includeMethod.MakeGenericMethod([$($type.FullName)])"
    
                $lambdaMethodGeneric2 = Invoke-Expression "`$lambdaMethod.MakeGenericMethod([System.Func``2[$($parentObject.GetType().FullName),System.Object]])"
                $callMethod = [System.Linq.Expressions.Expression]::Call($null, $includeMethodGeneric, $expressionArray)
                
                $expression2 = $lambdaMethodGeneric2.Invoke($null, @($callMethod, [System.Linq.Expressions.ParameterExpression[]] @($collectionParam)))
    
                $parentObject.Context.Load($parentObject, $expression2)
                if ($executeQuery) { $parentObject.Context.ExecuteQuery() }
            }
    }
    
        $ctx = New-Object Microsoft.SharePoint.Client.ClientContext($url)
        $ctx.Credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($admin, $pass)
        $web = $ctx.Web
        Load-CSOMProperties -Object $web -PropertyNames @("HasUniqueRoleAssignments", "Url", "Title")    
        $ctx.Load($ctx.Web.Webs)    
        $ctx.Load($ctx.Web.SiteGroups)
        $ctx.Load($ctx.Web.RoleAssignments)    
        $ctx.ExecuteQuery()
        Write-Host $web.Url
        $webUrl = $web.Url    
        $record = "`"$webUrl`",$($web.HasUniqueRoleAssignments),"     
        if($web.HasUniqueRoleAssignments -eq $true) {
            $firstIteration = $true #helps when to append commas
            foreach($roleAssignment in $ctx.Web.RoleAssignments) {
                $ctx.Load($roleAssignment.Member)
                $ctx.Load($roleAssignment.RoleDefinitionBindings)
                $ctx.ExecuteQuery()
                $roles = ($roleAssignment.RoleDefinitionBindings | Select -ExpandProperty Name) -join ", ";
                $loginName = if($roleAssignment.Member.PrincipalType -eq "User") { $($roleAssignment.Member.LoginName) } else { "" }
                $record += if($firstIteration) { "" } else { ",," }
                $record += "`"$($roleAssignment.Member.Title)`",`"$($roleAssignment.Member.OwnerTitle)`","
                $record += "`"$loginName`",`"$roles`","   
                $record += $($roleAssignment.Member.PrincipalType)
                Add-Content $OutputFile $record
                $firstIteration = $false
                $record = ""
                if($includeGroupUsers) {
                    if($roleAssignment.Member.PrincipalType -eq "SharePointGroup") {
                        $group = $ctx.Web.SiteGroups.GetByName($roleAssignment.Member.Title)    
                        $ctx.Load($group)
                        $users = $group.Users
                        $ctx.Load($users)
                        $ctx.ExecuteQuery()
                        foreach($user in $users) {
                               $record = ",,,,,,"
                            $record += "$($user.PrincipalType),"
                            $record += "`"$($user.LoginName)`",`"$($user.Title)`""
                            Add-Content $OutputFile $record
                            $record = ""                    
                        }
                    }
                }
            }
        }
        else {
            Add-Content $OutputFile $record #you can refer the permissions from its parent web.
        }
        if($web.Webs.Count -eq 0)
        {
       
        }
        else {
            foreach ($web in $web.Webs) {
                Get-SPOAllSitePermissions -Url $web.Url
            }
        }
    
    # Paths to SDK. Please verify location on your computer.
    # On farm it would be available at c:\Program Files\Common Files\microsoft shared\Web Server Extensions\16\ISAPI\
    # On a normal SDK install, the default x64 path is: C:\Program Files\SharePoint Online Management Shell\Microsoft.Online.SharePoint.PowerShell
    Add-Type -Path "C:\Program Files\SharePoint Online Management Shell\Microsoft.Online.SharePoint.PowerShell\Microsoft.SharePoint.Client.dll" #Default location with SDK install.
    Add-Type -Path "C:\Program Files\SharePoint Online Management Shell\Microsoft.Online.SharePoint.PowerShell\Microsoft.SharePoint.Client.Runtime.dll" #Default location with SDK install.
    
    If (Test-Path -Path $OutputFile)
        {
          Write-Host "File $OutputFile exists." -ForegroundColor Green
        }
    
    Else
        {
          Write-Host "File $OutputFile was not created." -ForegroundColor Red
        }
    
    }

#endregion SPO Group membership listing.


#region remove Exchange servers from AD

Function Remove-ExchangeServerFromAD {
    [cmdletbinding()]
    param($domain, #domain logging into for searching for servers
        $suffix='com', #Can be local, edu, org, com, or value that is in the on premises domain
        $AdministrativeGroup #this needs to be known within the environment
        )

Set-Location "CN=Servers,CN=Exchange Administrative Group(FYD12345789),CN=Administrative Groups,CN=$AdministrativeGroup,CN=Microsoft Exchange,CN=Services,CN=Configuration,DC=$domain,DC=$suffix"


CN=Microsoft Exchange >CN=CloudTalks>CN=Administrative Groups>CN=Exchange Administrative Group(FYD12345789)>CN=Servers

Start-Process https://msexperttalk.com/remove-exchange-server-using-adsi-edit/ #process to find.

}

#endregion remove Exchange servers from AD


#region Get-ADDirectReports
function Get-ADDirectReports
{
    <#
    .SYNOPSIS
        This function retrieve the directreports property from the IdentitySpecified.
        Optionally you can specify the Recurse parameter to find all the indirect
        users reporting to the specify account (Identity).
 
    .DESCRIPTION
        This function retrieve the directreports property from the IdentitySpecified.
        Optionally you can specify the Recurse parameter to find all the indirect
        users reporting to the specify account (Identity).
 
    .NOTES
        Francois-Xavier Cat
        www.lazywinadmin.com
        @lazywinadm
 
        VERSION HISTORY
        1.0 2014/10/05 Initial Version
 
    .PARAMETER Identity
        Specify the account to inspect
 
    .PARAMETER Recurse
        Specify that you want to retrieve all the indirect users under the account
 
    .EXAMPLE
        Get-ADDirectReports -Identity Test_director
 
Name SamAccountName Mail Manager
---- -------------- ---- -------
test_managerB test_managerB test_managerB@la... test_director
test_managerA test_managerA test_managerA@la... test_director
 
    .EXAMPLE
        Get-ADDirectReports -Identity Test_director -Recurse
 
Name SamAccountName Mail Manager
---- -------------- ---- -------
test_managerB test_managerB test_managerB@la... test_director
test_userB1 test_userB1 test_userB1@lazy... test_managerB
test_userB2 test_userB2 test_userB2@lazy... test_managerB
test_managerA test_managerA test_managerA@la... test_director
test_userA2 test_userA2 test_userA2@lazy... test_managerA
test_userA1 test_userA1 test_userA1@lazy... test_managerA
 
    #>

    [CmdletBinding()]
    PARAM (
        [Parameter(Mandatory)]
        [String[]]$Identity,
        [Switch]$Recurse
    )
    BEGIN
    {
        TRY
        {
            IF (-not (Get-Module -Name ActiveDirectory)) { Import-Module -Name ActiveDirectory -ErrorAction 'Stop' -Verbose:$false }
        }
        CATCH
        {
            Write-Verbose -Message "[BEGIN] Something wrong happened"
            Write-Verbose -Message $Error[0].Exception.Message
        }
    }
    PROCESS
    {
        foreach ($Account in $Identity)
        {
            TRY
            {
                IF ($PSBoundParameters['Recurse'])
                {
                    # Get the DirectReports
                    Write-Verbose -Message "[PROCESS] Account: $Account (Recursive)"
                    Get-Aduser -identity $Account -Properties directreports |
                    ForEach-Object -Process {
                        $_.directreports | ForEach-Object -Process {
                            # Output the current object with the properties Name, SamAccountName, Mail and Manager
                            Get-ADUser -Identity $PSItem -Properties mail, manager | Select-Object -Property Name, SamAccountName, Mail, @{ Name = "Manager"; Expression = { (Get-Aduser -identity $psitem.manager).samaccountname } }
                            # Gather DirectReports under the current object and so on...
                            Get-ADDirectReports -Identity $PSItem -Recurse
                        }
                    }
                }#IF($PSBoundParameters['Recurse'])
                IF (-not ($PSBoundParameters['Recurse']))
                {
                    Write-Verbose -Message "[PROCESS] Account: $Account"
                    # Get the DirectReports
                    Get-Aduser -identity $Account -Properties directreports | Select-Object -ExpandProperty directReports |
                    Get-ADUser -Properties mail, manager | Select-Object -Property Name, SamAccountName, Mail, @{ Name = "Manager"; Expression = { (Get-Aduser -identity $psitem.manager).samaccountname } }
                }#IF (-not($PSBoundParameters['Recurse']))
            }#TRY
            CATCH
            {
                Write-Verbose -Message "[PROCESS] Something wrong happened"
                Write-Verbose -Message $Error[0].Exception.Message
            }
        }
    }
    END
    {
        Remove-Module -Name ActiveDirectory -ErrorAction 'SilentlyContinue' -Verbose:$false | Out-Null
    }
}

<#
# Find all direct user reporting to Test_director
Get-ADDirectReports -Identity Test_director
 
# Find all Indirect user reporting to Test_director
Get-ADDirectReports -Identity Test_director -Recurse
#>

#endregion Get-ADDirectReports

#region Get-NetConnectionProfileWin7+

Function Get-NetConnectionProfileWin7 {
  <#
  .SYNOPSIS
     Works in Windows 7 with PS 2 and above OS versions, with higher PS versions.
  .DESCRIPTION
     Function that gets a connection profile associated with one or more physical network adapters.
     A connection profile represents a network connection.
  .EXAMPLE
     Get-NetConnectionProfileWin7
 
     Displays the current network connections of the local computer.
  .NOTES
     Created for Windows 7 and PS 2.0+.
  #>

  [cmdletbinding(HelpUri="")]
  param()

  $NetworkListManager = [Activator]::CreateInstance([Type]::GetTypeFromCLSID(‘DCB00C01-570F-4A9B-8D69-199FDBA5723B’))
  $NLM_ENUM_NETWORK_CONNECTED = 1
  $NLM_ENUM_NETWORK_DISCONNECTED = 2
  $NLM_ENUM_NETWORK_ALL = 3

  foreach($net in $NetworkListManager.GetNetworks($NLM_ENUM_NETWORK_CONNECTED))
      {
          Write-Host "Name: "$net.GetName() -ForegroundColor Green
          Write-Host "Status of connection: " $net.IsConnected
          Write-Host ""
      }
  }

#endregion Get-NetConnectionProfileWin7+


#region Get-DBMailboxCount

#
#
# NAME: Get-MDBMailboxCount.ps1
#
# AUTHOR: Jan Egil Ring
# EMAIL: jan.egil.ring@powershell.no
#
# COMMENT: Script to retrieve number of users per mailbox database within the Exchange-organization.
# System.DirectoryServices.DirectorySearcher are used to gather information rather than "Get-Mailbox -Database x"
# due to high performance benefits.
# The information are stored in the variable $MDBInfo which may be used for further processing (i.e. retrieve the
# database with the lowest/highest number of users and so on).
# $MDBInfo are output to a CSV-file saved in current user`s Documents-folder.
# Tested against Exchange 2010 only.
#
# For more information, see the following blog-post:
# http://blog.powershell.no/2010/11/21/retrieve-number-of-mailboxes-per-database-in-exchange-server-2010
#
# You have a royalty-free right to use, modify, reproduce, and
# distribute this script file in any way you find useful, provided that
# you agree that the creator, owner above has no warranty, obligations,
# or liability for such use.
#
# VERSION HISTORY:
# 1.0 20.11.2010 - Initial release
#
#

#Define function for counting number of mailboxes per mailbox database
function Get-MDBMailboxCount ([string]$DN) {
  $Searcher = New-Object System.DirectoryServices.DirectorySearcher
  $Searcher.SearchRoot = New-Object System.DirectoryServices.DirectoryEntry ("LDAP://$(([system.directoryservices.activedirectory.domain]::GetCurrentDomain()).Name)")
  $Searcher.Filter = "(&(objectClass=user)(homeMDB=$DN))"
  $Searcher.PageSize = 10000
  $Searcher.SearchScope = "Subtree"
  $results = $Searcher.FindAll()

  $returnValue = $results.Count

  #dispose of the search and results properly to avoid a memory leak
   $Searcher.Dispose()
   $results.Dispose()

  return $returnValue
  }

  #Variable for writing progress information
  $Count = 1

  #Variables for working with mailbox databases
  $MDBs = Get-MailboxDatabase
  $MDBInfo = @()

  foreach ($MDB in $MDBs) {

  #Write progress information
  Write-Progress -Activity "Gathering database info..." -Status "Current database: $($MDB.Name)" -Id 1 -PercentComplete (($Count/$MDBs.Count) * 100)

  #Create a new object and add custom note properties for each database
  $obj = New-Object -TypeName psobject
  $obj | Add-Member -Name "Mailbox Database" -Value $MDB.Name -MemberType NoteProperty
  $obj | Add-Member -Name "Number of users" -Value (Get-MDBMailboxCount -DN $MDB.DistinguishedName) -MemberType NoteProperty

  #Add current database-object to the $MDBInfo-array
  $MDBInfo += $obj

  #Increase counter variable
  $Count ++

  }

  #Export $MDBInfo-array to a CSV-file
  $MDBInfo | Export-Csv -Path $HOME\Documents\MDBUserCount.csv -NoTypeInformation


#endregion Get-DBMailboxCount


#region Export list of locations O365 users are logging on from
# https://gcits.com/knowledge-base/export-list-locations-office-365-users-logging/
#endregion Export list of locations O365 users


#region mail.que


function New-MailQueDatabase { #Invoke-Command -ComputerName SP2010-WFE -scriptBlock { C:\scripts\Test.PS1 }
<#
.SYNOPSIS
   Function to create new mail.que database.
.DESCRIPTION
   This is a function to assist Exchange administrators on creating a new mail.que DB and use it in the new location.
.EXAMPLE
   Example of how to use this cmdlet
.EXAMPLE
   Another example of how to use this cmdlet
.INPUTS
   Path for new location of where the mail.que database is to exist.
.OUTPUTS
   Results of the move.
#>

    [CmdletBinding(SupportsShouldProcess=$true,
                  HelpUri = 'https://docs.microsoft.com/en-us/exchange/mail-flow/queues/relocate-queue-database?view=exchserver-2019',
                  ConfirmImpact='High')]
    Param
    ([Parameter(Mandatory=$true,
        HelpMessage="Enter the value of the path to use for new mail.que database location.")]
        $LocalPath)

    Begin
    {
    }
    Process
    {

#open file Notepad %ExchangeInstallPath%Bin\EdgeTransport.exe.config
#change values in config class
#Find and modify the following keys in the <appSettings> section.
$OriginalConfigPath= "%ExchangeInstallPath%Bin\TransportRoles\Data\Queue"
$NewConfigPath=$LocalPath

(Get-Content -path %ExchangeInstallPath%Bin\EdgeTransport.exe.config -Raw) -replace '<add key="QueueDatabasePath" value'*,'white'

if ($pscmdlet.ShouldProcess("Target", "Operation"))
        {
            $a = Get-Content -path c:\temp\EdgeTransport.exe.config -Raw
            ($a) -replace '<add key=','white'

            <add key="QueueDatabasePath" value="$LocalPath" />
            <add key="QueueDatabaseLoggingPath" value="$LocalPath" />
        }

    #Save and close .config file
    #restart Exchange transport service
    net stop MSExchangeTransport
    net start MSExchangeTransport

    }
    End
    {
        $MSExchangeTransport = Get-Service MSExchangeTransport
        if ($MSExchangeTransport) {
                Write-Host "MSExchangeTransport service is running." -ForegroundColor Green
            }
        Else {
                Write-Host "MSExchangeTransport service is stopped." -ForegroundColor Red
        }
        Test-Path $LocalPath
    }
}



$a = Get-Content C:\temp\EdgeTransport.exe.config | Where-Object { $_ -match '^<add key='} |

  ForEach-Object { $_ -replace 'add','subtrack' }


function Move-MailQueDatabase { #Invoke-Command -ComputerName SP2010-WFE -scriptBlock { C:\scripts\Test.PS1 }
<#
.SYNOPSIS
   Function to move the mail.que database.
.DESCRIPTION
   This is a function to assist Exchange administrators on moving the mail.que DB from it's default insall location to a desired disk location.
.EXAMPLE
   Example of how to use this cmdlet
.EXAMPLE
   Another example of how to use this cmdlet
.INPUTS
   path to move the database to.
.OUTPUTS
   Results of the move.
#>

    [CmdletBinding(SupportsShouldProcess=$true,
                  HelpUri = 'https://docs.microsoft.com/en-us/exchange/mail-flow/queues/relocate-queue-database?view=exchserver-2019',
                  ConfirmImpact='Medium')]
    Param
    ([Parameter(Mandatory=$true,
                   ValueFromPipeline=$true,
                   ValueFromPipelineByPropertyName=$true,
                   ValueFromRemainingArguments=$false,
                   Position=0)]

       $Param1)

    Begin
    {
    }
    Process
    {
        if ($pscmdlet.ShouldProcess("Target", "Operation"))
        {

        }
    }
    End
    {
    }
}

#endregion mail.que

#region promote DC new domain and to add into domain

#https://blogs.technet.microsoft.com/chadcox/2016/10/25/chads-quick-notes-installing-a-domain-controller-with-server-2016-core/

Rename-Computer $Computer

Get-NetAdapter
$ipaddress = "10.0.0.2"
$dnsaddress = "127.0.0.1"
New-NetIPAddress -InterfaceAlias Ethernet -IPAddress $ipaddress -AddressFamily IPv4 -PrefixLength 24
Set-DnsClientServerAddress -InterfaceAlias Ethernet -ServerAddresses $dnsaddress

Rename-Computer $Computer
Set-TimeZone -Id "Mountain Standard Time"

Restart-Computer

[ValidateSet("Windows2000Domain", "Windows2003InterimDomain", "Windows2003Domain", "Windows2008Domain", "Windows2008R2Domain", "Windows2012Domain", "Windows2012R2Domain", "Windows2016Domain", "Windows2019Domain","UnknownDomain")]
        $DomainMode

[ValidateSet("Windows2000Forest", "Windows2003InterimForest", "Windows2003Forest", "Windows2008Forest", "Windows2008R2Forest", "Windows2012Forest", "Windows2012R2Forest", "Windows2016Forest", "Windows2019Forest", "UnknownForest")]
        $ForestMode

Install-ADDSForest -DomainName contoso.com -SafeModeAdministratorPassword $password `
-NoRebootOnCompletion:$false -DomainMode $DomainMode -ForestMode $ForestMode

# para with confirm switch and reboot server

#confirm services
Get-Service adws,kdc,netlogon,dns

#confirm shared info
Get-smbshare






#endregion promote DC new domain and to add into domain


#region setup/install exchange 2019 server
# https://gallery.technet.microsoft.com/scriptcenter/Exchange-2019-Preview-b696abcc
#endregion


#region list inbox rules for all mailboxes set to forward, redirect, or forward



Function Get-InboxRulesSettingsForOrganization {
<#
.SYNOPSIS
   List inbox rules for all mailboxes which set to forward, forward as attachment, or redirect to external recipient(s)
.DESCRIPTION
   This function lists inbox rules for all mailboxes which set to forward, forward as attachment, or redirect to external recipient(s).
.EXAMPLE
   Example of how to use this cmdlet
 
.NOTES
   Original content provided by Niko Cheng
.ROLE
   The role of Exchange Server session is required, either local to an Exchange server or remote PowerShell session.
#>


[cmdletbinding()]
param()


$mailboxes = Get-Mailbox -ResultSize Unlimited

foreach ($mailbox in $mailboxes) {

    $forwardingRules = $null
    Write-Host "Checking rules for $($mailbox.displayname) - $($mailbox.primarysmtpaddress)" -foregroundColor Green
    $rules = get-inboxrule -Mailbox $mailbox.displayname
    Write-Verbose "`$rules array contains the values: $rules"

    $forwardingRules = $rules | Where-Object {$_.forwardto -or $_.forwardasattachmentto -or $_.RedirectTo}
    Write-Verbose "`$forwardingRules array contains the values $forwardingRules"

    foreach ($rule in $forwardingRules) {
        $recipients = @()
        $recipients = $rule.ForwardTo | Where-Object {$_ -match "SMTP"}
        $recipients += $rule.ForwardAsAttachmentTo | Where-Object {$_ -match "SMTP"}
        $recipients += $rule.RedirectTo | Where-Object {$_ -match "SMTP"}
        $externalRecipients = @()
        Write-Verbose "`$recipients array contains the values: $recipients"
        Write-Verbose "$externalRecipients array contains the values: $externalRecipients"

        foreach ($recipient in $recipients) {
            $email = ($recipient -split "SMTP:")[1].Trim("]")
            $domain = ($email -split "@")[1]
            Write-Verbose "`$email array contains the values: $email"
            Write-Verbose "`$domain array contains the values: $domain"

            if ($domains.DomainName -notcontains $domain) {
                $externalRecipients += $email
                Write-Verbose "$externalRecipients array contains the values: $externalRecipients"
            }
        }

        if ($externalRecipients) {
            $extRecString = $externalRecipients -join ", "
            Write-Host "$($rule.Name) forwards to $extRecString" -ForegroundColor Yellow

            $ruleHash = $null
            $ruleHash = [ordered]@{
                PrimarySmtpAddress = $mailbox.PrimarySmtpAddress
                DisplayName        = $mailbox.DisplayName
                RuleId             = $rule.Identity
                RuleName           = $rule.Name
                RuleDescription    = $rule.Description
                ExternalRecipients = $extRecString
            }
            $ruleObject = New-Object PSObject -Property $ruleHash
            $ruleObject | Export-Csv C:\testScript\externalrules.csv -NoTypeInformation -Append
        }
    }
}

}


<#
 May I request 2 more outputs?
 
1. How to add a column showing whether the rule is on or off?
 
2. The description column is very good but many of them are too long. Is it possible to replace it with the type of rule such as 'Forward to" or "Redirect to" or Forward as attachment"?
 
 
#>


#endregion


#region New-ExchangeServer builds Exchange servers in org


function New-ExchangeServer
<#
.SYNOPSIS
   Short description
.DESCRIPTION
   Long description
.EXAMPLE
   Example of how to use this cmdlet
.EXAMPLE
   Another example of how to use this cmdlet
.INPUTS
   Inputs to this cmdlet (if any)
.OUTPUTS
   Output from this cmdlet (if any)
.NOTES
   General notes
.COMPONENT
   The component this cmdlet belongs to
.ROLE
   The role this cmdlet belongs to
.FUNCTIONALITY
   The functionality that best describes this cmdlet
#>

{
    [CmdletBinding(DefaultParameterSetName='Parameter Set 1', 
                  SupportsShouldProcess=$true, 
                  HelpUri = 'http://mikeoneill.blog/MO_Module/New-ExchangeServer',
                  ConfirmImpact='High')]
    Param
    (
        # Calls Exchange version functions to install specific value
        [Parameter(Position=0,
                   ParameterSetName='Parameter Set 1')]
        [ValidateSet("2019", "2016", "2013")]
        $ExchangeServerVersion,

        # Param2 help description
        [Parameter(ParameterSetName='Parameter Set 1')]
        [AllowNull()]
        [AllowEmptyCollection()]
        [AllowEmptyString()]
        [ValidateScript({$true})]
        [ValidateRange(0,5)]
        [int]
        $Param2,

        # Param3 help description
        [Parameter(ParameterSetName='Another Parameter Set')]
        [ValidatePattern("[a-z]*")]
        [ValidateLength(0,15)]
        [String]
        $Param3
    )

    Begin
    {
    }
    Process
    {
        If ($ExchangeServerVersion -eq '2019') #Isntall logic for 2019 Exchange
        {
            New-2019ExchangeServer #Calls the 2019 install function
        }
        elseif ($ExchangeServerVersion -eq '2016') #Isntall logic for 2016 Exchange
        {   
            New-2016ExchangeServer #Calls the 2016 install function
        }
        elseif ($ExchangeServerVersion -eq '2013') #Isntall logic for 2019 Exchange
        {
            New-2013ExchangeServer #Calls the 2013 install function
        }
        else {Write-Host "Please select a valid Exchange Server version to install: 2019, 2016, or 2013." -ForegroundColor Yellow}


        
    }
    End
    {
    }
}


#region pre-requests for Exchange server 2019 install

Function New-2019ExchangeServer {}

#region Check for OS version
<# OS version reference
NT 10.0 version 1809 : 2019 RTM
NT 10.0 version 1803 : 2016 RTM
NT 6.3 : 2012 R2
NT 6.2 : 2012
NT 6.1 : 2008 R2
#>


$OSVersion = (Get-CimInstance Win32_OperatingSystem).version

    If ($OSVersion -notmatch '10.0.1809')
        {
            Write-Host ""            
            Write-Host "This function requires a version of Windows Server 2019, which this is not. Exiting..." -ForegroundColor Red
            Write-Host ""                
            Exit
        }
    elseif ($OSVersion -match '10.0.1809'){
            Write-Host ""            
            Write-Host "OS is 2019, proceeding with install steps..." -ForegroundColor Green
            Write-Host ""    }
#endregion Check for OS version

# https://gallery.technet.microsoft.com/scriptcenter/Exchange-2019-Preview-b696abcc


#endregion pre-requests for Exchange server 2019 install

#region pre-requests for Exchange server 2016 install

# https://gallery.technet.microsoft.com/office/Install-Exchange-2016-48983e13

#Check for OS version
#region Check for OS version
$OSVersion = (Get-CimInstance Win32_OperatingSystem).version

If ($OSVersion -notmatch '6.2'){
    If ($OSVersion -notmatch '6.3')    {
        If ($OSVersion -notmatch '10.0.1803')
        {
            Write-Host ""            
            Write-Host "This script requires a version of Windows Server: 2012, 2012R2, or 2016, which this is not. Exiting..." -ForegroundColor Red
            Write-Host ""                
            Exit
        }
    }
}
#endregion

#endregion pre-requests for Exchange server 2016 install

#region pre-requests for Exchange server 2013 install

# need code

#endregion pre-requests for Exchange server 2013 install


#endregion New-ExchangeServer builds Exchange servers in org

#region get-EAIO365photos
Get-EAIO365Photos -Identity 'mike@mcdeo.onmicrosoft.com' -Verbose
Get-EAIO365Photos -Identity 'bandit@mcdeo.onmicrosoft.com'
Get-EAIO365Photos -Identity 'mike@mcdeo.onmicrosoft.com', 'chloe@mcdeo.onmicrosoft.com', 'bandit@mcdeo.onmicrosoft.com' -Verbose
Get-EAIO365Photos -Unlimited -Verbose
Get-Userphoto -Identity 'mike@mcdeo.onmicrosoft.com'
Set-UserPhoto "mike@mcdeo.onmicrosoft.com" -PictureData ([System.IO.File]::ReadAllBytes("C:\Users\mconeill\Desktop\photo.jpg"))

#endregion get-EAIO365photos


<# Test UPN
get-LAPATH -UPN bandit@mcdeo.onmicrosoft.com
get-LAPATH -UPN bob@mcdeo.com
 
$userUPN="bandit@mcdeo.onmicrosoft.com"
$licensePlanList = Get-AzureADSubscribedSku
$userList = Get-AzureADUser -ObjectID $userUPN | Select -ExpandProperty AssignedLicenses | Select SkuID
$userList | ForEach { $sku=$_.SkuId ; $licensePlanList | ForEach { If ( $sku -eq $_.ObjectId.substring($_.ObjectId.length - 36, 36) ) { Write-Host $_.SkuPartNumber } } }
#>



#region MFA task for a function:

<#
start-process https://office365itpros.com/2018/12/10/reporting-the-managed-folder-assistant/
 
#>


#endregion MFA task for a function


#region remove msol direct assigned licenses
Function remove-MSOLBulkRemoveDirectAssignedLicense {
    
    [cmdletbinding()]
    param ( #license to be removed
            $skuId = "Contoso:STANDARDPACK",

            #add here the group with license assignment to be processed
            $LicensedGroup = "Licensed_Group"
    
    
    )
    
    
<#
From here:
start-process https://gist.github.com/mrik23/2ed37ce0c7c4a79605bdcf052e29b391
 
  Modified version of the script from Microsoft Documentation.
  Removed the part that checks if the users is assigned more products than the group assigned license.
  Added connection part and help to find Sku and Group Object ID.
  This script requires Azure AD (aks MSOL) PowerShell v1. It doesn't seem possible to do so with v2.
  Ref: https://docs.microsoft.com/en-us/azure/active-directory/active-directory-licensing-ps-examples
#>


Import-Module MSOnline
$UserCredential = Get-Credential
Connect-MsolService -Credential $UserCredential

#Get License Sku for the tenant
Get-MsolAccountSku

#Get the group Object ID
$groupId = (Get-MsolGroup -SearchString $LicensedGroup).ObjectId

#Helper functions used by the script

#Returns TRUE if the user has the license assigned directly
function UserHasLicenseAssignedDirectly
{
    Param([Microsoft.Online.Administration.User]$user, [string]$skuId)

    $license = GetUserLicense $user $skuId

    if ($null -ne $license)
    {
        #GroupsAssigningLicense contains a collection of IDs of objects assigning the license
        #This could be a group object or a user object (contrary to what the name suggests)
        #If the collection is empty, this means the license is assigned directly - this is the case for users who have never been licensed via groups in the past
        if ($license.GroupsAssigningLicense.Count -eq 0)
        {
            return $true
        }

        #If the collection contains the ID of the user object, this means the license is assigned directly
        #Note: the license may also be assigned through one or more groups in addition to being assigned directly
        foreach ($assignmentSource in $license.GroupsAssigningLicense)
        {
            if ($assignmentSource -ieq $user.ObjectId) 
            {
                return $true
            }
        }
        return $false
    }
    return $false
}

#Returns TRUE if the user is inheriting the license from a specific group
function UserHasLicenseAssignedFromThisGroup
{
    Param([Microsoft.Online.Administration.User]$user, [string]$skuId, [Guid]$groupId)

    $license = GetUserLicense $user $skuId

    if ($null -ne $license)
    {
        #GroupsAssigningLicense contains a collection of IDs of objects assigning the license
        #This could be a group object or a user object (contrary to what the name suggests)
        foreach ($assignmentSource in $license.GroupsAssigningLicense)
        {
            #If the collection contains at least one ID not matching the user ID this means that the license is inherited from a group.
            #Note: the license may also be assigned directly in addition to being inherited
            if ($assignmentSource -ieq $groupId) 
            {
                return $true
            }
        }
        return $false
    }
    return $false
}

#Returns the license object corresponding to the skuId. Returns NULL if not found
function GetUserLicense
{
    Param([Microsoft.Online.Administration.User]$user, [string]$skuId, [Guid]$groupId)
    #we look for the specific license SKU in all licenses assigned to the user
    foreach($license in $user.Licenses)
    {
        if ($license.AccountSkuId -ieq $skuId)
        {
            return $license
        }
    }
    return $null
}

#process staging removal for only 20 members in the group first
Get-MsolGroupMember -MaxResults 20 -GroupObjectId $groupId | 
    #get full info about each user in the group
    Get-MsolUser -ObjectId {$_.ObjectId} | 
    ForEach-Object { 
        $user = $_;
        $operationResult = "";

        #check if Direct license exists on the user
        if (UserHasLicenseAssignedDirectly $user $skuId)
        {
            #check if the license is assigned from this group, as expected
            if (UserHasLicenseAssignedFromThisGroup $user $skuId $groupId)
            {
                    #remove the direct license from user
                    Set-MsolUserLicense -ObjectId $user.ObjectId -RemoveLicenses $skuId
                    $operationResult = "Removed direct license from user."   
            }
            else
            {
                $operationResult = "User does not inherit this license from this group. License removal was skipped."
            }
        }
        else
        {
            $operationResult = "User has no direct license to remove. Skipping."
        }

        #format output
        New-Object Object | 
                    Add-Member -NotePropertyName UserId -NotePropertyValue $user.ObjectId -PassThru |
                    Add-Member -NotePropertyName OperationResult -NotePropertyValue $operationResult -PassThru 
    } | Format-Table


<#You can then process all members in the group if the result of staging is OK
Get-MsolGroupMember -All -GroupObjectId $groupId |
    #get full info about each user in the group
    Get-MsolUser -ObjectId {$_.ObjectId} |
    Foreach {
        $user = $_;
        $operationResult = "";
 
        #check if Direct license exists on the user
        if (UserHasLicenseAssignedDirectly $user $skuId)
        {
            #check if the license is assigned from this group, as expected
            if (UserHasLicenseAssignedFromThisGroup $user $skuId $groupId)
            {
                #remove the direct license from user
                Set-MsolUserLicense -ObjectId $user.ObjectId -RemoveLicenses $skuId
                $operationResult = "Removed direct license from user."
 
            }
            else
            {
                $operationResult = "User does not inherit this license from this group. License removal was skipped."
            }
        }
        else
        {
            $operationResult = "User has no direct license to remove. Skipping."
        }
 
        #format output
        New-Object Object |
                    Add-Member -NotePropertyName UserId -NotePropertyValue $user.ObjectId -PassThru |
                    Add-Member -NotePropertyName OperationResult -NotePropertyValue $operationResult -PassThru
    } | Format-Table
    #>

  
}
#endregion remove msol direct assigned licenses

#region get list of authentication policies assigned to users

#references:
start-process https://docs.microsoft.com/en-us/powershell/module/exchange/get-user?view=exchange-ps
start-process https://docs.microsoft.com/en-us/powershell/module/exchange/get-authenticationpolicy?view=exchange-ps

function Get-EAISpecificAuthenticationPoilicy{
<#
    .Synopsis
        Short description
    .DESCRIPTION
        Long description
    .EXAMPLE
        Example of how to use this cmdlet
    .EXAMPLE
        Another example of how to use this cmdlet
    .INPUTS
        Inputs to this cmdlet (if any)
    .OUTPUTS
        Output from this cmdlet (if any)
    .NOTES
       General notes
    .COMPONENT
        The component this cmdlet belongs to
#>

    [CmdletBinding(HelpUri = 'http://www.microsoft.com/')]

    Param (
            # Param1 help description

            $Param1,    
    
            # Param3 help description
            [String]$Param3
        )
    
        Begin
        {
        }
        Process
        {
            
        }
        End
        {
        }
    }

Get-User | Where-Object {$_.authentication -ne $null}

Get-AuthenticationPolicy | gm

New-AuthenticationPolicy -Name "Research and Development Group"

set-user -AuthenticationPolicy "Research and Development Group" -Identity sally2@mcdeo.onmicrosoft.com

#endregion get list of authentication policies assigned to users



New-Mailbox -Room Workspace1 | Set-Mailbox -Type Workspace
get-place -Identity workspace1 | fl city

Set-Place -Identity workspace1 -Building DownTown -Floor 3 -City Denver

$A = $null


Get-Service

Get-SecretVault
Get-secret -Name -TenantId

$creds = Get-Credential -Username contoso\administrator
$creds.password


Connect-ExchangeOnline -Certificate -AppId
Connect-MgGraph -TenantId (Get-secret -vault MyVault -Name TenantID) `
-ClientId '4261b98b-c5d0-4a08-8906-4ef0dde54875' `
-CertificateThumbprint '3AAB56598C75EA06B99C2576577119B60BD29AF2'
new-mgteam -DisplayName $displayname -Group 
$null = $variable

get-mggroup -GroupId '20892504-a38e-490d-8879-cd28e5042cd4'
    get-help Get-MgUserLoggedOnManagedDevice -full
    
    Get-Service | Where-Object {$_.status -eq 'running'}
    get-service | Get-Member
    
Get-Service | Where-Object {$_.name -eq 'svchost'}

#region

#endregion

#region graph SDK connect to source tenant
$SourceCertThumbprint = "5F744EB9F72BEB00ABB7346A5BB1FE39E6894C29"
$SourceTenantID = "77a7ebb7-ae63-4259-9307-d2683e631623"
$SourceAppID = "dcb0dcea-e8d2-4561-94c2-5a18be45eaca"

Connect-MgGraph -ClientId $SourceAppID `
    -TenantId $SourceTenantID `
    -CertificateThumbprint $SourceCertThumbprint
#endregion graph SDK connect to source tenant
$users = get-mguser
$users.id
get-mguser
$users | gm



$a = Import-Csv C:\temp2\Users.csv
Get-EAICalendarEventsOfRecurrenceItems -UserUPN $a.userupn -DaysBackwards 180 -DaysForwards 180 -OutputPath c:\temp2\ -verbose
Get-EAICalendarEventsOfRecurrenceItems -UserUPN 'user1@contoso.com','user2@contoso.com' -DaysBackwards 180 -DaysForwards 180 -OutputPath c:\temp2\ -verbose


    Connect-MgGraph -CertificateThumbprint (Get-AccessToken -ClientSecret ) -ClientId '4261b98b-c5d0-4a08-8906-4ef0dde54875' -TenantId '77a7ebb7-ae63-4259-9307-d2683e631623'


#region Azure App registration health check

#region graph SDK connect to source tenant
$SourceCertThumbprint = "5F744EB9F72BEB00ABB7346A5BB1FE39E6894C29"
$SourceTenantID = "77a7ebb7-ae63-4259-9307-d2683e631623"
$SourceAppID = "a2ba9a39-a4c8-4037-b808-7552320c80c9"

Connect-ExchangeOnline

Connect-MgGraph -ClientId $SourceAppID -TenantId $SourceTenantID -CertificateThumbprint $SourceCertThumbprint
#endregion graph SDK connect to source tenant
#reference: https://morgantechspace.com/2018/04/get-list-of-registered-azure-ad-apps-powershell.html
Connect-AzureAD
Get-AzureADApplication -All:$true

Get-AzureADServicePrincipal -All:$true | ? {$_.Tags -eq "WindowsAzureActiveDirectoryIntegratedApp"}

Get-AzureADApplication -All:$true |
Select-Object DisplayName, AppID, PublicClient, AvailableToOtherTenants, HomePage, LogoutUrl  |
Export-Csv "C:\AzureADApps.csv"  -NoTypeInformation -Encoding UTF8

Get-AzureADApplicationOwner

Get-AzADApplication

get-command -verb get -noun *mg*app*

Get-MgReportAzureAdApplicationSign

Get-MgApplicationById
Get-MgApplicationAvailableExtensionProperty
Get-MgApplication

Get-MgUserManagedAppRegistrationUserId
Get-MgUserManagedAppRegistrationByRef
Get-MgUserManagedAppRegistration
Get-MgUserManagedAppDiagnosticStatuses
Get-MgPrivilegedApprovalRoleInfo
Get-MgPrivilegedApprovalRequestByRef
Get-MgDeviceAppMgtVppTokenLicense
Get-MgDeviceAppMgtVppToken


#endregion Azure App registration health check


#region get MFA run against mailbox(es)


Function Get-EaiMFAData {
<#
.SYNOPSIS
    Short description
.DESCRIPTION
    Long description
.EXAMPLE
    Example of how to use this cmdlet
.EXAMPLE
    Another example of how to use this cmdlet
.INPUTS
    Inputs to this cmdlet (if any)
.OUTPUTS
    Output from this cmdlet (if any)
.NOTES
    start-process 'https://office365itpros.com/2018/12/10/reporting-the-managed-folder-assistant/'
    General notes
.COMPONENT
    The component this cmdlet belongs to
.ROLE
    The role this cmdlet belongs to
.FUNCTIONALITY
    The functionality that best describes this cmdlet
#>

    [CmdletBinding(HelpUri = 'http://www.microsoft.com/')]
param ()

$Mbx = Get-Mailbox -RecipientTypeDetails UserMailbox -ResultSize Unlimited
$Report = @()
ForEach ($M in $Mbx) {
    $LastProcessed = $Null
    Write-Host "Processing" $M.DisplayName
    $Log = Export-MailboxDiagnosticLogs -Identity $M.Alias -ExtendedProperties
    $xml = [xml]($Log.MailboxLog)  
    $LastProcessed = ($xml.Properties.MailboxTable.Property | ? {$_.Name -like "*ELCLastSuccessTimestamp*"}).Value   
    $ItemsDeleted  = $xml.Properties.MailboxTable.Property | ? {$_.Name -like "*ElcLastRunDeletedFromRootItemCount*"}
    If ($null -eq $LastProcessed) {
        $LastProcessed = "Not processed"}

    $ReportLine = [PSCustomObject]@{
        User          = $M.DisplayName
        LastProcessed = $LastProcessed
        ItemsDeleted  = $ItemsDeleted.Value}      
    $Report += $ReportLine
    }
$Report | Select-Object User, LastProcessed, ItemsDeleted
}


#If mailbox is under 10mb, MFA will not process
Get-MailboxStatistics -Identity "imran khan" | Select-Object TotalItemSize
#endregion get MFA run against mailbox(es)


get-command -Verb get -noun *owner*

#region Teams private owners

Invoke-MgGraphRequest 

#region graph SDK connect to source tenant
$SourceCertThumbprint = "6EF36C20F5AE5ECF962288405F82CC09B4FA905D"
$SourceTenantID = "77a7ebb7-ae63-4259-9307-d2683e631623"
$SourceAppID = "dcb0dcea-e8d2-4561-94c2-5a18be45eaca"

Connect-MgGraph -ClientId $SourceAppID `
    -TenantId $SourceTenantID `
    -CertificateThumbprint $SourceCertThumbprint
#endregion graph SDK connect to source tenant

Invoke-MgGraphRequest -Uri 'https://graph.microsoft.com/v1.0/user/2b009992-e963-4b7c-90e4-547e8c0d34c3' -Method get
Get-MgUser -UserId '2b009992-e963-4b7c-90e4-547e8c0d34c3' | fl *
$users = get-mguser
$users | where-object {}
$UserUPN = 'mike@fieldwestern.onmicrosoft.com','diane@fieldwestern.onmicrosoft.com','MeganB@fieldwestern.onmicrosoft.com'
Get-EaiCalendarEventsOfRecurrenceItems7 -UserUPN 'mike@fieldwestern.onmicrosoft.com','diane@fieldwestern.onmicrosoft.com' -Verbose -OutputPath c:\temp\files\ -DaysForwards 180 -DaysBackwards 360
(Get-mguser).id
$UserID = (Get-MgUser -userid '2b009992-e963-4b7c-90e4-547e8c0d34c3').Id
    $UserMail = (Get-MgUser -UserId $UserUPN).Mail

(Measure-Command {
$UserUPN | ForEach-Object -parallel {

    [string]$StartTime = (get-date).AddDays(-180)
    $FormatedStartTime = $StartTime.Split( )[0]
    [string]$EndTime = (get-date).AddDays(180)
    $FormatedEndtime = $EndTime.Split( )[0]
    $FormatedStartTime
    $FormatedEndtime

    $UserID = (Get-MgUser -userid $_).Id
    $UserMail = (Get-MgUser -UserId $_).Mail
    $calEvents = Get-MgUserCalendarView -UserId $userId -StartDateTime $startTime -EndDateTime $endTime -All
    $UserID
    $userMail
    [array]$ResultList.$_ = @()
    $eventid = (($calEvents[0]).id)

    $calEvents = Invoke-MgGraphRequest -Method get `
        -Uri "https://graph.microsoft.com/v1.0/users/$userID/events?filter=isorganizer eq true"
    #$calEvents = Get-MgUserCalendarView -UserId $userId -StartDateTime $startTime -EndDateTime $endTime -All
    $calEvents.value[0].isorganizer
    $calEvents.value[0].start.datetime
    $calEvents.value[0].recurrence.range
    $calEvents.value[0].recurrence.range.startDate
    $calEvents.value[0].recurrence.range.enddate
    $calEvents.value[0].id
    $b= $calEvents.value.attendees.emailaddress.Address 
    foreach ($calevent in $calEvents){
        $calEvent.value.attendees.emailaddress | select-object -ExpandProperty address
    }
    $calEvent.value.recurrence.range

    foreach ($a in $b){$a}
    
    $eventid = (($calEvents.value[0].AdditionalProperties.occurrenceId).split("."))[1]
    
        $calevent = $calEvent + $resultlist.$_
        $resultlist.$_
        $eventDetail = Invoke-MgGraphRequest -Method get -Uri "https://graph.microsoft.com/v1.0/$UserID/events?`$select=id,attendess&?filter=isorganizer eq true"
        $eventDetail = Get-MgUserEvent -UserId $UserID -EventId $eventId
        $eventDetail.AdditionalProperties.occurrenceId

}

}
).Seconds
   
$calevent | gm

get-Command -noun mg*user | Measure-Object

Invoke-MgGraphRequest  -Method get -Uri 'https://graph.microsoft.com/v1.0/users/2b009992-e963-4b7c-90e4-547e8c0d34c3/messages' 

Invoke-MgGraphRequest -Method get -Uri "https://graph.microsoft.com/v1.0/users?$filter=usertype eq 'guest'"
$users = Invoke-MgGraphRequest -Method get -Uri https://graph.microsoft.com/v1.0/users
$users.value

Find-MgGraphCommand -Uri 'https://graph.microsoft.com/v1.0/users/{id}/messages'

https://graph.microsoft.com/v1.0/users/2b009992-e963-4b7c-90e4-547e8c0d34c3/messages

Get-MgUserCalendarView -UserId 'mike@fieldwestern.onmicrosoft.com' -StartDateTime (get-date).AddDays(-30) -EndDateTime (get-date).AddDays(30) -All
Get-EaiCalendarEventsOfRecurrenceItems -UserUPN 'mike@fieldwestern.onmicrosoft.com' -DaysBackwards 180 -DaysForwards 180 -OutputPath c:\temp\ -verbose

#region Get-EaiCalendarEventsOfRecurrenceItems
Function Get-EaiCalendarEventsOfAllItems {
    <#
    .SYNOPSIS
    Get all calendar events for a specific user.
    .DESCRIPTION
    While being logged into MS Graph API, this function will list out all meetings of specific user or users into an Excel file.
    Only the attendees listed for events that owned by the requested user will be listed.
    Logic written to exclude non-owner attendee events.
    This function also will list out non-specfic users of events.
    .EXAMPLE
    Get-EaiCalendarEventsOfAllItems -UserUPN 'mike@contoso.com' -DaysBackwards 180 -DaysForwards 180 -OutputPath c:\temp2\ -verbose
  
    This will search the mailbox of Mike@contoso.com for a period of 180 days before and after the day this is run and output the file to the c:\temp2\ folder with verbose information displayed.
    .EXAMPLE
    Get-EaiCalendarEventsOfAllItems -UserUPN 'Diane@forthcoffee.com' -DaysBackwards 30 -DaysForwards 0 -OutputPath c:\temp\
  
    This will search the mailbox of Diane@forthcoffee.com for a period of 30 days before until the day this is run and output the file to the c:\temp\ folder.
    .EXAMPLE
    Get-EaiCalendarEventsOfAllItems -UserUPN 'Sam@TailSpinToys.com','Chloe@TailSpinToys.com' -DaysBackwards 30 -DaysForwards 0 -OutputPath c:\temp\
  
    This will search the mailboxes of Sam@TailSpinToys.com and Chloe@TailSpinToys.com for a period of 30 days before until the day this is run and output the file to the c:\temp\ folder.
    .INPUTS
    UserUPN to scan, date range for the query, and users to not list out in results file.
    .OUTPUTS
    CSV file in defined value.
    .ROLE
    Azure AD and Exchange online calendaring
    Application access needed:
    - User.Read.All
    - Calendars.Read
    .FUNCTIONALITY
    This function can list all attendees from a reoccuring meeting and using the domain parameter, can exlude all current domain attendees and only list non-domain ones.
    .NOTES
    Filter parameter value
    start-process https://docs.microsoft.com/en-us/graph/query-parameters#filter-parameter
     
    recurrance values
    start-process https://docs.microsoft.com/en-us/graph/api/resources/recurrencerange?view=graph-rest-1.0
     
    Recurrence patterns
    https://docs.microsoft.com/en-us/graph/api/resources/recurrencepattern?view=graph-rest-1.0
    #>

    [cmdletbinding(HelpUri = 'https://mikeoneill.blog/exchange_addin/get-eaicalendareventsofrecurrenceitems/')]
    Param(
    [parameter(Mandatory=$false)][array]$UserUPN = 'mike@fieldwestern.onmicrosoft.com',
    [parameter(Mandatory=$false)]$OutputPath = 'c:\temp',
    $ExcludedUsers = ('diane@fieldwestern.onmicrosoft.com','meganb@fieldwestern.onmicrosoft.com'),
    $DaysForwards = 180,
    $DaysBackwards = 180 #add output date of event and need non-recurrence
    )
    
#collection class defined
Class Result {
    [String]$Subject
    [String]$Attendee
    [datetime]$DateOfMeeting
    #[String]$RecurrencePattern
    #[String]$RecurrenceRange
}

#loop through multiple users
foreach ($UserName in $UserUPN){

    $username = $userUPN
    $startTime = (get-date).AddDays(-$DaysBackwards)
    $endTime = (get-date).AddDays($DaysForwards)
    $UserID = (Get-MgUser -userid $UserName).Id
    $UserMail = (Get-MgUser -UserId $UserName).Mail
    
    [array]$ResultList = @()
    
    #region get content
    #first call needed should be get-mgusercalendarevents
    $calEvents = Get-MgUserCalendarView -UserId $userId -StartDateTime $startTime -EndDateTime $endTime -All
    Write-Verbose "`$calEvents is $calEvents"
    #loop through all returned events
    foreach ($calEvent in $calEvents) {
        <#
        $calEvents.id
        $calEvent = $calEvents
        #>

        Write-Verbose "`$calEvent is $calEvent"
        $eventID = $calEvent.id
        Write-Verbose "`$eventID is $eventID"
        $eventDetails = Get-MgUserEvent -UserId $UserID -EventId $eventId
        Write-Verbose "`$eventDetails is $eventDetails"
        $Attendees = $eventDetails.Attendees.EmailAddress
        if ($null -ne $Attendees){
            $Compare = Compare-Object -ReferenceObject $Attendees -DifferenceObject $ExcludedUsers
                if (($Compare.sideindicator -eq '<=') -or ($null -ne $compare)){
                    foreach ($Attendee in $Attendees){
                        [datetime]$a = $eventDetails.start.DateTime
                        $Results = New-Object -TypeName Result -Property @{
                            Attendee = $Attendee.address
                            Subject = $eventDetails.Subject
                            DateOfMeeting = $a 
                        }
                        $ResultList = $ResultList + $Results
                    }
                }
        }
        }
        }
    }
    $ResultList

        #record the data to arraylist
        foreach ($eventDetail in $eventDetails){
            Write-Verbose "`$eventDetail is $eventDetail"
            $EmailAddresses = $eventDetail.Attendees.EmailAddress
            $EmailWithoutUserMailIncluded = $EmailAddresses | Where-Object {$_.address -notlike $userMail}
            if ($EmailWithoutUserMailIncluded.length -eq $EmailAddresses.length){
            $EmailAddresses = $eventDetail.Attendees.EmailAddress
            Write-Verbose "`$EmailAddresses is $EmailAddresses"
            foreach ($EmailAddress in $EmailWithoutUserMailIncluded) {
                Write-Verbose "`$EmailAddress is $EmailAddress"
                $Results = New-Object -TypeName Result -Property @{
                    Subject = $eventDetail.Subject
                    Attendee = $EmailAddress.address
                    #RecurrencePattern = $eventDetail.Recurrence.Pattern
                    #RecurrenceRange = $eventDetail.Recurrence.Range
                }
                $ResultList = $ResultList + $Results
                Write-Verbose "`$Results is $Results"
                Write-Verbose "`$ResultList is $ResultList"
                }
            }
        }
    #endregion getting content

    #region Compile CSV file
    if ($null -ne $OutputPath){
    Write-Verbose "`$OutputPath is $OutputPath"
    $OutputFile = $OutputPath + "ExportCalendarEvents_$UserName.csv"
    Write-Verbose "`$OutputFile is $OutputFile"
    #Takes the entire ControlList variable, ensures the values are unique.
    $FinalFile = $ResultList | Sort-Object Subject | select-object Subject, Attendee, DateOfMeeting -Unique

    $FinalFile | Sort-Object Subject | `
        Select-Object Subject, Attendee, DateOfMeeting | `
        Export-CSV -NoTypeInformation $OutputFile
        Write-Verbose "`$FinalFile is $FinalFinal"
    }
    #endregion Compile CSV file
    } #end of foreach username loop
    } 
 }#end of function
 
 Get-EaiCalendarEventsOfAllItems -UserUPN 'mike@fieldwestern.onmicrosoft.com' -DaysBackwards 180 -DaysForwards 180 -OutputPath c:\temp\ -verbose
 
 #endregion Get-EaiCalendarEventsOfRecurrenceItems

$usersNeeded = Get-Content \\someshere\someshare\
([DateTime]::Today.AddDays(-1))
[DateTime]::Today
get-date

get-command -Verb get -noun *audit*
Get-MgUserPhoto -UserId mike@fieldwestern.onmicrosoft.com

Find-MgGraphCommand -uri "https://graph.microsoft.com/v1.0/users?$select=givenname,surname"

Find-MgGraphCommand -uri "https://graph.microsoft.com/v1.0/users?$filter=userType eq 'Guest'"
$a = get-mggroup
$b = $a[2].Id

$u = Import-Csv c:\temp\users.csv
$u
Get-EaiCalendarEventsOfRecurrenceItems -UserUPN 'mike@fieldwestern.onmicrosoft.com','diane@fieldwestern.onmicrosoft.com','AdeleV@fieldwestern.onmicrosoft.com' -Verbose -OutputPath 'c:\temp2\'
Get-EaiCalendarEventsOfRecurrenceItems -UserUPN $u.user -Verbose -OutputPath 'c:\temp\' -DaysForwards 180 -DaysBackwards 90


Get-MgTeam -TeamId $b | Format-List channels

#channel ID, then get members and then expand roles

Invoke-MgGraphRequest -uri "https://graph.microsoft.com/v1.0/me/events" -Method get

"https://graph.microsoft.com/v1.0/teams/Guid/channels/quid/members"
#pipe to private channel via where-object

Get-MgTeamChannelMember -TeamId $b


get-mgteam -TeamId $b | fl *

(get-mgteam -TeamId $b).channels
$a = Get-MgUserCalendarEvent -UserId 'mike@fieldwestern.onmicrosoft.com' -CalendarId "$((Get-MgUserCalendar -UserId 'mike@fieldwestern.onmicrosoft.com').id)"
foreach ($b in $a) {
        $b.Attendees.EmailAddress
        $b.Subject
        
    }
    
    
    $a.Attendees | gm
    Get-MgUserCalendar -UserId 'mike@fieldwestern.onmicrosoft.com'
    
    get-command -Verb get -Noun mg*mail*
    #endregion Teams private owners
    
    
    
$uri = "https://graph.microsoft.com/v1.0/users?$filter=userType eq 'Guest'"
$uri


#get
$result = Invoke-DscResource -Name File -Method Get -Property @{
    DestinationPath="$env:SystemDrive\\temp\\stuff.txt";
    Contents='This file is create by Invoke-DscResource'} -Verbose
$result.ItemValue | fl

#set
$result = Invoke-DscResource -Name File -Method Set -Property @{
    DestinationPath = "$env:SystemDrive\\temp\\stuff.txt";
    Contents = 'This file is create by Invoke-DscResource'} -Verbose
$result | fl

#test
$result = Invoke-DscResource -Name File -Method Test -ModuleName PSDesiredStateConfiguration -Property @{
    DestinationPath="$env:SystemDrive\\temp\\stuff.txt";
    Contents='This file is create by Invoke-DscResource'} -Verbose
$result | fl


get-service | Format-List




$a = 'mike','diane','chloe','bandit','muffin'
$a |ForEach-Object -parallel {
    Write-Host $_
    Out-File c:\temp\files\$_.txt
    
} -ThrottleLimit 5


$uri = "https://graph.microsoft.com/v1.0/users/$user/messages?`$search=`"$searchString`""
$uri = "https://graph.microsoft.com/v1.0/users/2b009992-e963-4b7c-90e4-547e8c0d34c3/messages?`$search=`"$searchString`""
$a = Invoke-MgGraphRequest -Method get -Uri "https://graph.microsoft.com/v1.0/users/2b009992-e963-4b7c-90e4-547e8c0d34c3/messages"
$a.value.id
internetMessageHeaders
$messageId = $a.value[0].id

$a.value.internetMessageHeaders
$uri = "https://graph.microsoft.com/v1.0/users/2b009992-e963-4b7c-90e4-547e8c0d34c3/messages/$messageId/?`$select=internetMessageHeaders"
$b = Invoke-MgGraphRequest -Method get -Uri $uri
$b.internetMessageHeaders

$uri = "https://graph.microsoft.com/v1.0/users/2b009992-e963-4b7c-90e4-547e8c0d34c3/messages/$messageId/?`$select=internetMessageHeaders"