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 = "9CB02FA5A9AA03B5E3E0B3054CF13956711BF5F2" $SourceTenantID = "77a7ebb7-ae63-4259-9307-d2683e631623" $SourceAppID = "b91c2be9-45f1-4157-83d7-c456c7793372" Connect-MgGraph -ClientId $SourceAppID -TenantId $SourceTenantID ` -CertificateThumbprint $SourceCertThumbprint #endregion graph SDK connect to source tenant (get-mguser).UserPrincipalName get-mguser | gm Find-MgGraphCommand -uri 'https://graph.microsoft.com/v1.0/users' -Method update $a = get-mguser $a | gm $b = Invoke-MgGraphRequest -Uri 'https://graph.microsoft.com/v1.0/users' -Method get $b.value.id 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" |