Added Files

This commit is contained in:
DistractADD
2021-07-06 13:16:46 +10:00
parent f2475a3bdd
commit cfddd4fad2
393 changed files with 65842 additions and 0 deletions

View File

@@ -0,0 +1,167 @@
<#
.SYNOPSIS
Cleanup Microsoft Teams Client
.DESCRIPTION
Cleanup Microsoft Teams Client by deleting several local cache files
.EXAMPLE
PS C:\> .\Clear-MicrosoftTeamsClientCache.ps1
Cleanup Microsoft Teams Client by deleting several local cache files
.EXAMPLE
PS C:\> .\Clear-MicrosoftTeamsClientCache.ps1 -Verbose
Cleanup Microsoft Teams Client by deleting several local cache files, but be verbose while doing it
.EXAMPLE
PS C:\> .\Clear-MicrosoftTeamsClientCache.ps1 -WhatIf
Cleanup Microsoft Teams Client by deleting several local cache files - Dry Run!!!
.NOTES
Due to some issues, Windows is not supported at this time!
#>
[CmdletBinding(ConfirmImpact = 'Medium',
SupportsShouldProcess)]
param ()
if ($IsMacOS -eq $true)
{
$AppDataBasePath = '~/Library/Application Support/Microsoft/Teams/'
}
else
{
Write-Warning -Message 'Due to some issues, Windows is not supported at this time!'
exit 1
$AppDataBasePath = ($env:APPDATA + '\Microsoft\teams\')
}
#region BoundParameters
if (($PSCmdlet.MyInvocation.BoundParameters['Verbose']).IsPresent)
{
$VerboseValue = $true
}
else
{
$VerboseValue = $false
}
if (($PSCmdlet.MyInvocation.BoundParameters['Debug']).IsPresent)
{
$DebugValue = $true
}
else
{
$DebugValue = $false
}
if (($PSCmdlet.MyInvocation.BoundParameters['WhatIf']).IsPresent)
{
$WhatIfValue = $true
}
else
{
$WhatIfValue = $false
}
#endregion BoundParameters
#region
$paramGetChildItem = @{
Verbose = $VerboseValue
Debug = $DebugValue
Recurse = $true
ErrorAction = 'SilentlyContinue'
}
$paramRemoveItem = @{
Verbose = $VerboseValue
Debug = $DebugValue
WhatIf = $WhatIfValue
Confirm = $false
Force = $true
Recurse = $true
ErrorAction = 'SilentlyContinue'
}
#endregion
#region
if ($PSCmdlet.ShouldProcess('Microsoft Teams Client', 'Hard Kill'))
{
$null = (Get-Process -Name Teams -ErrorAction SilentlyContinue | Stop-Process -Force -ErrorAction SilentlyContinue)
Start-Sleep -Seconds 2
}
#endregion
Get-ChildItem -Path ($AppDataBasePath + 'blob_storage') @paramGetChildItem -Verbose -Debug | ForEach-Object -Process {
Remove-Item -Path $_.FullName @paramRemoveItem -WhatIf
}
Get-ChildItem -Path ($AppDataBasePath + 'databases') @paramGetChildItem | ForEach-Object -Process {
Remove-Item -Path $_.FullName @paramRemoveItem
}
Get-ChildItem -Path ($AppDataBasePath + 'Cache') @paramGetChildItem | ForEach-Object -Process {
Remove-Item -Path $_.FullName @paramRemoveItem
}
Get-ChildItem -Path ($AppDataBasePath + 'gpucache') @paramGetChildItem | ForEach-Object -Process {
Remove-Item -Path $_.FullName @paramRemoveItem
}
Get-ChildItem -Path ($AppDataBasePath + 'IndexedDB') @paramGetChildItem | ForEach-Object -Process {
Remove-Item -Path $_.FullName -Confirm:$false -Force -Recurse -ErrorAction SilentlyContinue
}
Get-ChildItem -Path ($AppDataBasePath + 'Local Storage') @paramGetChildItem | ForEach-Object -Process {
Remove-Item -Path $_.FullName @paramRemoveItem
}
Get-ChildItem -Path ($AppDataBasePath + 'tmp') @paramGetChildItem | ForEach-Object -Process {
Remove-Item -Path $_.FullName @paramRemoveItem
}
Get-ChildItem -Path $AppDataBasePath -Include 'old_logs_*.txt', 'logs.txt', 'in_progress_download_metadata_store' @paramGetChildItem | ForEach-Object -Process {
Remove-Item -Path $_.FullName @paramRemoveItem
}
if (Test-Path -Path ($AppDataBasePath + 'installTime.txt'))
{
$InstallDateInput = (Get-Content -Path ($AppDataBasePath + 'installTime.txt'))
$Culture = (New-Object -TypeName System.Globalization.CultureInfo -ArgumentList ('de-DE'))
$InstallDate = (Get-Date -Date $InstallDateInput -Format ($Culture.DateTimeFormat.ShortDatePattern))
Write-Output -InputObject ('Latest Version of Microsoft Teams from: {0}' -f $InstallDate)
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,535 @@
<#
.SYNOPSIS
Microsoft Teams Client customization settings via PowerShell
.DESCRIPTION
Microsoft Teams Client customization settings via PowerShell
.EXAMPLE
PS C:\> .\Default_MicrosoftTeams_DesktopConfig.ps1
.EXAMPLE
PS C:\> .\Default_MicrosoftTeams_DesktopConfig.ps1 -verbose
.NOTES
Refactored and extended version of Desktop-Config-Json.ps1 by eshlomo1
.LINK
https://github.com/eshlomo1/MS_Teams/blob/master/Desktop-Config-Json.ps1
.LINK
https://www.eshlomo.us/microsoft-teams-client-personalization-with-powershell/
#>
[CmdletBinding(ConfirmImpact = 'None')]
param ()
begin
{
#region DefaultSettings
$AppPrefSetOpenAsHidden = $false
$AppPrefSetOpenAtLogin = $false
$AppPrefSetRegisterAsIMProvider = $true
$AppPrefSetRunningOnClose = $false
$NotificationWindowOnClose = $true
$OverrideOpenAsHiddenProperty = $true
$IsAppFirstRun = $false
$CurrentWebLanguage = 'en-us'
#endregion DefaultSettings
#region Cleanup
$ChangedConfig = $null
$SourceConfigFile = $null
$Teams = $null
#endregion Cleanup
#region SetConfigPath
if (($PSVersionTable.PSEdition -eq 'Core') + ($PSVersionTable.Platform -eq 'Unix') -and ($PSVersionTable.OS -like 'Darwin*'))
{
# OK, macOS is supported
$SourceConfigFile = ($Env:HOME + '/Library/Application Support/Microsoft/Teams/desktop-config.json')
}
elseif (($PSVersionTable.PSEdition -eq 'Core') + ($PSVersionTable.Platform -eq 'Unix') -and ($PSVersionTable.OS -like 'Linux*'))
{
# Sorry, Linux is not supported...
$paramWriteError = @{
Message = 'Sorry, Linux is not supported...'
Exception = 'Sorry, Linux is not supported!'
Category = 'NotImplemented'
ErrorAction = 'Stop'
}
Write-Error @paramWriteError
exit 1
}
else
{
# Windows? Really??? OK, sure this is supported
$SourceConfigFile = ($env:userprofile + '\AppData\Roaming\Microsoft\Teams\desktop-config.json')
}
#endregion SetConfigPath
}
process
{
if (Test-Path -Path $SourceConfigFile -ErrorAction SilentlyContinue -WarningAction Continue)
{
#region GetConfig
try
{
# Splat the parameters
$paramGetContent = @{
Path = $SourceConfigFile
Force = $true
ErrorAction = 'Stop'
WarningAction = 'Continue'
}
$Teams = (Get-Content @paramGetContent | ConvertFrom-Json -ErrorAction Stop)
# Cleanup
$paramGetContent = $null
}
catch
{
# get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
# output information. Post-process collected info, and log info (optional)
$info | Out-String | Write-Verbose
Write-Error -Message $e.Exception.Message -ErrorAction Stop -Exception $e.Exception -TargetObject $e.CategoryInfo.TargetName
break
}
#endregion GetConfig
#region
if ($Teams.appPreferenceSettings)
{
if ($Teams.appPreferenceSettings.openAsHidden)
{
if ($Teams.appPreferenceSettings.openAsHidden -ne $AppPrefSetOpenAsHidden)
{
Write-Verbose -Message 'Value of openAsHidden will be changed to the desired default'
$Teams.appPreferenceSettings.openAsHidden = $AppPrefSetOpenAsHidden
# Set the change indicator
$ChangedConfig = $true
Write-Verbose -Message 'Value of openAsHidden was changed to the desired default'
}
else
{
Write-Verbose -Message 'Value of openAsHidden is unchanged'
}
}
else
{
Write-Verbose -Message 'The Parameter openAsHidden will be created with the desired default value'
# Splat the parameters
$paramAddMember = @{
MemberType = 'NoteProperty'
Name = 'openAsHidden'
Value = $AppPrefSetOpenAsHidden
}
$null = ($Teams.appPreferenceSettings | Add-Member @paramAddMember)
# Cleanup
$paramAddMember = $null
# Set the change indicator
$ChangedConfig = $true
Write-Verbose -Message 'The Parameter openAsHidden was created with the desired default value'
}
if ($Teams.appPreferenceSettings.openAtLogin)
{
if ($Teams.appPreferenceSettings.openAtLogin -ne $AppPrefSetOpenAtLogin)
{
Write-Verbose -Message 'Value of openAtLogin will be changed to the desired default'
$Teams.appPreferenceSettings.openAtLogin = $AppPrefSetOpenAtLogin
# Set the change indicator
$ChangedConfig = $true
Write-Verbose -Message 'Value of openAtLogin was changed to the desired default'
}
else
{
Write-Verbose -Message 'Value of openAtLogin is unchanged'
}
}
else
{
Write-Verbose -Message 'The Parameter openAtLogin will be created with the desired default value'
# Splat the parameters
$paramAddMember = @{
MemberType = 'NoteProperty'
Name = 'openAtLogin'
Value = $AppPrefSetOpenAtLogin
}
$null = ($Teams.appPreferenceSettings | Add-Member @paramAddMember)
# Cleanup
$paramAddMember = $null
# Set the change indicator
$ChangedConfig = $true
Write-Verbose -Message 'The Parameter openAtLogin was created with the desired default value'
}
if (($PSVersionTable.PSEdition -eq 'Core') + ($PSVersionTable.Platform -eq 'Unix') -and ($PSVersionTable.OS -like 'Darwin*') )
{
Write-Verbose -Message 'The setting registerAsIMProvider is not supported on macOS...'
}
else
{
if ($Teams.appPreferenceSettings.registerAsIMProvider)
{
if ($Teams.appPreferenceSettings.registerAsIMProvider -ne $AppPrefSetRegisterAsIMProvider)
{
Write-Verbose -Message 'Value of registerAsIMProvider will be changed to the desired default'
$Teams.appPreferenceSettings.registerAsIMProvider = $AppPrefSetRegisterAsIMProvider
# Set the change indicator
$ChangedConfig = $true
Write-Verbose -Message 'Value of registerAsIMProvider was changed to the desired default'
}
else
{
Write-Verbose -Message 'Value of registerAsIMProvider is unchanged'
}
}
else
{
Write-Verbose -Message 'The Parameter registerAsIMProvider will be created with the desired default value'
# Splat the parameters
$paramAddMember = @{
MemberType = 'NoteProperty'
Name = 'registerAsIMProvider'
Value = $AppPrefSetRegisterAsIMProvider
}
$null = ($Teams.appPreferenceSettings | Add-Member @paramAddMember)
# Cleanup
$paramAddMember = $null
# Set the change indicator
$ChangedConfig = $true
Write-Verbose -Message 'The Parameter registerAsIMProvider was created with the desired default value'
}
}
if ($Teams.appPreferenceSettings.runningOnClose)
{
if ($Teams.appPreferenceSettings.runningOnClose -ne $AppPrefSetRunningOnClose)
{
Write-Verbose -Message 'Value of runningOnClose will be changed to the desired default'
$Teams.appPreferenceSettings.runningOnClose = $AppPrefSetRunningOnClose
# Set the change indicator
$ChangedConfig = $true
Write-Verbose -Message 'Value of runningOnClose was changed to the desired default'
}
else
{
Write-Verbose -Message 'Value of runningOnClose is unchanged'
}
}
else
{
Write-Verbose -Message 'The Parameter runningOnClose will be created with the desired default value'
# Splat the parameters
$paramAddMember = @{
MemberType = 'NoteProperty'
Name = 'runningOnClose'
Value = $AppPrefSetRunningOnClose
}
$null = ($Teams.appPreferenceSettings | Add-Member @paramAddMember)
# Cleanup
$paramAddMember = $null
# Set the change indicator
$ChangedConfig = $true
Write-Verbose -Message 'The Parameter runningOnClose was created with the desired default value'
}
}
if ($Teams.notificationWindowOnClose)
{
if ($Teams.notificationWindowOnClose -ne $NotificationWindowOnClose)
{
Write-Verbose -Message 'Value of notificationWindowOnClose will be changed to the desired default'
$Teams.notificationWindowOnClose = $NotificationWindowOnClose
# Set the change indicator
$ChangedConfig = $true
Write-Verbose -Message 'Value of notificationWindowOnClose was changed to the desired default'
}
else
{
Write-Verbose -Message 'Value of notificationWindowOnClose is unchanged'
}
}
else
{
Write-Verbose -Message 'The Parameter currentWebLanguage will be created with the desired default value'
# Splat the parameters
$paramAddMember = @{
MemberType = 'NoteProperty'
Name = 'notificationWindowOnClose'
Value = $NotificationWindowOnClose
}
$null = ($Teams | Add-Member @paramAddMember)
# Cleanup
$paramAddMember = $null
# Set the change indicator
$ChangedConfig = $true
Write-Verbose -Message 'The Parameter currentWebLanguage was created with the desired default value'
}
if ($Teams.overrideOpenAsHiddenProperty)
{
if ($Teams.overrideOpenAsHiddenProperty -ne $OverrideOpenAsHiddenProperty)
{
Write-Verbose -Message 'Value of overrideOpenAsHiddenProperty will be changed to the desired default'
$Teams.overrideOpenAsHiddenProperty = $OverrideOpenAsHiddenProperty
# Set the change indicator
$ChangedConfig = $true
Write-Verbose -Message 'Value of overrideOpenAsHiddenProperty was changed to the desired default'
}
else
{
Write-Verbose -Message 'Value of overrideOpenAsHiddenProperty is unchanged'
}
}
else
{
Write-Verbose -Message 'The Parameter overrideOpenAsHiddenProperty will be created with the desired default value'
# Splat the parameters
$paramAddMember = @{
MemberType = 'NoteProperty'
Name = 'overrideOpenAsHiddenProperty'
Value = $OverrideOpenAsHiddenProperty
}
$null = ($Teams | Add-Member @paramAddMember)
# Cleanup
$paramAddMember = $null
# Set the change indicator
$ChangedConfig = $true
Write-Verbose -Message 'The Parameter overrideOpenAsHiddenProperty was created with the desired default value'
}
if ($Teams.isAppFirstRun)
{
if ($Teams.isAppFirstRun -ne $IsAppFirstRun)
{
Write-Verbose -Message 'Value of isAppFirstRun will be changed to the desired default'
$Teams.isAppFirstRun = $IsAppFirstRun
# Set the change indicator
$ChangedConfig = $true
Write-Verbose -Message 'Value of isAppFirstRun was changed to the desired default'
}
else
{
Write-Verbose -Message 'Value of isAppFirstRun is unchanged'
}
}
else
{
Write-Verbose -Message 'The Parameter isAppFirstRun will be created with the desired default value'
# Splat the parameters
$paramAddMember = @{
MemberType = 'NoteProperty'
Name = 'isAppFirstRun'
Value = $false
}
$null = ($Teams | Add-Member @paramAddMember)
# Cleanup
$paramAddMember = $null
# Set the change indicator
$ChangedConfig = $true
Write-Verbose -Message 'The Parameter isAppFirstRun was created with the desired default value'
}
if ($Teams.currentWebLanguage)
{
if ($Teams.currentWebLanguage -ne $CurrentWebLanguage)
{
Write-Verbose -Message 'Value of currentWebLanguage will be changed to the desired default'
$Teams.currentWebLanguage = $CurrentWebLanguage
# Set the change indicator
$ChangedConfig = $true
Write-Verbose -Message 'Value of currentWebLanguage was changed to the desired default'
}
else
{
Write-Verbose -Message 'Value of currentWebLanguage is unchanged'
}
}
else
{
Write-Verbose -Message 'The Parameter currentWebLanguage will be created with the desired default value'
# Splat the parameters
$paramAddMember = @{
MemberType = 'NoteProperty'
Name = 'currentWebLanguage'
Value = $CurrentWebLanguage
}
$null = ($Teams | Add-Member @paramAddMember)
#Cleanup
$paramAddMember = $null
# Set the change indicator
$ChangedConfig = $true
Write-Verbose -Message 'The Parameter currentWebLanguage was created with the desired default value'
}
#endregion
#region SaveNewConfig
if ($ChangedConfig)
{
Write-Verbose -Message 'Changed configuration will be saved'
try
{
# Splat the parameters
$paramConvertToJson = @{
Compress = $true
ErrorAction = 'Stop'
WarningAction = 'Continue'
}
$paramSetContent = @{
Path = $SourceConfigFile
Force = $true
Confirm = $false
ErrorAction = 'Stop'
WarningAction = 'Continue'
}
$null = ($Teams | ConvertTo-Json @paramConvertToJson | Set-Content @paramSetContent)
# Cleanup
$paramConvertToJson = $null
$paramSetContent = $null
$Teams = $null
}
catch
{
# get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
# output information. Post-process collected info, and log info (optional)
$info | Out-String | Write-Verbose
Write-Error -Message $e.Exception.Message -ErrorAction Stop -Exception $e.Exception -TargetObject $e.CategoryInfo.TargetName
break
}
Write-Verbose -Message 'Changed configuration was saved'
}
else
{
Write-Verbose -Message 'No changes made to the configuration'
}
#endregion SaveNewConfig
}
else
{
Write-Warning -Message 'No Configuration File for Microsoft Teams was found.'
exit 1
}
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,65 @@
#requires -Version 2.0 -Modules MicrosoftTeams
<#
.SYNOPSIS
Get a List of external Microsoft Teams Applications
.DESCRIPTION
Get a List of external Microsoft Teams Applications for the tenant.
.EXAMPLE
PS C:\> .\Get-AllExternalTeamsApps.ps1
Get a List of external Microsoft Teams Applications for the tenant.
.EXAMPLE
PS C:\> .\Get-AllExternalTeamsApps.ps1 | Select-Object -Property DisplayName, DistributionMethod
Get a List of external Microsoft Teams Applications for the tenant.
.NOTES
You need to use the Microsoft Teams Cmdlets module
If you don't have, install it from the gallery:
Install-Module -Name MicrosoftTeams
#>
[CmdletBinding(ConfirmImpact = 'None')]
param ()
<#
Simple filter: External Apps will have the ExternalId field filled,
where store apps (from the Microsoft Teams App Store) not.
#>
Get-TeamsApp | Where-Object -FilterScript {
$_.ExternalId
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,717 @@
#requires -Version 3.0 -Modules MicrosoftTeams
<#
.SYNOPSIS
Collects assigned phone numbers from Microsoft Teams
.DESCRIPTION
This script queries Microsoft Teams for assigned numbers and displays in a formatted table with the option to export the report in several formats
During processing LineURI's are run against a regex pattern to extract the DDI/DID and the extension to a separate column
This script collects Microsoft Teams objects including:
Users, Meeting Rooms, Online Application Instances (Resource Accounts)
.PARAMETER OutputType
Define the Script Output
Valid values are:
CONSOLE - Dump a formatted list into the console
HTML - Create a simple HTML report with Tables. Only here to be compatible to our older version
XML - Create a simple Extensible Markup Language (XML) report
YAML - Create a simple YAML Ain't Markup Language (YAML) report
JSON - Create a simple JavaScript Object Notation (JSON) report. Handy if you need to upload the data via WebServices/APIs
CSV - Create a simple comma-separated values (CSV) report. This is perfect for re-use within Excel, or other applications
If you leave it empty (this is the default), the object will be dumped to the console!
This can become handy, if you use this script to generate the report and re-use it in the pipe or your own application
.PARAMETER Path
Where to store the Report File
Default is 'C:\scripts\PowerShell\logs\'
.PARAMETER DateFormat
Use the format for Get-Date
Default is 'yyyyMMdd-HHmmUTC'
.PARAMETER UTC
Use ToUniversalTime for the Date Strings
Default is $true
.PARAMETER Report
Define what to report.
Valid values are:
Users - Report numbers assigned to users
MeetingRooms - Report numbers assigned to Meetings Room accounts
ResourceAccounts - Report numbers assigned to Applications. (Auto Attendants (AA) and/or Call Queues (CQ) are supported)
All - All assigned numbers (all above merged into one report)
Default is 'All'
.EXAMPLE
PS C:\> .\Get-TeamsAssignedNumbers.ps1
The Report will be dumped to the console (unformatted)
.EXAMPLE
PS C:\> .\Get-TeamsAssignedNumbers.ps1 -OutputType CONSOLE -Report MeetingRooms
Dump a formatted report for numbers assigned to Meeting Rooms into the console
.EXAMPLE
PS C:\> .\Get-TeamsAssignedNumbers.ps1 -OutputType CONSOLE -Report ResourceAccounts
Dump a formatted report for numbers assigned to Resource Accounts into the console
.EXAMPLE
PS C:\> .\Get-TeamsAssignedNumbers.ps1 -OutputType CONSOLE -Report Users
Dump a formatted report for numbers assigned to Resource Accounts into the console
This will contain function users for Resource Accounts and Meeting Rooms, but they will be shown as user object!
.EXAMPLE
PS C:\> .\Get-TeamsAssignedNumbers.ps1 -OutputType CONSOLE -Report all
Dump a formatted report for every assigned number into the console
.EXAMPLE
PS C:\> .\Get-TeamsAssignedNumbers.ps1 -OutputType HTML
Create a simple HTML report with Tables.
.EXAMPLE
PS C:\> .\Get-TeamsAssignedNumbers.ps1 -OutputType XML
Create a simple Extensible Markup Language (XML) report
.EXAMPLE
PS C:\> .\Get-TeamsAssignedNumbers.ps1 -OutputType YAML
Create a simple YAML Ain't Markup Language (YAML) report
.EXAMPLE
PS C:\> .\Get-TeamsAssignedNumbers.ps1 -OutputType JSON
Create a simple JavaScript Object Notation (JSON) report.
.EXAMPLE
PS C:\> .\Get-TeamsAssignedNumbers.ps1 -OutputType CSV
Create a simple comma-separated values (CSV) report.
.LINK
https://github.com/ucgeek/Get-TeamsAssignedNumbers
.LINK
https://github.com/ucgeek/Get-TeamsAssignedNumbers/blob/master/LICENSE
.NOTES
Based on the work off Andrew Morpeth (@ucgeek and https://ucgeek.co/)
Licensed under the GNU General Public License v3.0 terms (by @ucgeek)
REQUIREMENTS:
If you haven't already, you will need to install the MicrosoftTeams PowerShell module
The script assumes, you are connect to the Teams/Skype for Business Online Service of Microsoft Office 365
#>
[CmdletBinding(ConfirmImpact = 'None')]
param
(
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName)]
[AllowNull()]
[AllowEmptyString()]
[AllowEmptyCollection()]
[ValidateSet('CONSOLE', 'HTML', 'XML', 'YAML', 'JSON', 'CSV', IgnoreCase = $true)]
[Alias('ReportFormat')]
[string]
$OutputType = $null,
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName)]
[AllowNull()]
[AllowEmptyString()]
[AllowEmptyCollection()]
[string]
$Path = 'C:\scripts\PowerShell\logs',
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName)]
[AllowNull()]
[AllowEmptyString()]
[AllowEmptyCollection()]
[string]
$DateFormat = 'yyyyMMdd-HHmmUTC',
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName)]
[Alias('UseUTC')]
[switch]
$UTC,
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName)]
[AllowEmptyCollection()]
[AllowEmptyString()]
[AllowNull()]
[ValidateSet('Users', 'MeetingRooms', 'ResourceAccounts', 'All', IgnoreCase = $true)]
[Alias('ReportType')]
[string[]]
$Report = 'All'
)
begin
{
#region BoundParameters
if (($PSCmdlet.MyInvocation.BoundParameters['Verbose']).IsPresent)
{
$VerboseValue = $true
}
else
{
$VerboseValue = $false
}
if (($PSCmdlet.MyInvocation.BoundParameters['Debug']).IsPresent)
{
$DebugValue = $true
}
else
{
$DebugValue = $false
}
#endregion BoundParameters
#region DateToUTC
if (-not ($UTC))
{
$UTC = $true
}
#endregion DateToUTC
#region UseDateUTC
if (($UTC -eq $true) -and ($DateFormat))
{
$FileName = ('MicrosoftTeamsAssignedNumbers_' + ((Get-Date).ToUniversalTime()).ToString($DateFormat))
}
#endregion UseDateUTC
#region UseDateFormat
if ($DateFormat)
{
$FileName = ('MicrosoftTeamsAssignedNumbers_' + ((Get-Date).ToString($DateFormat)))
}
else
{
# This is the default
$FileName = ('MicrosoftTeamsAssignedNumbers_' + (Get-Date -Format s).replace(':', '-'))
}
#endregion UseDateFormat
#region PathMangle
if ($Path)
{
# Save to a given PATH
$FilePath = ($Path + '\' + $FileName)
}
else
{
# Save here (where the script was started)
$FilePath = ('.\' + $FileName)
}
#endregion PathMangle
#region Regex
# Regex values
$LineURIRegex = '^(?:tel:)?(?:\+)?(\d+)(?:;ext=(\d+))?(?:;([\w-]+))?$'
#endregion Regex
#region ReportType
if (-not ($Report))
{
$Report = 'All'
}
#endregion ReportType
# Cleanup
$ReportData = @()
}
process
{
#region Users
if (($Report -eq 'Users') -or ($Report -eq 'All'))
{
# Get Users with LineURI
$UsersLineURI = $null
$paramGetCsOnlineUser = @{
ResultSize = 30000
Verbose = $VerboseValue
Debug = $DebugValue
Filter = {
(LineURI -ne $null)
}
ErrorAction = 'SilentlyContinue'
WarningAction = 'SilentlyContinue'
}
$paramSelectObject = @{
Property = 'UserPrincipalName', 'LineURI', 'DisplayName', 'FirstName', 'LastName', 'Enabled', 'SipAddress'
Verbose = $VerboseValue
Debug = $DebugValue
}
$UsersLineURI = (Get-CsOnlineUser @paramGetCsOnlineUser | Select-Object @paramSelectObject)
if ($UsersLineURI)
{
Write-Verbose -Message 'Processing User Numbers'
foreach ($ReportingItem in $UsersLineURI)
{
$Matches = @()
($ReportingItem.PhoneNumber -match $LineURIRegex | Out-Null)
$ReportingObject = (New-Object -TypeName System.Object)
$null = ($ReportingObject | Add-Member -MemberType NoteProperty -Name 'UserPrincipalName' -Value $ReportingItem.UserPrincipalName)
$null = ($ReportingObject | Add-Member -MemberType NoteProperty -Name 'LineURI' -Value $ReportingItem.LineURI)
$null = ($ReportingObject | Add-Member -MemberType NoteProperty -Name 'DDI' -Value $Matches[1])
$null = ($ReportingObject | Add-Member -MemberType NoteProperty -Name 'Ext' -Value $Matches[2])
$null = ($ReportingObject | Add-Member -MemberType NoteProperty -Name 'DisplayName' -Value $ReportingItem.DisplayName)
$null = ($ReportingObject | Add-Member -MemberType NoteProperty -Name 'FirstName' -Value $ReportingItem.FirstName)
$null = ($ReportingObject | Add-Member -MemberType NoteProperty -Name 'LastName' -Value $ReportingItem.LastName)
$null = ($ReportingObject | Add-Member -MemberType NoteProperty -Name 'SipAddress' -Value $($ReportingItem.SipAddress -replace 'sip:', ''))
$null = ($ReportingObject | Add-Member -MemberType NoteProperty -Name 'Enabled' -Value $ReportingItem.Enabled)
$null = ($ReportingObject | Add-Member -MemberType NoteProperty -Name 'Type' -Value 'User')
# Add to array
$null = ($ReportData += $ReportingObject)
}
}
}
#endregion Users
#region MeetingRooms
if (($Report -eq 'MeetingRooms') -or ($Report -eq 'All'))
{
# Get meeting room numbers
$MeetingRoomLineURI = $null
$paramGetCsMeetingRoom = @{
ResultSize = 10000
Verbose = $VerboseValue
Debug = $DebugValue
Filter = {
LineURI -ne $null
}
ErrorAction = 'SilentlyContinue'
WarningAction = 'SilentlyContinue'
}
$paramSelectObject = @{
Property = 'UserPrincipalName', 'LineURI', 'DisplayName', 'Enabled', 'SipAddress'
Verbose = $VerboseValue
Debug = $DebugValue
}
$MeetingRoomLineURI = (Get-CsMeetingRoom @paramGetCsMeetingRoom | Select-Object @paramSelectObject)
if ($MeetingRoomLineURI)
{
Write-Verbose -Message 'Processing Meeting Room Numbers'
foreach ($ReportingItem in $MeetingRoomLineURI)
{
$Matches = @()
($ReportingItem.PhoneNumber -match $LineURIRegex | Out-Null)
$ReportingObject = (New-Object -TypeName System.Object)
$null = ($ReportingObject | Add-Member -MemberType NoteProperty -Name 'UserPrincipalName' -Value $ReportingItem.UserPrincipalName)
$null = ($ReportingObject | Add-Member -MemberType NoteProperty -Name 'LineURI' -Value $ReportingItem.LineURI)
$null = ($ReportingObject | Add-Member -MemberType NoteProperty -Name 'DDI' -Value $Matches[1])
$null = ($ReportingObject | Add-Member -MemberType NoteProperty -Name 'Ext' -Value $Matches[2])
$null = ($ReportingObject | Add-Member -MemberType NoteProperty -Name 'DisplayName' -Value $ReportingItem.DisplayName)
$null = ($ReportingObject | Add-Member -MemberType NoteProperty -Name 'SipAddress' -Value $($ReportingItem.SipAddress -replace 'sip:', ''))
$null = ($ReportingObject | Add-Member -MemberType NoteProperty -Name 'Enabled' -Value $ReportingItem.Enabled)
$null = ($ReportingObject | Add-Member -MemberType NoteProperty -Name 'Type' -Value 'Meeting Room')
# Remove existing User entry (Rooms have an user object as well)
if ($ReportData.UserPrincipalName -contains $ReportingItem.UserPrincipalName)
{
$ReportData = ($ReportData | Where-Object -FilterScript {
($_.UserPrincipalName -ne $ReportingItem.UserPrincipalName)
})
}
# Add to array
$null = ($ReportData += $ReportingObject)
}
}
}
#endregion MeetingRooms
#region ResourceAccounts
if (($Report -eq 'ResourceAccounts') -or ($Report -eq 'All'))
{
# Get online resource accounts
$OnlineApplicationInstanceLineURI = $null
$paramGetCsOnlineApplicationInstance = @{
Force = $true
ResultSize = 10000
Verbose = $VerboseValue
Debug = $DebugValue
ErrorAction = 'SilentlyContinue'
WarningAction = 'SilentlyContinue'
}
$paramSelectObject = @{
Property = 'UserPrincipalName', 'DisplayName', 'PhoneNumber', 'ApplicationId', 'Enabled'
Verbose = $VerboseValue
Debug = $DebugValue
}
$OnlineApplicationInstanceLineURI = (Get-CsOnlineApplicationInstance @paramGetCsOnlineApplicationInstance | Where-Object -FilterScript {
($_.PhoneNumber -ne $null)
} -ErrorAction SilentlyContinue -WarningAction SilentlyContinue | Select-Object @paramSelectObject)
if ($OnlineApplicationInstanceLineURI)
{
Write-Verbose -Message 'Processing Online Application Instances (Resource Accounts) Numbers'
foreach ($ReportingItem in $OnlineApplicationInstanceLineURI)
{
$Matches = @()
($ReportingItem.PhoneNumber -match $LineURIRegex | Out-Null)
<#
Workaround:
Get-CsOnlineApplicationInstance does not return an "Enabled" and "SipAddress" field,
so we try to re-use any existing object information
Will not work all the time, only if regular users are reported as well!
#>
if ($ReportData.UserPrincipalName -contains $ReportingItem.UserPrincipalName)
{
# Cleanup
$WorkaroundInfo = $null
$WorkaroundInfo = ($ReportData | Where-Object -FilterScript {
($_.UserPrincipalName -eq $ReportingItem.UserPrincipalName)
} | Select-Object -Property 'Enabled', 'SipAddress')
if (($WorkaroundInfo).Enabled)
{
$isAppEnabled = (($WorkaroundInfo).Enabled)
}
else
{
$isAppEnabled = 'unknown'
}
if (($WorkaroundInfo).SipAddress)
{
$isSipAddress = (($WorkaroundInfo).SipAddress)
}
else
{
$isSipAddress = 'unknown'
}
}
$ReportingObject = (New-Object -TypeName System.Object)
$null = ($ReportingObject | Add-Member -MemberType NoteProperty -Name 'UserPrincipalName' -Value $ReportingItem.UserPrincipalName)
$null = ($ReportingObject | Add-Member -MemberType NoteProperty -Name 'LineURI' -Value $ReportingItem.PhoneNumber)
$null = ($ReportingObject | Add-Member -MemberType NoteProperty -Name 'DDI' -Value $Matches[1])
$null = ($ReportingObject | Add-Member -MemberType NoteProperty -Name 'Ext' -Value $Matches[2])
$null = ($ReportingObject | Add-Member -MemberType NoteProperty -Name 'DisplayName' -Value $ReportingItem.DisplayName)
$null = ($ReportingObject | Add-Member -MemberType NoteProperty -Name 'SipAddress' -Value $isSipAddress)
$null = ($ReportingObject | Add-Member -MemberType NoteProperty -Name 'Enabled' -Value $isAppEnabled)
$null = ($ReportingObject | Add-Member -MemberType NoteProperty -Name 'Type' -Value $(
if ($ReportingItem.ApplicationId -eq 'ce933385-9390-45d1-9512-c8d228074e07')
{
'Auto Attendant Resource Account'
}
elseif ($ReportingItem.ApplicationId -eq '11cd3e2e-fccb-42ad-ad00-878b93575e07')
{
'Call Queue Resource Account'
}
else
{
'Unknown Resource Account'
}
))
# Remove existing User entry (Apps have an user object as well)
if ($ReportData.UserPrincipalName -contains $ReportingItem.UserPrincipalName)
{
$ReportData = ($ReportData | Where-Object -FilterScript {
($_.UserPrincipalName -ne $ReportingItem.UserPrincipalName)
})
}
# Add to array
$null = ($ReportData += $ReportingObject)
}
}
}
#endregion ResourceAccounts
# Sort the Array data, based on the LineURI object
$paramSortObject = @{
Property = 'LineURI'
Verbose = $VerboseValue
Debug = $DebugValue
}
$ReportData = ($ReportData | Sort-Object @paramSortObject)
#region Output
switch ($OutputType)
{
CSV
{
$FilePath = ($FilePath + '.csv')
$paramConvertToCsv = @{
Delimiter = ','
NoTypeInformation = $true
Verbose = $VerboseValue
Debug = $DebugValue
ErrorAction = 'Continue'
WarningAction = 'Continue'
}
$PsCsv = ($ReportData | ConvertTo-Csv @paramConvertToCsv)
$paramOutFile = @{
FilePath = $FilePath
Force = $true
Append = $false
Encoding = 'utf8'
ErrorAction = 'Continue'
WarningAction = 'Continue'
Verbose = $VerboseValue
Debug = $DebugValue
}
($PsCsv | Out-File @paramOutFile)
Write-Verbose -Message ('Your CSV report was saved to: {0}' -f $FilePath)
}
JSON
{
$FilePath = ($FilePath + '.json')
$paramConvertToJson = @{
Depth = 5
ErrorAction = 'Continue'
WarningAction = 'Continue'
Verbose = $VerboseValue
Debug = $DebugValue
}
$PsJson = @($ReportData | ConvertTo-Json @paramConvertToJson)
$paramOutFile = @{
FilePath = $FilePath
Force = $true
Append = $false
Encoding = 'utf8'
ErrorAction = 'Continue'
WarningAction = 'Continue'
Verbose = $VerboseValue
Debug = $DebugValue
}
($PsJson | Out-File @paramOutFile)
Write-Verbose -Message ('Your JSON report was saved to: {0}' -f $FilePath)
}
YAML
{
if (Get-Command -Name 'ConvertTo-Yaml' -ErrorAction SilentlyContinue)
{
$FilePath = ($FilePath + '.yml')
<#
Workaround for ConvertTo-Yaml
#>
$paramJsonWorkaround = @{
ErrorAction = 'Continue'
WarningAction = 'Continue'
Verbose = $VerboseValue
Debug = $DebugValue
}
$ReportData = @($ReportData | ConvertTo-Json @paramJsonWorkaround | ConvertFrom-Json @paramJsonWorkaround)
$paramConvertToYaml = @{
Data = $ReportData
Force = $true
ErrorAction = 'Continue'
WarningAction = 'Continue'
Verbose = $VerboseValue
Debug = $DebugValue
}
$PsYaml = (ConvertTo-Yaml @paramConvertToYaml)
$paramOutFile = @{
FilePath = $FilePath
Force = $true
Append = $false
Encoding = 'utf8'
ErrorAction = 'Continue'
WarningAction = 'Continue'
Verbose = $VerboseValue
Debug = $DebugValue
}
($PsYaml | Out-File @paramOutFile)
Write-Verbose -Message ('Your YAML report was saved to: {0}' -f $FilePath)
}
else
{
$paramWriteError = @{
Exception = 'The ConvertTo-Yaml command was not found'
Message = 'Please ensure, that the ''powershell-yaml'' module is installed.'
Category = 'NotInstalled'
RecommendedAction = 'Please use ''Install-Module -Name powershell-yaml'' to install the required module!'
ErrorAction = 'Stop'
}
Write-Error @paramWriteError
}
}
XML
{
$FilePath = ($FilePath + '.xml')
$paramConvertToXml = @{
As = 'Stream'
InputObject = $ReportData
NoTypeInformation = $true
ErrorAction = 'Continue'
WarningAction = 'Continue'
Verbose = $VerboseValue
Debug = $DebugValue
}
$PsXML = (ConvertTo-Xml @paramConvertToXml)
$paramOutFile = @{
FilePath = $FilePath
Force = $true
Append = $false
Encoding = 'utf8'
ErrorAction = 'Continue'
WarningAction = 'Continue'
Verbose = $VerboseValue
Debug = $DebugValue
}
($PsXML | Out-File @paramOutFile)
Write-Verbose -Message ('Your XML report was saved to: {0}' -f $FilePath)
}
HTML
{
$FilePath = ($FilePath + '.html')
$Header = @"
<title>Microsoft Teams assigned phone number report</title>
<meta charset='UTF-8'>
<meta name='viewport' content='width=device-width, initial-scale=1'>
<style>
table {
border-width: 1px;
border-style: solid;
border-color: black;
border-collapse: collapse;
}
th {
border-width: 1px;
padding: 3px;
border-style: solid;
border-color: black;
background-color: #6495ED;
}
td {
border-width: 1px;
padding: 3px;
border-style: solid;
border-color: black;
}
</style>
"@
$htmlParams = @{
Title = 'Microsoft Teams assigned phone number report'
Head = $Header
body = '<h3>Microsoft Teams assigned phone number report</h3>'
PreContent = '<p>The following Phone numbers are assigned in Microsoft Teams:</p>'
PostContent = '<p><i>Last updated: ' + ((Get-Date).ToUniversalTime()).ToString('HH:MM dd.MM.yyyy (UTC)') + '</i></p>'
Verbose = $VerboseValue
Debug = $DebugValue
}
$PsHtml = ($ReportData | ConvertTo-Html @htmlParams)
$paramOutFile = @{
FilePath = $FilePath
Force = $true
Append = $false
Encoding = 'utf8'
ErrorAction = 'Continue'
WarningAction = 'Continue'
Verbose = $VerboseValue
Debug = $DebugValue
}
($PsHtml | Out-File @paramOutFile)
Write-Verbose -Message ('Your HTML report was saved to: {0}' -f $FilePath)
}
CONSOLE
{
$paramFormatTable = @{
AutoSize = $true
Property = 'UserPrincipalName', 'LineURI', 'DDI', 'Ext', 'DisplayName', 'Type'
Verbose = $VerboseValue
Debug = $DebugValue
}
($ReportData | Format-Table @paramFormatTable)
Write-Verbose -Message 'Formated Object was dumped'
}
default
{
$ReportData
Write-Verbose -Message 'Unformated Object was dumped'
}
}
#endregion Output
}
end
{
# Cleanup
$ReportData = $null
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,276 @@
function Get-TeamsServiceNumbers
{
<#
.SYNOPSIS
Get the Phone numbers assigned to Teams/SfB Services
.DESCRIPTION
Get the Phone numbers assigned to Teams/SfB Services
Supported are AutoAttendant and/or CallQueue
.PARAMETER AutoAttendant
Get the numbers assigned to AutoAttendant(s)
.PARAMETER CallQueue
Get the numbers assigned to CallQueue(s)
.PARAMETER All
Get all Numbers, assignee to AutoAttendant(s) and CallQueue(s)
.PARAMETER LeaveTel
Normally the function dumps phone numbers with a stripped tel:
With this switch the function will dump it with the leading tel:
.PARAMETER Export
Export the result to a CSV
.PARAMETER Path
Path for the CSV Export
.EXAMPLE
PS C:\> Get-TeamsServiceNumbers -All
Get all Services numbers, AutoAttendant(s) and CallQueue(s), and dump them to the terminal
.EXAMPLE
PS C:\> Get-TeamsServiceNumbers -All -Export -Path '.\TeamsServiceNumbers.csv'
Get all Services numbers, AutoAttendant(s) and CallQueue(s), and export them to the given CSV file '.\TeamsServiceNumbers.csv'
TeamsServiceNumbers.csv is in the directory where the user is right now (and calls the function)
.EXAMPLE
PS C:\> Get-TeamsServiceNumbers -All -Export -Path 'c:\temp\TeamsServiceNumbers.csv'
Get all Services numbers, AutoAttendant(s) and CallQueue(s), and export them to the given CSV file 'c:\temp\TeamsServiceNumbers.csv'
.EXAMPLE
PS C:\> Get-TeamsServiceNumbers -All -Export
Get all Services numbers, AutoAttendant(s) and CallQueue(s), and export them to a CSV
The funtion will ask for the Path to the CSV File
.EXAMPLE
PS C:\> Get-TeamsServiceNumbers -AutoAttendant
Get all AutoAttendant(s) Services numbers and dump them to the terminal
.EXAMPLE
PS C:\> Get-TeamsServiceNumbers -AA
Get all AutoAttendant(s) Services numbers and dump them to the terminal
Same as above, but use the Alias (Shorter)
.EXAMPLE
PS C:\> Get-TeamsServiceNumbers -CallQueue
Get all CallQueue(s) Services numbers and dump them to the terminal
.EXAMPLE
PS C:\> Get-TeamsServiceNumbers -CQ
Get all CallQueue(s) Services numbers and dump them to the terminal
Same as above, but use the Alias (Shorter)
.NOTES
Additional information about the function.
#>
[CmdletBinding(DefaultParameterSetName = 'AllNumbers',
ConfirmImpact = 'None')]
[OutputType([psobject])]
param
(
[Parameter(ParameterSetName = 'AANumbers',
ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true)]
[Alias('AA')]
[switch]
$AutoAttendant = $null,
[Parameter(ParameterSetName = 'CQNumbers',
ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true)]
[Alias('CQ')]
[switch]
$CallQueue = $null,
[Parameter(ParameterSetName = 'AllNumbers',
ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true)]
[Alias('Any')]
[switch]
$All,
[Parameter(ParameterSetName = '__AllParameterSets',
ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true)]
[switch]
$LeaveTel = $null,
[Parameter(ParameterSetName = '__AllParameterSets',
ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true)]
[Alias('ExportCsv', 'CSV')]
[switch]
$Export = $false
)
dynamicparam
{
if ($PSBoundParameters['Export'])
{
# The PATH parameter is only needed if -Export is given
$PathAttribute = New-Object System.Management.Automation.ParameterAttribute
$PathAttribute.Mandatory = $true
$PathAttribute.HelpMessage = "Path for the CSV Export:"
$PathAttribute.ValueFromPipeline = $true
$PathAttribute.ValueFromPipelineByPropertyName = $true
$PathAttribute.ParameterSetName = '__AllParameterSets'
$attributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute]
$attributeCollection.Add($PathAttribute)
$PathParam = New-Object System.Management.Automation.RuntimeDefinedParameter('Path', [String], $attributeCollection)
$paramDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
$paramDictionary.Add('Path', $PathParam)
$paramDictionary
}
}
begin
{
switch ($PsCmdlet.ParameterSetName)
{
'AANumbers'
{
$AutoAttendant = $true
}
'CQNumbers'
{
$CallQueue = $true
}
'AllNumbers'
{
$All = $true
}
default
{
$All = $true
}
}
if ($PSBoundParameters.Path)
{
$Path = $PSBoundParameters.Path
}
$NumberReport = @()
}
process
{
if (($AutoAttendant) -or ($All))
{
foreach ($AA in (Get-CsAutoAttendant -ErrorAction Continue))
{
foreach ($AppInstance in $AA.ApplicationInstances)
{
$AAName = $AA.Name
$AppPhoneNum = $null
if ($LeaveTel)
{
$AppPhoneNum = ((Get-CsOnlineApplicationInstance -Identity $AppInstance -ErrorAction Continue).PhoneNumber)
}
else
{
$AppPhoneNum = (((Get-CsOnlineApplicationInstance -Identity $AppInstance -ErrorAction Continue).PhoneNumber).replace('tel:', ''))
}
Write-Verbose ('AutoAttendant ' + $AA.Name + ' has ' + $AppPhoneNum + ' assigned')
$NewRow = $null
$NewRow = [PSCustomObject][ordered]@{
Name = ($AA.Name)
Number = ($AppPhoneNum)
Type = 'AutoAttendant'
}
$NumberReport += $newrow
}
}
}
if (($CallQueue) -or ($All))
{
foreach ($CQ in (Get-CsCallQueue -ErrorAction Continue))
{
foreach ($AppInstance in $CQ.ApplicationInstances)
{
$CQName = $null
$CQName = $CQ.Name
$AppPhoneNum = $null
if ($LeaveTel)
{
$AppPhoneNum = ((Get-CsOnlineApplicationInstance -Identity $AppInstance -ErrorAction Continue).PhoneNumber)
}
else
{
$AppPhoneNum = (((Get-CsOnlineApplicationInstance -Identity $AppInstance -ErrorAction Continue).PhoneNumber).replace('tel:', ''))
}
Write-Verbose ('CallQueue ' + $CQ.Name + ' has ' + $AppPhoneNum + ' assigned')
$NewRow = $null
$NewRow = [PSCustomObject][ordered]@{
Name = ($CQ.Name)
Number = ($AppPhoneNum)
Type = 'CallQueue'
}
$NumberReport += $newrow
}
}
}
}
end
{
if (($PSBoundParameters['Export']) -or ($Path))
{
$NumberReport | Export-Csv -Path $Path -NoTypeInformation -Encoding UTF8 -ErrorAction Stop -WarningAction Continue
Write-Verbose -Message $NumberReport
}
else
{
$NumberReport
}
}
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,446 @@
function Get-bdcMicrosoftTeamsReporting
{
<#
.SYNOPSIS
Get a Report for all Microsoft Teams Teams
.DESCRIPTION
Get a Report for all Microsoft Teams Teams
.PARAMETER Connect
Executes Connect-MicrosoftTeams for you
.PARAMETER Disconnect
Executes Disconnect-MicrosoftTeams for you as soon as the report is generated
.PARAMETER GiphyDetails
Include Giphy Details in the report
.PARAMETER MemesDetails
Include Memes Details in the report
.PARAMETER GuestDetails
Include Guest Details in the report
.PARAMETER Detailed
Report some more Details about the Teams.
.PARAMETER AllDetails
All of the Details are reported, might be a bit verbose for some.
.EXAMPLE
PS C:\> Get-bdcMicrosoftTeamsReporting
Get a Report for all Microsoft Teams Teams
.EXAMPLE
PS C:\> Get-bdcMicrosoftTeamsReporting | Where-Object -FilterScript {
$_.owners -eq 0
} | Select-Object -ExpandProperty DisplayName
Find all Teams without an owner.
.EXAMPLE
PS C:\> Get-bdcMicrosoftTeamsReporting | Where-Object -FilterScript {
$_.owners -eq 1
} | Select-Object -ExpandProperty DisplayName | ForEach-Object -Process {
Write-Warning -Message "Looks like $_ is an orphaned objects, it has no owner!" -ErrorAction Continue
}
Find all Teams without an owner. Teams without an owner are bad teams...
.EXAMPLE
PS C:\> Get-bdcMicrosoftTeamsReporting | Where-Object -FilterScript {
($_.Members -eq 0) -and ($_.Guests -eq 0)
} | Select-Object -ExpandProperty DisplayName | ForEach-Object -Process {
Write-Warning -Message "Looks like $_ has no members and guests!" -ErrorAction Continue
}
Find Teams without members and guests, empty teams are boring and, more or less, useless
.EXAMPLE
PS C:\> Get-bdcMicrosoftTeamsReporting | Where-Object -FilterScript {
($_.Archived -eq $true) -and ($_.ShowInTeamsSearchAndSuggestions -eq $true)
} | Select-Object -ExpandProperty DisplayName | ForEach-Object -Process {
Write-Warning -Message "Looks like $_ is archived but searchable!" -ErrorAction Continue
}
Find archived Teams that are still searchable, might not be a bad thing...
.EXAMPLE
PS C:\> Get-bdcMicrosoftTeamsReporting -Connect -Disconnect
Get a Report for all Microsoft Teams Teams, invokes the Connect-MicrosoftTeams and Disconnect-MicrosoftTeams for you
.EXAMPLE
PS C:\> Get-bdcMicrosoftTeamsReporting -AllDetails
Get a Report for all Microsoft Teams Teams, with all the details (very verbose)
.EXAMPLE
PS C:\> Get-bdcMicrosoftTeamsReporting -GiphyDetails
Get a Report for all Microsoft Teams Teams, and include Giphy Details
.EXAMPLE
PS C:\> Get-bdcMicrosoftTeamsReporting -MemesDetails
Get a Report for all Microsoft Teams Teams, and include Memes Details
.EXAMPLE
PS C:\> Get-bdcMicrosoftTeamsReporting -GiphyDetails -MemesDetails
Get a Report for all Microsoft Teams Teams, and include Giphy and Memes Details
.EXAMPLE
PS C:\> Get-bdcMicrosoftTeamsReporting -GuestDetails
Get a Report for all Microsoft Teams Teams, and include Guest Details
.EXAMPLE
PS C:\> Get-bdcMicrosoftTeamsReporting -Detailed
Get a Report for all Microsoft Teams Teams, with more details then the regular report
.EXAMPLE
PS C:\> Get-bdcMicrosoftTeamsReporting -Detailed -GuestDetails
Get a Report for all Microsoft Teams Teams, with more details then the regular report and Guest Details
.NOTES
Reworked function to deliver everything we need to have for our Office 365 reporting service.
See the examples above and you will get an idea what you can do with filtering :-)
.LINK
https://www.powershellgallery.com/packages/MicrosoftTeams/1.0.3
.LINK
https://github.com/MicrosoftDocs/office-docs-powershell/tree/master/teams
.LINK
Get-Team
.LINK
Get-TeamUser
.LINK
Get-TeamChannel
.LINK
Connect-MicrosoftTeams
.LINK
Disconnect-MicrosoftTeams
.LINK
https://aka.ms/InstallModule
.LINK
https://github.com/tomarbuthnot/Microsoft-Teams-PowerShell
.LINK
https://opensource.org/licenses/BSD-3-Clause
#>
[CmdletBinding(ConfirmImpact = 'None')]
[OutputType([psobject])]
param
(
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName)]
[Alias('DoConnect')]
[switch]
$Connect,
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName)]
[Alias('DoDisconnect')]
[switch]
$Disconnect,
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName)]
[Alias('IncludeGiphyDetails', 'Giphy')]
[switch]
$GiphyDetails,
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName)]
[Alias('IncludeMemesDetails', 'Memes')]
[switch]
$MemesDetails,
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName)]
[Alias('IncludeGuestDetails', 'Guest')]
[switch]
$GuestDetails,
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName)]
[Alias('DetailedReport')]
[switch]
$Detailed,
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName)]
[Alias('VerboseReport')]
[switch]
$AllDetails
)
begin
{
#region Connect
if ($Connect)
{
# Logon
$null = (Connect-MicrosoftTeams)
}
#endregion Connect
# Crete an empty Report variable
$MicrosoftTeamsReport = @()
}
process
{
# Get all Microsoft Teams Teams and loop over them
try
{
Get-Team -ErrorAction Stop | ForEach-Object -Process {
try
{
Write-Verbose -Message ('Generate the Report for the Microsoft Teams Team {0}' -f $_.DisplayName)
# Cleanup
$TeamUserDetails = $null
# Get the User information for the Microsoft Teams Team and save it for reuse
$TeamUserDetails = $null
$TeamUserDetails = (Get-TeamUser -GroupId $_.GroupID -ErrorAction Stop)
# Get the Channel information for the Microsoft Teams Team
$TeamChannelDetails = $null
$TeamChannelDetails = ((Get-TeamChannel -GroupId $_.GroupID -ErrorAction Stop).count)
# Put all details into an object
$SingleTeamReport = (New-Object -TypeName PSobject)
#region FillSingleTeamReport
$SingleTeamReport | Add-Member -MemberType NoteProperty -Name 'DisplayName' -Value $_.DisplayName
$SingleTeamReport | Add-Member -MemberType NoteProperty -Name 'Description' -Value $_.Description
$SingleTeamReport | Add-Member -MemberType NoteProperty -Name 'Visibility' -Value $_.Visibility
$SingleTeamReport | Add-Member -MemberType NoteProperty -Name 'Archived' -Value $_.Archived
$SingleTeamReport | Add-Member -MemberType NoteProperty -Name 'ShowInTeamsSearchAndSuggestions' -Value $_.ShowInTeamsSearchAndSuggestions
$SingleTeamReport | Add-Member -MemberType NoteProperty -Name 'Channels' -Value $TeamChannelDetails
$SingleTeamReport | Add-Member -MemberType NoteProperty -Name 'Owners' -Value (($TeamUserDetails | Where-Object -FilterScript {
$_.Role -like 'owner'
}).count)
$SingleTeamReport | Add-Member -MemberType NoteProperty -Name 'Members' -Value (($TeamUserDetails | Where-Object -FilterScript {
$_.Role -like 'member'
}).count)
$SingleTeamReport | Add-Member -MemberType NoteProperty -Name 'Guests' -Value (($TeamUserDetails | Where-Object -FilterScript {
$_.Role -like 'guest'
}).count)
#region GiphyDetails
if ($GiphyDetails -or $AllDetails)
{
$SingleTeamReport | Add-Member -MemberType NoteProperty -Name 'AllowGiphy' -Value $_.AllowGiphy
$SingleTeamReport | Add-Member -MemberType NoteProperty -Name 'GiphyContentRating' -Value $_.GiphyContentRating
}
#endregion GiphyDetails
#region MemesDetails
if ($MemesDetails -or $AllDetails)
{
$SingleTeamReport | Add-Member -MemberType NoteProperty -Name 'AllowStickersAndMemes' -Value $_.AllowStickersAndMemes
$SingleTeamReport | Add-Member -MemberType NoteProperty -Name 'AllowCustomMemes' -Value $_.AllowCustomMemes
}
#endregion MemesDetails
#region GuestDetails
if ($GuestDetails -or $AllDetails)
{
$SingleTeamReport | Add-Member -MemberType NoteProperty -Name 'AllowGuestCreateUpdateChannels' -Value $_.AllowGuestCreateUpdateChannels
$SingleTeamReport | Add-Member -MemberType NoteProperty -Name 'AllowGuestDeleteChannels' -Value $_.AllowGuestDeleteChannels
}
#endregion GuestDetails
#region DetailedReport
if ($Detailed -or $AllDetails)
{
# Based on the idea of Tom Arbuthnot (https://github.com/tomarbuthnot/Microsoft-Teams-PowerShell)
$DescriptionWordCount = (($_.Description | Out-String | Measure-Object -Word).words)
$SingleTeamReport | Add-Member -MemberType NoteProperty -Name 'DescriptionWordCount' -Value $DescriptionWordCount
$SingleTeamReport | Add-Member -MemberType NoteProperty -Name 'DescriptionScore' -Value $(if ($DescriptionWordCount -eq 0)
{
'Terrible'
}
elseif ($DescriptionWordCount -le 2)
{
'Poor'
}
elseif ($DescriptionWordCount -le 5)
{
'OK'
}
elseif ($DescriptionWordCount -ge 6)
{
'Good'
}
else
{
'Unknown'
}
)
# As requested by Peter Duda
$SingleTeamReport | Add-Member -MemberType NoteProperty -Name 'Classification' -Value $(if ($_.Classification)
{
$_.Classification
}
else
{
'None'
}
)
# New since 2020/01
$SingleTeamReport | Add-Member -MemberType NoteProperty -Name 'MailNickName' -Value $(if ($_.MailNickName)
{
$_.MailNickName
}
else
{
'None'
}
)
# Verbose reporting
$SingleTeamReport | Add-Member -MemberType NoteProperty -Name 'AllowCreateUpdateChannels' -Value $_.AllowCreateUpdateChannels
$SingleTeamReport | Add-Member -MemberType NoteProperty -Name 'AllowDeleteChannels' -Value $_.AllowDeleteChannels
$SingleTeamReport | Add-Member -MemberType NoteProperty -Name 'AllowAddRemoveApps' -Value $_.AllowAddRemoveApps
$SingleTeamReport | Add-Member -MemberType NoteProperty -Name 'AllowCreateUpdateRemoveTabs' -Value $_.AllowCreateUpdateRemoveTabs
$SingleTeamReport | Add-Member -MemberType NoteProperty -Name 'AllowCreateUpdateRemoveConnectors' -Value $_.AllowCreateUpdateRemoveConnectors
$SingleTeamReport | Add-Member -MemberType NoteProperty -Name 'AllowUserEditMessages' -Value $_.AllowUserEditMessages
$SingleTeamReport | Add-Member -MemberType NoteProperty -Name 'AllowUserDeleteMessages' -Value $_.AllowUserDeleteMessages
$SingleTeamReport | Add-Member -MemberType NoteProperty -Name 'AllowOwnerDeleteMessages' -Value $_.AllowOwnerDeleteMessages
$SingleTeamReport | Add-Member -MemberType NoteProperty -Name 'AllowTeamMentions' -Value $_.AllowTeamMentions
$SingleTeamReport | Add-Member -MemberType NoteProperty -Name 'AllowChannelMentions' -Value $_.AllowChannelMentions
}
#endregion DetailedReport
#region FinalValue
$SingleTeamReport | Add-Member -MemberType NoteProperty -Name 'GroupId' -Value $_.GroupId
#endregion FinalValue
#endregion FillSingleTeamReport
# Append to the Report
$MicrosoftTeamsReport += $SingleTeamReport
}
catch
{
#region WarningHandler
# get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
# output information. Post-process collected info, and log info (optional)
$info | Out-String | Write-Verbose
$paramWriteWarning = @{
Message = $e.Exception.Message
ErrorAction = 'Continue'
WarningAction = 'Continue'
}
Write-Warning @paramWriteWarning
#region WarningHandler
}
}
}
catch
{
#region ErrorHandler
# get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
# output information. Post-process collected info, and log info (optional)
$info | Out-String | Write-Verbose
$paramWriteError = @{
Message = $e.Exception.Message
ErrorAction = 'Stop'
Exception = $e.Exception
TargetObject = $e.CategoryInfo.TargetName
}
Write-Error @paramWriteError
# Just in case
Exit 1
#region ErrorHandler
}
}
end
{
#region Disconnect
if ($Disconnect)
{
# Logoff
$null = (Disconnect-MicrosoftTeams -Confirm:$false)
}
#endregion Disconnect
#region ShowReport
# Dump the Report
$MicrosoftTeamsReport
#endregion ShowReport
}
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,300 @@
#requires -Version 3.0 -Modules BitsTransfer -RunAsAdministrator
<#
.SYNOPSIS
Download, install, and Tweak System and Apps for Terminal Server use
.DESCRIPTION
Download, install, and Tweak System and Apps for Terminal Server (WVD/VDI/WDS) use
.NOTES
Early testing release - Future releases might get some parameters
Changelog:
1.0.0: Initial Release
Version 1.0.0
.LINK
http://enatec.io
#>
[CmdletBinding(ConfirmImpact = 'Low',
SupportsShouldProcess)]
param ()
begin
{
Write-Output -InputObject 'Download, install, and Tweak System and Apps for Terminal Server use'
# Default URL (Assume we use 64Bit)
[string]$FSLogixUrl = 'https://aka.ms/fslogix_download'
#region PossibleParameters
# Where to Store it
[string]$Target = ($env:Temp)
# File Name
[string]$TargetName = 'fslogix.zip'
# Install Switch
[string]$Arguments = '/install /quiet /norestart'
#endregion PossibleParameters
#region Defaults
# Set the full path of the downloaded installer
[string]$InstallerPackage = ($Target + '\' + $TargetName)
[string]$InstallerDestination = (($InstallerPackage).Replace('.zip', ''))
[string]$InstallerExecutable = ($InstallerDestination + '\x64\Release\FSLogixAppsSetup.exe')
$SCT = 'SilentlyContinue'
$STP = 'Stop'
#endregion Defaults
}
process
{
Write-Verbose -Message ('Downloading {0} to {1}' -f $TargetName, $InstallerPackage)
# Use BitsTransfer to download the latest installer
$paramStartBitsTransfer = @{
Source = $FSLogixUrl
Destination = $InstallerPackage
Priority = 'High'
TransferPolicy = 'Always'
ErrorAction = $STP
}
$null = (Start-BitsTransfer @paramStartBitsTransfer)
# Expand FSLogix Installer
$paramTestPath = @{
Path = $InstallerPackage
ErrorAction = $SCT
}
if (Test-Path @paramTestPath)
{
$paramTestPath = @{
Path = $InstallerDestination
ErrorAction = $SCT
}
if (-not (Test-Path @paramTestPath))
{
$paramNewItem = @{
Path = $InstallerDestination
Force = $true
Confirm = $false
ItemType = 'Directory'
ErrorAction = $SCT
}
$null = (New-Item @paramNewItem)
}
try
{
# Expand-Archive is to buggy!
$null = (Add-Type -AssemblyName System.IO.Compression.FileSystem -ErrorAction $STP)
$null = ([IO.Compression.ZipFile]::ExtractToDirectory($InstallerPackage, $InstallerDestination))
}
catch
{
# OK! That is crappy, but it still works well as a fallback.
$shellApp = (New-Object -ComObject Shell.Application -ErrorAction $STP)
$shellZip = $shellApp.NameSpace([String]$InstallerPackage)
$shellDest = $shellApp.NameSpace($InstallerDestination)
$shellDest.CopyHere($shellZip.items())
}
}
else
{
# get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
# output information. Post-process collected info, and log info (optional)
$info | Out-String | Write-Verbose
$paramWriteError = @{
Message = $e.Exception.Message
ErrorAction = $STP
Exception = $e.Exception
TargetObject = $e.CategoryInfo.TargetName
}
Write-Error @paramWriteError
# We are done
break
}
# Install FSLogix
$paramTestPath = @{
Path = $InstallerExecutable
ErrorAction = $SCT
}
if (Test-Path @paramTestPath)
{
$paramGetItemProperty = @{
Path = $InstallerExecutable
ErrorAction = $SCT
}
$InstallerVersion = ((Get-ItemProperty @paramGetItemProperty).VersionInfo.ProductVersion)
Write-Verbose -Message ('Running FSLogix installer version {0}' -f $InstallerVersion)
$paramStartProcess = @{
FilePath = $InstallerExecutable
ArgumentList = $Arguments
Wait = $true
PassThru = $true
ErrorAction = $STP
}
$InstallerProcess = (Start-Process @paramStartProcess)
if (($InstallerProcess | Select-Object -ExpandProperty ExitCode) -eq 0)
{
Write-Verbose -Message ('Installed FSLogix version {0}' -f $InstallerVersion)
}
else
{
Write-Warning -Message ('Installer exit code {0}.' -f $InstallerProcess.ExitCode)
}
Write-Verbose -Message ('Removing file: {0}' -f $InstallerPackage)
# Remove the downloaded Installaer Package
$paramRemoveItem = @{
Path = $InstallerPackage
Confirm = $false
Force = $true
ErrorAction = $SCT
}
$null = (Remove-Item @paramRemoveItem)
# Install the expanded stuff
$paramRemoveItem = @{
Path = $InstallerDestination
Recurse = $true
Confirm = $false
Force = $true
ErrorAction = $SCT
}
$null = (Remove-Item @paramRemoveItem)
# Legacy HKLM Path for WVD/VDI/WDS Environment
$paramNewItem = @{
Path = 'HKLM:\SOFTWARE\Citrix\PortICA'
Confirm = $false
Force = $true
ErrorAction = $SCT
}
$null = (New-Item @paramNewItem)
# Ensure that the registry path exists
$paramNewItem = @{
Path = 'HKLM:\SOFTWARE\Microsoft\Teams'
Confirm = $false
Force = $true
ErrorAction = $SCT
}
$null = (New-Item @paramNewItem)
# Tell Microsoft Teams that it runs in an WVD/VDI/WDS Environment
# Source: https://docs.microsoft.com/en-us/azure/virtual-desktop/teams-on-wvd
$paramNewItemProperty = @{
Path = 'HKLM:\SOFTWARE\Microsoft\Teams'
Name = 'IsWVDEnvironment'
PropertyType = 'DWORD'
Value = 1
Confirm = $false
Force = $true
ErrorAction = $SCT
}
$null = (New-ItemProperty @paramNewItemProperty)
# Ensure that the registry path exists
$paramNewItem = @{
Path = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\StartupApproved\Run32'
Confirm = $false
Force = $true
ErrorAction = $SCT
}
$null = (New-Item @paramNewItem)
# Do not start Microsoft Teams after Login
$paramNewItemProperty = @{
Path = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\StartupApproved\Run32'
Name = 'Teams'
PropertyType = 'Binary'
Value = ([byte[]](0x01, 0x00, 0x00, 0x00, 0x1a, 0x19, 0xc3, 0xb9, 0x62, 0x69, 0xd5, 0x01))
Confirm = $false
Force = $true
ErrorAction = $SCT
}
$null = (New-ItemProperty @paramNewItemProperty)
}
else
{
# get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
# output information. Post-process collected info, and log info (optional)
$info | Out-String | Write-Verbose
$paramWriteError = @{
Message = $e.Exception.Message
ErrorAction = $STP
Exception = $e.Exception
TargetObject = $e.CategoryInfo.TargetName
}
Write-Error @paramWriteError
# We are done
break
}
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,137 @@
#requires -Version 3.0 -Modules NetSecurity -RunAsAdministrator
<#
.SYNOPSIS
Tweak the Firewall Rules for Microsoft Teams clients
.DESCRIPTION
Tweak the Firewall Rules for Microsoft Teams clients for all users that have it installed
.NOTES
Early testing release
Changelog:
1.0.0: Initial Release
Version 1.0.0
.LINK
http://enatec.io
#>
[CmdletBinding(ConfirmImpact = 'Low',
SupportsShouldProcess)]
param ()
begin
{
Write-Output -InputObject 'Tweak the Firewall Rules for Microsoft Teams clients for all users that have it installed'
#region Defaults
$SCT = 'SilentlyContinue'
#endregion Defaults
}
process
{
# Creates firewall rules for Microsoft Teams
$AllUsers = $null
$paramJoinPath = @{
Path = $env:SystemDrive
ChildPath = 'Users'
ErrorAction = $SCT
}
$paramGetChildItem = @{
Path = (Join-Path @paramJoinPath)
ErrorAction = $SCT
Exclude = 'Public', 'ADMINI~*'
}
$AllUsers = (Get-ChildItem @paramGetChildItem)
if ($null -ne $AllUsers)
{
foreach ($SingleUser in $AllUsers)
{
# Cleanup
$FullTeamsPath = $null
# get the Executable
$paramJoinPath = @{
Path = $SingleUser.FullName
ChildPath = 'AppData\Local\Microsoft\Teams\Current\Teams.exe'
ErrorAction = $SCT
}
$FullTeamsPath = (Join-Path @paramJoinPath)
$paramTestPath = @{
Path = $FullTeamsPath
ErrorAction = $SCT
}
if (Test-Path @paramTestPath)
{
$paramGetNetFirewallApplicationFilter = @{
Program = $FullTeamsPath
ErrorAction = $SCT
}
if (-not (Get-NetFirewallApplicationFilter @paramGetNetFirewallApplicationFilter))
{
# Cleanup
$NetFirewallRuleName = $null
# Apply the Rulename
$NetFirewallRuleName = ('Teams.exe for user {0}' -f $SingleUser.Name)
'UDP', 'TCP' | ForEach-Object -Process {
$paramNewNetFirewallRule = @{
DisplayName = $NetFirewallRuleName
Direction = 'Inbound'
Profile = 'Any'
Program = $FullTeamsPath
Action = 'Allow'
Protocol = $_
Enabled = 'True'
Confirm = $false
ErrorAction = $SCT
}
$null = (New-NetFirewallRule @paramNewNetFirewallRule)
}
# Cleanup
$NetFirewallRuleName = $null
}
}
# Cleanup
$FullTeamsPath = $null
}
}
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,184 @@
function Invoke-mtrDisableModernAuthentication
{
<#
.SYNOPSIS
Disable Modern Authentication for a Microsoft Teams Room Device Account
.DESCRIPTION
Disable Modern Authentication for a Microsoft Teams Room Device Account
It dsables it in Exchange Online and Skype for Business Online. It also configures the tenant to do so, if needed.
.PARAMETER Identity
The Microsoft Teams Rooms (MTR) Account Search String
.EXAMPLE
PS C:\> .\Invoke-mtrDisableModernAuthentication.ps1 -Identity 'MyTeamRoom'
.EXAMPLE
PS C:\> .\Invoke-mtrDisableModernAuthentication.ps1 -Identity 'TeamRoom@contoso.com'
.NOTES
Just a quick and dirty tool to do the job, nothing fancy and without a real error handling!
-> Use at your own risk!
You need to be a tenant admin to configure all the things
#>
[CmdletBinding(ConfirmImpact = 'Low')]
param
(
[Parameter(Mandatory, HelpMessage = 'The Microsoft Teams Rooms (MTR) Account Search String',
ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 0)]
[ValidateNotNullOrEmpty()]
[Alias('SearchString', 'mtrAccount')]
[string]
$Identity
)
begin
{
#region Defaults
$STP = 'Stop'
$SCT = 'SilentlyContinue'
#endregion Defaults
#region GeneralParameters
$RemovePSSessionDefaultParams = @{
Confirm = $false
ErrorAction = $SCT
}
$RemoveModuleDefaultParams = @{
Force = $true
Confirm = $false
ErrorAction = $SCT
}
#endregion GeneralParameters
Write-Verbose -Message 'Message'
}
process
{
#region ConnectAzureAD
$null = (Connect-AzureAD)
#endregion ConnectAzureAD
#region ConnectSkypeForBusinessOnline
# We use a crappy workaround, because the Modern Auth window never shows up to querry the admin UPN, and I do NOT trust the command to querry it
$SkypeForBusinessSession = (New-CsOnlineSession -UserName (Read-Host -Prompt 'Please enter the admin principal name (ex. admin@contoso.com)'))
$paramImportPSSession = @{
Session = $SkypeForBusinessSession
DisableNameChecking = $true
AllowClobber = $true
}
$null = (Import-PSSession @paramImportPSSession)
#endregion ConnectSkypeForBusinessOnline
#region ConnectExchangeOnline
# We use the ExchangeOnlineShell Module from the Gallery
if (-not (Get-Command -Name Get-Mailbox -ErrorAction $SCT))
{
$paramConnectExchangeOnlineShell = @{
Confirm = $false
WarningAction = $SCT
ErrorAction = $STP
}
$null = (Connect-ExchangeOnlineShell @paramConnectExchangeOnlineShell)
}
#endregion ConnectExchangeOnline
#region CheckModernAuth
# Do we have Modern Auth enabled Global?
if ((Get-OrganizationConfig | Select-Object -ExpandProperty OAuth2ClientProfileEnabled) -eq $true)
{
# Disconnect Modern Authentication (For a single user) - In this case the MTR
$paramRevokeAzureADUserAllRefreshToken = @{
ObjectId = (Get-AzureADUser -SearchString $Identity | Select-Object -ExpandProperty objectId)
ErrorAction = $SCT
}
$null = (Revoke-AzureADUserAllRefreshToken @paramRevokeAzureADUserAllRefreshToken)
$null = (Revoke-AzureADUserAllRefreshToken @paramRevokeAzureADUserAllRefreshToken)
# Allow non Modern Auth in Skype for Business
if ((Get-CsOAuthConfiguration -ErrorAction $SCT | Select-Object -ExpandProperty ClientAdalAuthOverride) -ne 'Allowed')
{
$paramSetCsOAuthConfiguration = @{
ClientAdalAuthOverride = 'Allowed'
Confirm = $false
ErrorAction = $SCT
}
$null = (Set-CsOAuthConfiguration @paramSetCsOAuthConfiguration)
}
}
else
{
# Shame on you!
Write-Warning -Message 'Looks like Modern Auth is not enabled for this tenant!' -WarningAction $STP
}
#endregion CheckModernAuth
#region DisconnectAzureAD
$null = (Disconnect-AzureAD -Confirm:$false -ErrorAction $SCT)
#endregion DisconnectAzureAD
#region DisconnectSkypeForBusiness
$paramRemoveModule = @{
Name = (Get-Command -Name Set-CsOAuthConfiguration -ErrorAction $SCT | Select-Object -ExpandProperty Source)
Force = $true
ErrorAction = $SCT
}
$null = (Remove-Module @paramRemoveModule)
$null = ($SkypeForBusinessSession.Id | Remove-PSSession @RemovePSSessionDefaultParams)
#endregion DisconnectSkypeForBusiness
#region DisconnectExchangeOnline
$ExchangeSessionID = (Get-PSSession | Where-Object {
$_.ComputerName -eq 'outlook.office365.com'
} | Select-Object -ExpandProperty Id)
if ($ExchangeSessionID)
{
$paramDisconnectExchangeOnlineShell = @{
SessionID = $ExchangeSessionID
Confirm = $false
}
$null = (Disconnect-ExchangeOnlineShell @paramDisconnectExchangeOnlineShell)
}
# Will be removed soon (Disconnect-ExchangeOnlineShell will handle this for us!)
$RemoveModuleName = (Get-Command -Name Get-OrganizationConfig -ErrorAction $SCT | Select-Object -ExpandProperty Source)
if ($RemoveModuleName)
{
$paramRemoveModule = @{
Name = $RemoveModuleName
Force = $true
ErrorAction = $SCT
}
$null = (Remove-Module @RemoveModuleDefaultParams)
}
#endregion DisconnectExchangeOnline
}
end
{
#region FinalCleanup
# Just in case: We remove all sessions that might still be around
$null = ((Get-PSSession -ErrorAction $SCT | Where-Object {
$_.ComputerName -eq 'outlook.office365.com'
}) | Remove-PSSession @RemovePSSessionDefaultParams)
$null = ((Get-PSSession -ErrorAction $SCT | Where-Object {
$_.ComputerName -like 'admin*.online.lync.com'
}) | Remove-PSSession @RemovePSSessionDefaultParams)
# Remove the Modules (Here just in case we missed something above)
$null = (Remove-Module -Name (Get-Command -Name Connect-ExchangeOnlineShell -ErrorAction $SCT | Select-Object -ExpandProperty Source) @RemoveModuleDefaultParams)
$null = (Remove-Module -Name (Get-Command -Name Disconnect-AzureAD -ErrorAction $SCT | Select-Object -ExpandProperty Source) @RemoveModuleDefaultParams)
$null = (Remove-Module -Name (Get-Command -Name New-CsOnlineSession -ErrorAction $SCT | Select-Object -ExpandProperty Source) @RemoveModuleDefaultParams)
#endregion FinalCleanup
}
}

View File

@@ -0,0 +1,29 @@
BSD 3-Clause License
Copyright (c) 2021, enabling Technology <http://enatec.io>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -0,0 +1,448 @@
#requires -Version 3.0
<#
.SYNOPSIS
Creates a new Microsoft Teams team with the MicrosoftTeams Module.
.DESCRIPTION
Creates a new Microsoft Teams team with the MicrosoftTeams Module.
The new team will be backed by a newly created unified group and SharePoint Online Site.
The script depends on Microsoft's Version 0.9.6 of the MicrosoftTeams Module.
Please note: Not all authentications methods of the latest MicrosoftTeams Module are supported!
.PARAMETER msTeamsCreds
Specifies a PSCredential object. For more information about the PSCredential object, type Get-Help Get-Credential.
The PSCredential object provides the user ID and password for organizational ID credentials.
.PARAMETER mfa
Use the web based authentication. Supports MFA and prevents issues in non ADFS implementations.
.PARAMETER DisplayName
Todeam display name. Team Name Characters Limit is 256.
.PARAMETER Alias
Same as displayName without any spaces. Team Alias Characters Limit is 64
.PARAMETER Description
Team description. Team Description Characters Limit is 1024.
.PARAMETER AccessType
Team access type. Valid values are "Private" and "Public". Default is "Private". (This parameter has the same meaning as -AccessType in New-UnifiedGroup.)
.PARAMETER AddCreatorAsMember
This setting lets you decide if you will be added as a member of the team. The default is false.
.PARAMETER Owner
UPN/Mail of the Teams Owner, multiple values are supported!
.PARAMETER User
member Users for the Group, Please use UPN or Mail.
Multiple values are supported.
.EXAMPLE
PS C:\> .\New-AITMicrosoftTeams -mfa -DisplayName 'Contoso Support'
Creates the Microsoft Team 'Contoso Support'. Uses Weblog (supports MFA)
.EXAMPLE
PS C:\> .\New-AITMicrosoftTeams -msTeamsCreds $O365 -DisplayName 'Contoso Support'
Creates the Microsoft Team 'Contoso Support'. Uses existing credentials stored in the variable $O365 to authenticate.
This might be the perfect way for automation, but use stored credentials might also be insecure.
.EXAMPLE
PS C:\> .\New-AITMicrosoftTeams -DisplayName 'Contoso Support'
Creates the Microsoft Team 'Contoso Support'
.EXAMPLE
PS C:\> .\New-AITMicrosoftTeams -DisplayName 'Contoso Support' -Alias 'AITSupport'
Creates the Microsoft Team 'Contoso Support' with an Alias 'AITSupport'
.EXAMPLE
PS C:\> .\New-AITMicrosoftTeams -DisplayName 'Contoso Info-pool' -AccessType 'public'
Creates the Microsoft Team 'Contoso Info-pool', public mean open to join for every member of the organization.
.EXAMPLE
PS C:\> .\New-AITMicrosoftTeams -DisplayName 'Contoso Development' -Description 'Contoso IT Development Team' -Owner 'john.doe@acontoso.com'
Creates the Microsoft Team 'Contoso Development', sets a description and add 'john.doe@contoso.com' as Owner.
.EXAMPLE
PS C:\> .\New-AITMicrosoftTeams -DisplayName 'Contoso Core Dev' -AddCreatorAsMember $true
Creates the Microsoft Team 'Contoso Core Dev' and adds the creator to the new Team.
.EXAMPLE
PS C:\> Install-Module -Name MicrosoftTeams
Install the dependency Module from Microsoft via PowerShellGet.
.NOTES
Releasenotes:
1.0.3 2019-04-26: Fix Module Statement to use the correct version (0.9.6) to avoid issues with our workaround.
1.0.2 2019-02-05: Reintroduce the -MFA switch to support the web based authentication. Prevent issues in non ADFS implementations.
1.0.1 2019-02-04: Add workaround for AddCreatorAsMember Bug (Creator is added as owner all the time)
1.0.0 2018-12-31: Internal Release
THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER.
Dependencies:
The script depends on Microsoft's Version 0.9.6 of the MicrosoftTeams PowerShell Module.
The MicrosoftTeams PowerShell Module GA Version (1.0.0) is not yet tested!
Install it with PowerShellGet:
PS C:\> Install-Module -Name MicrosoftTeams -RequiredVersion 0.9.6
.LINK
https://www.powershellgallery.com/packages/MicrosoftTeams/0.9.6
.LINK
https://aka.ms/InstallModule
#>
[CmdletBinding(DefaultParameterSetName = 'MFA',
ConfirmImpact = 'None')]
param
(
[Parameter(ParameterSetName = 'Credentials',
ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 0)]
[System.Management.Automation.Credential()]
[Alias('TeamsCredentials', 'TeamsAdminCredentials', 'Office365creds')]
[pscredential]
$msTeamsCreds,
[Parameter(ParameterSetName = 'MFA',
ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 0)]
[Alias('UseMFA')]
[switch]
$mfa,
[Parameter(Mandatory,
ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 1,
HelpMessage = 'Team display name.')]
[ValidateNotNullOrEmpty()]
[Alias('TeamsDisplayName')]
[string]
$DisplayName,
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 2)]
[Alias('TeamsAlias')]
[string]
$Alias,
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 3)]
[Alias('TeamsDescription')]
[string]
$Description,
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 4)]
[ValidateSet('HiddenMembership', 'Private', 'Public', IgnoreCase = $true)]
[Alias('TeamsAccessType')]
[string]
$AccessType = 'Private',
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 5)]
[Alias('AddCreatorAsTeamsMember')]
[switch]
$AddCreatorAsMember = $false,
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 6)]
[Alias('TeamsOwner')]
[string[]]
$Owner,
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 7)]
[Alias('TeamsUser', 'TeamsMember')]
[string[]]
$User
)
begin
{
#region VersionRequirement
try
{
$paramRemoveModule = @{
Name = 'MicrosoftTeams'
Force = $true
ErrorAction = 'SilentlyContinue'
WarningAction = 'SilentlyContinue'
}
$null = (Remove-Module @paramRemoveModule)
$paramImportModule = @{
Name = 'MicrosoftTeams'
MaximumVersion = '0.9.6'
Force = $true
ErrorAction = 'Stop'
WarningAction = 'SilentlyContinue'
}
$null = (Import-Module @paramImportModule)
}
catch
{
$paramWriteError = @{
Message = 'Microsoft´s Version 0.9.6 of the MicrosoftTeams PowerShell Module'
ErrorAction = 'Stop'
Category = 'NotInstalled'
Exception = 'Required Module not found'
RecommendedAction = 'Please install Version 0.9.6 of the MicrosoftTeams PowerShell Module via Install-Module -Name MicrosoftTeams -RequiredVersion 0.9.6'
}
Write-Error @paramWriteError
# Only here to catch a global ErrorAction overwrite
break
}
#endregion VersionRequirement
#region AuthChecker
if (($msTeamsCreds) -and ($mfa))
{
$paramWriteError = @{
Message = 'You have selected muliple authentication methods. This is not valid'
Exception = 'Muliple authentication methods selected'
Category = 'AuthenticationError'
ErrorAction = 'Stop'
}
Write-Error @paramWriteError
# Only here to catch a global ErrorAction overwrite
break
}
#endregion AuthChecker
#region Defaults
#region AccessType
if (-not ($AccessType))
{
$AccessType = 'Private'
}
#endregion AccessType
#region AddCreatorAsMember
if (-not ($AddCreatorAsMember))
{
$AddCreatorAsMember = $false
}
#endregion AddCreatorAsMember
#endregion defaults
}
process
{
if ($pscmdlet.ShouldProcess($DisplayName, 'Create'))
{
try
{
#region Authentication
if (-not ($mfa))
{
#region CredentialHandler
if (-not ($msTeamsCreds))
{
# Get the credentials / Use it within the script only
$script:msTeamsCreds = (Get-Credential -Message 'Please use credentials with Teams Admin capabilities.')
}
#endregion CredentialHandler
#region ConnectMicrosoftTeams
$paramConnectMicrosoftTeams = @{
Credential = $msTeamsCreds
Confirm = $false
ErrorAction = 'Stop'
}
$null = (Connect-MicrosoftTeams @paramConnectMicrosoftTeams)
#endregion ConnectMicrosoftTeams
}
else
{
#region ConnectMicrosoftTeams
# Use the Web login
$paramConnectMicrosoftTeams = @{
Confirm = $false
ErrorAction = 'Stop'
}
$null = (Connect-MicrosoftTeams @paramConnectMicrosoftTeams)
#endregion ConnectMicrosoftTeams
}
#endregion Authentication
#region NewTeam
#region SplatDefaults
$paramNewTeam = @{
DisplayName = $DisplayName
Visibility = $AccessType
ErrorAction = 'Stop'
}
#endregion SplatDefaults
#region Optionals
if (($msTeamsCreds.UserName) -and ($AddCreatorAsMember -eq $true))
{
$paramNewTeam | Add-Member -MemberType NoteProperty -Name Owner -Value $msTeamsCreds.UserName
}
if ($Alias)
{
$paramNewTeam | Add-Member -MemberType NoteProperty -Name Alias -Value $Alias
}
if ($Description)
{
$paramNewTeam | Add-Member -MemberType NoteProperty -Name Description -Value $Description
}
#region Optionals
#region CreateTeam
$NewTeam = (New-Team @paramNewTeam)
#endregion CreateTeam
if (-not ($NewTeam.GroupId))
{
Write-Error -Message ('Error while try to create {0}' -f $DisplayName)
}
else
{
Write-Verbose -Message "The new Team id is $($NewTeam.GroupId)"
#region BugWorkAround
#BUG: There is a bug in the AddCreatorAsMember implemntation of Microsoft
if ($AddCreatorAsMember -eq $false)
{
Write-Verbose -Message 'Workaround: Workaround for the AddCreatorAsMember of the Microsoft MicrosoftTeams Module'
Remove-TeamUser -GroupId $NewTeam.GroupId -User $msTeamsCreds.UserName -ErrorAction SilentlyContinue
}
#endregion BugWorkAround
#region SetOwner
if ($Owner)
{
foreach ($Admin in $Owner)
{
try
{
$paramAddTeamUser = @{
GroupId = $NewTeam.GroupId
User = $Admin
Role = 'Owner'
ErrorAction = 'Stop'
}
$null = (Add-TeamUser @paramAddTeamUser)
}
catch
{
Write-Warning -Message ('Unable to add {0} as owner to the Team {1}' -f $Admin, $DisplayName)
}
}
}
else
{
Write-Warning -Message ('The Team {0} has no owner!' -f $DisplayName)
}
#endregion SetOwner
#region Setmember
if ($User)
{
foreach ($Member in $User)
{
try
{
$paramAddTeamUser = @{
GroupId = $NewTeam.GroupId
User = $Member
Role = 'Member'
ErrorAction = 'Stop'
}
$null = (Add-TeamUser @paramAddTeamUser)
}
catch
{
Write-Warning -Message ('Unable to add {0} as member to the Team {1}' -f $Member, $DisplayName)
}
}
}
#endregion Setmember
}
#endregion NewTeam
}
catch
{
#region ErrorHandler
# get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
$info | Out-String | Write-Verbose
Write-Error -Message ($info.Exception) -ErrorAction Stop
# Only here to catch a global ErrorAction overwrite
break
#endregion ErrorHandler
}
finally
{
#region Cleanup
$null = (Disconnect-MicrosoftTeams -Confirm:$false)
#endregion Cleanup
}
}
}
end
{
Write-Verbose -Message ('Created the Team {0}' -f $DisplayName)
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,719 @@
<#
.SYNOPSIS
Create a Project team in Teams with folder structure in files tab
.TeamsDescription
Create a Project team in Teams with folder structure in files tab
Based on Alexander Holmeset's version.
.DESCRIPTION
A detailed description of the file.
.PARAMETER TeamName
Name of the Microsoft Teams team
.PARAMETER TeamsOwner
TeamsOwner of the new Microsoft Teams team
.PARAMETER privatepublic
Os it a private or Public team?
.PARAMETER TeamsDescription
The TeamsDescription for the new team
.PARAMETER ClientId
Azure AD Application (client) ID
.PARAMETER TenantId
Azure AD Tenant ID
.PARAMETER ClientSecret
Azure AD secret
.PARAMETER TenantName
Office 365 Tenant Name (e.g. contoso for https://contoso.sharepoint.com)
.PARAMETER DocumentLibrary
Document Library Folder, default is /shared documents
.EXAMPLE
PS C:\> .\ProjectTeamStructure.ps1 -TeamName 'Value1' -TeamsOwner 'Value2'
.NOTES
Original found in Alexander Holmeset's Blog
My version starts to make it a bit more flexible (e.g. more parameters)
I might update this to be more configurable in the future
.LINK
https://alexholmeset.blog/2019/05/01/project-team-in-teams-with-folder-structure-in-files-tab/
.LINK
https://gist.githubusercontent.com/AlexanderHolmeset/d447cd7c24dd91c3275ad17a5091f0ed/raw/79478f1e789ab36a9236f21a3161685091b12e62/ProjectTeamStructure.ps1
#>
[CmdletBinding(ConfirmImpact = 'Low')]
param
(
[Parameter(Mandatory,
ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 0,
HelpMessage = 'Name of the Microsoft Teams team')]
[Parameter (Mandatory)]
[ValidateNotNullOrEmpty()]
[Alias('Name')]
[String]
$TeamName,
[Parameter(Mandatory,
ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 1,
HelpMessage = 'Owner of the new Microsoft Teams team')]
[Parameter (Mandatory)]
[ValidateNotNullOrEmpty()]
[Alias('Owner')]
[String]
$TeamsOwner,
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 2)]
[Parameter (Mandatory)]
[ValidateNotNullOrEmpty()]
[String]
$privatepublic = 'Public',
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 3)]
[Parameter (Mandatory)]
[Alias('description')]
[String]
$TeamsDescription,
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 4)]
[Alias('OAuthClientId')]
[string]
$ClientId,
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 5)]
[Alias('OAuthTenantId')]
[string]
$TenantId,
[Parameter(Position = 6)]
[Alias('OAuthClientSecret')]
[string]
$ClientSecret,
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 7)]
[string]
$TenantName = 'contoso',
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 8)]
[string]
$DocumentLibrary = '/shared documents'
)
begin
{
<#
# Azure AD OAuth Application Token for Graph API
# Get OAuth token for a AAD Application (returned as $token)
# Application (client) ID, tenant ID and secret
$ClientId = 'xxxxxxxxxxxxxxxxxxxxxxxx'
$TenantId = 'xxxxxxxxxxxxxxxxxxxxxxxx'
$ClientSecret = 'xxxxxxxxxxxxxxxxxxxxxxxx'
#>
}
process
{
# Get the credentials to use
$Cred = (Get-Credential)
# Connect to Exchange Online
$paramNewPSSession = @{
ConfigurationName = 'Microsoft.Exchange'
ConnectionUri = 'https://outlook.office365.com/powershell-liveid'
Credential = $Cred
Authentication = 'Basic'
AllowRedirection = $true
}
$Session = (New-PSSession @paramNewPSSession)
$paramImportPSSession = @{
Session = $Session
DisableNameChecking = $true
AllowClobber = $true
}
$null = (Import-PSSession @paramImportPSSession)
# Connect to Microsoft Teams
$null = (Connect-MicrosoftTeams -Credential $Cred)
# Contruct URI
$uri = 'https://login.microsoftonline.com/' + $TenantId + '/oauth2/v2.0/token'
# Construct the JSON Body
$body1 = @{
client_id = $ClientId
scope = 'https://graph.microsoft.com/.default'
client_secret = $ClientSecret
grant_type = 'client_credentials'
}
try
{
# Get OAuth 2.0 Token
$paramInvokeWebRequest = @{
Method = 'Post'
Uri = $uri
ContentType = 'application/x-www-form-urlencoded'
Body = $body1
ErrorAction = 'Stop'
UseBasicParsing = $true
}
$tokenRequest = (Invoke-WebRequest @paramInvokeWebRequest)
}
catch
{
# get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
# output information. Post-process collected info, and log info (optional)
$info | Out-String | Write-Verbose
$paramWriteError = @{
Message = $e.Exception.Message
ErrorAction = 'Stop'
Exception = $e.Exception
TargetObject = $e.CategoryInfo.TargetName
}
Write-Error @paramWriteError
}
# Extract the Token
$token = (($tokenRequest.Content | ConvertFrom-Json).access_token)
# Get ID of team requester and set as owner.
$uri = 'https://graph.microsoft.com/beta/users/' + $TeamsOwner + '?$select=id'
$method = 'GET'
try
{
$paramInvokeWebRequest = @{
Method = $method
Uri = $uri
ContentType = 'application/json'
Headers = @{
Authorization = 'Bearer ' + $token
}
ErrorAction = 'Stop'
UseBasicParsing = $true
}
$query = (Invoke-WebRequest @paramInvokeWebRequest)
}
catch
{
# get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
# output information. Post-process collected info, and log info (optional)
$info | Out-String | Write-Verbose
$paramWriteError = @{
Message = $e.Exception.Message
ErrorAction = 'Stop'
Exception = $e.Exception
TargetObject = $e.CategoryInfo.TargetName
}
Write-Error @paramWriteError
}
# Extract the ID
$ownerID = (($query.content | ConvertFrom-Json).id)
# Specify the URI to call and method
$uri = 'https://graph.microsoft.com/beta/teams'
$method = 'Post'
# Construct the JSON Body
<#
Please review this defaults,
these settings are applied to the new Microsoft Teams team!
#>
$body = @"
{
"template@odata.bind": "https://graph.microsoft.com/beta/teamsTemplates/standard",
"displayName": "$TeamName",
"description": "$TeamsDescription",
"channels": [
{
"displayName": "01-Management",
"isFavoriteByDefault": true,
"description": "Description"
},
{
"displayName": "02-Developement",
"isFavoriteByDefault": true,
"description": "DEscription"
},
{
"displayName": "03-Marketing",
"isFavoriteByDefault": true,
"description": "Description"
},
{
"displayName": "04-Finance",
"isFavoriteByDefault": true,
"description": "Description"
}
],
"memberSettings": {
"allowCreateUpdateChannels": true,
"allowDeleteChannels": false,
"allowAddRemoveApps": true,
"allowCreateUpdateRemoveTabs": true,
"allowCreateUpdateRemoveConnectors": true
},
"guestSettings": {
"allowCreateUpdateChannels": false,
"allowDeleteChannels": false
},
"funSettings": {
"allowGiphy": true,
"giphyContentRating": "Moderate",
"allowStickersAndMemes": true,
"allowCustomMemes": true
},
"messagingSettings": {
"allowUserEditMessages": true,
"allowUserDeleteMessages": true,
"allowOwnerDeleteMessages": true,
"allowTeamMentions": true,
"allowChannelMentions": true
},
"visibility": "$Private",
"owners@odata.bind": [
"https://graph.microsoft.com/beta/users('$ownerID')"
]
}
"@
try
{
$paramInvokeWebRequest = @{
Method = $method
Uri = $uri
ContentType = 'application/json'
Body = $body
Headers = @{
Authorization = 'Bearer ' + $token
}
ErrorAction = 'Stop'
UseBasicParsing = $true
}
$query = (Invoke-WebRequest @paramInvokeWebRequest)
}
catch
{
# get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
# output information. Post-process collected info, and log info (optional)
$info | Out-String | Write-Verbose
$paramWriteError = @{
Message = $e.Exception.Message
ErrorAction = 'Stop'
Exception = $e.Exception
TargetObject = $e.CategoryInfo.TargetName
}
Write-Error @paramWriteError
}
# Extract the Data
$location = ($query.Headers).Location
$GroupID = $location.Substring(8, 36)
# Wait a minute to setup the stuff
Start-Sleep -Seconds 60
# Get the Mail info about the new team
$TeamSiteName = ((Get-Team -groupid $GroupID).MailNickName)
# Set some defaults
$SiteURL = 'https://' + $TenantName + '.sharepoint.com/sites/' + $TeamSiteName
$DocumentLibrary = '/shared documents'
# Channels
# Config Variables
$FolderNames = '01-Management', '02-Developement', '03-Marketing', '04-Finance'
# Relative URL of the Parent Folder
$RelativeURL = $DocumentLibrary
try
{
# Connect to PNP Online
$null = (Connect-PnPOnline -Url $SiteURL -Credentials $Cred)
#sharepoint online create folder powershell
foreach ($Folder in $FolderNames)
{
$paramAddPnPFolder = @{
Name = $Folder
Folder = $RelativeURL
ErrorAction = 'Stop'
}
$null = (Add-PnPFolder @paramAddPnPFolder)
Write-Verbose -Message ("New Folder '{0}' Added!" -f $Folder)
}
}
catch
{
# get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
# output information. Post-process collected info, and log info (optional)
$info | Out-String | Write-Verbose
$paramWriteError = @{
Message = $e.Exception.Message
ErrorAction = 'Continue'
Exception = $e.Exception
TargetObject = $e.CategoryInfo.TargetName
}
Write-Error @paramWriteError
}
# 01-Management
# Config Variables
$FolderNames = 'Meetings', 'Presentations'
$RelativeURL = $DocumentLibrary + '/01-management' #Relative URL of the Parent Folder
try
{
# Connect to PNP Online
$null = (Connect-PnPOnline -Url $SiteURL -Credentials $Cred)
# sharepoint online create folder powershell
foreach ($Folder in $FolderNames)
{
$paramAddPnPFolder = @{
Name = $Folder
Folder = $RelativeURL
ErrorAction = 'Stop'
}
$null = (Add-PnPFolder @paramAddPnPFolder)
Write-Verbose -Message ("New Folder '{0}' Added!" -f $Folder)
}
}
catch
{
# get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
# output information. Post-process collected info, and log info (optional)
$info | Out-String | Write-Verbose
$paramWriteError = @{
Message = $e.Exception.Message
ErrorAction = 'Continue'
Exception = $e.Exception
TargetObject = $e.CategoryInfo.TargetName
}
Write-Error @paramWriteError
}
# 02-Developement
# Config Variables
$FolderNames = 'Design', 'Specs', 'Labeling'
# Relative URL of the Parent Folder
$RelativeURL = $DocumentLibrary + '/02-Developement'
try
{
# Connect to PNP Online
$null = (Connect-PnPOnline -Url $SiteURL -Credentials $Cred)
# sharepoint online create folder powershell
foreach ($Folder in $FolderNames)
{
$paramAddPnPFolder = @{
Name = $Folder
Folder = $RelativeURL
ErrorAction = 'Stop'
}
$null = (Add-PnPFolder @paramAddPnPFolder)
Write-Verbose -Message ("New Folder '{0}' Added!" -f $Folder)
}
}
catch
{
# get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
# output information. Post-process collected info, and log info (optional)
$info | Out-String | Write-Verbose
$paramWriteError = @{
Message = $e.Exception.Message
ErrorAction = 'Continue'
Exception = $e.Exception
TargetObject = $e.CategoryInfo.TargetName
}
Write-Error @paramWriteError
}
# Subfolder
$FolderNames = 'Sketches', 'Requirements'
# Relative URL of the Parent Folder
$RelativeURL = $DocumentLibrary + '/02-Developement/Design'
try
{
# Connect to PNP Online
$null = (Connect-PnPOnline -Url $SiteURL -Credentials $Cred)
# sharepoint online create folder powershell
foreach ($Folder in $FolderNames)
{
$paramAddPnPFolder = @{
Name = $Folder
Folder = $RelativeURL
ErrorAction = 'Stop'
}
$null = (Add-PnPFolder @paramAddPnPFolder)
Write-Verbose -Message ("New Folder '{0}' Added!" -f $Folder)
}
}
catch
{
# get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
# output information. Post-process collected info, and log info (optional)
$info | Out-String | Write-Verbose
$paramWriteError = @{
Message = $e.Exception.Message
ErrorAction = 'Continue'
Exception = $e.Exception
TargetObject = $e.CategoryInfo.TargetName
}
Write-Error @paramWriteError
}
# 03-Marketing
# Config Variables
$FolderNames = 'Communication Brief', 'Competitor Review', 'Consumer Insights', 'Product FAQ', 'Product Information', 'Product Strategy'
# Relative URL of the Parent Folder
$RelativeURL = $DocumentLibrary + '/03-Marketing'
try
{
# Connect to PNP Online
$null = (Connect-PnPOnline -Url $SiteURL -Credentials $Cred)
# sharepoint online create folder powershell
foreach ($Folder in $FolderNames)
{
$paramAddPnPFolder = @{
Name = $Folder
Folder = $RelativeURL
ErrorAction = 'Stop'
}
$null = (Add-PnPFolder @paramAddPnPFolder)
Write-Verbose -Message ("New Folder '{0}' Added!" -f $Folder)
}
}
catch
{
# get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
# output information. Post-process collected info, and log info (optional)
$info | Out-String | Write-Verbose
$paramWriteError = @{
Message = $e.Exception.Message
ErrorAction = 'Continue'
Exception = $e.Exception
TargetObject = $e.CategoryInfo.TargetName
}
Write-Error @paramWriteError
}
# 04-Finance
# Config Variables
$FolderNames = 'Budget', 'Presentations'
# Relative URL of the Parent Folder
$RelativeURL = $DocumentLibrary + '/04-Finance'
try
{
# Connect to PNP Online
$null = (Connect-PnPOnline -Url $SiteURL -Credentials $Cred)
# sharepoint online create folder powershell
foreach ($Folder in $FolderNames)
{
$paramAddPnPFolder = @{
Name = $Folder
Folder = $RelativeURL
ErrorAction = 'Stop'
}
$null = (Add-PnPFolder @paramAddPnPFolder)
Write-Verbose -Message ("New Folder '{0}' Added!" -f $Folder)
}
}
catch
{
# get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
# output information. Post-process collected info, and log info (optional)
$info | Out-String | Write-Verbose
$paramWriteError = @{
Message = $e.Exception.Message
ErrorAction = 'Continue'
Exception = $e.Exception
TargetObject = $e.CategoryInfo.TargetName
}
Write-Error @paramWriteError
}
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,300 @@
<#
.SYNOPSIS
Remove the Wiki tab on Microsoft Teams teams
.DESCRIPTION
Remove the Wiki tab on Microsoft Teams teams
I like Teams, but I never use the Wiki within Teams.
Alexander Holmeset figured out a smart way to get rid of the Wiki tab.
.PARAMETER ClientId
Azure AD Application (client) ID
.PARAMETER TenantId
Azure AD Tenant ID
.PARAMETER ClientSecret
Azure AD secret
.EXAMPLE
PS C:\> .\RemoveWiki.ps1 -ClientId 'Value1' -TenantId 'Value2' -ClientSecret 'Value3'
.NOTES
Original found in Alexander Holmeset's Blog
My version starts to make it a bit more flexible (e.g. more parameters)
.LINK
https://alexholmeset.blog/2019/05/10/remove-the-wiki-tab/
.LINK
https://gist.github.com/AlexanderHolmeset/e40c7e9297ae9cc01cb832871a9ff770
#>
[CmdletBinding(ConfirmImpact = 'Low')]
param
(
[Parameter(Mandatory,
ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 0,
HelpMessage = 'Azure AD Application (client) ID')]
[ValidateNotNullOrEmpty()]
[Alias('OAuthClientId')]
[string]
$ClientId,
[Parameter(Mandatory,
ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 1,
HelpMessage = 'Azure AD Tenant ID')]
[ValidateNotNullOrEmpty()]
[Alias('OAuthTenantId')]
[string]
$TenantId,
[Parameter(Mandatory,
ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 2,
HelpMessage = 'Azure AD secret')]
[ValidateNotNullOrEmpty()]
[Alias('OAuthClientSecret')]
[string]
$ClientSecret
)
process
{
# Contruct URI
$uri = 'https://login.microsoftonline.com/' + $TenantId + '/oauth2/v2.0/token'
try
{
# Construct Body
$body1 = @{
client_id = $ClientId
scope = 'https://graph.microsoft.com/.default'
client_secret = $ClientSecret
grant_type = 'client_credentials'
}
# Get OAuth 2.0 Token
$paramInvokeWebRequest = @{
Method = 'Post'
Uri = $uri
ContentType = 'application/x-www-form-urlencoded'
Body = $body1
ErrorAction = 'Stop'
UseBasicParsing = $true
}
$tokenRequest = (Invoke-WebRequest @paramInvokeWebRequest)
}
catch
{
# get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
# output information. Post-process collected info, and log info (optional)
$info | Out-String | Write-Verbose
$paramWriteError = @{
Message = $e.Exception.Message
ErrorAction = 'Stop'
Exception = $e.Exception
TargetObject = $e.CategoryInfo.TargetName
}
Write-Error @paramWriteError
}
# Extract the Token
$token = ($tokenRequest.Content | ConvertFrom-Json).access_token
# Just in case
Write-Verbose -Message $token
try
{
# URI to call
$uri = 'https://graph.microsoft.com/v1.0/groups'
$paramInvokeRestMethod = @{
Method = 'GET'
Uri = $uri
ContentType = 'application/json'
Headers = @{
Authorization = 'Bearer ' + $token
}
ErrorAction = 'Stop'
}
$query = (Invoke-RestMethod @paramInvokeRestMethod)
}
catch
{
# get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
# output information. Post-process collected info, and log info (optional)
$info | Out-String | Write-Verbose
$paramWriteError = @{
Message = $e.Exception.Message
ErrorAction = 'Stop'
Exception = $e.Exception
TargetObject = $e.CategoryInfo.TargetName
}
Write-Error @paramWriteError
}
# Extract the Value
$groups = $query.value
foreach ($group in $groups)
{
try
{
if ($group.resourceProvisioningOptions -contains 'Team')
{
# Extract the ID
$id = $group.id
# Build the URI
$uri2 = 'https://graph.microsoft.com/v1.0/teams/' + $id + '/channels'
$paramInvokeRestMethod = @{
Method = 'Get'
Uri = $uri2
ContentType = 'application/json'
Headers = @{
Authorization = 'Bearer ' + $token
}
}
$query2 = (Invoke-RestMethod @paramInvokeRestMethod)
# Extract the Value
$Channels = $query2.value
foreach ($Channel in $Channels)
{
$id2 = $Channel.id
$uri3 = 'https://graph.microsoft.com/v1.0/teams/' + $id + '/channels/' + $id2 + '/tabs'
$paramInvokeRestMethod = @{
Method = 'Get'
Uri = $uri3
ContentType = 'application/json'
Headers = @{
Authorization = 'Bearer ' + $token
}
}
$query3 = (Invoke-RestMethod @paramInvokeRestMethod)
# Extract the Value
$tabs = $query3.value
# Find the Wiki Tab
$WikiTabs = ($tabs | Where-Object -FilterScript {
$_.displayname -eq 'Wiki'
})
if ($WikiTabs)
{
foreach ($wikitab in $WikiTabs)
{
# Extract the ID
$wikitabID = $wikitab.id
# Build the URI
$uri4 = 'https://graph.microsoft.com/v1.0/teams/' + $id + '/channels/' + $id2 + '/tabs/' + $wikitabID
$paramInvokeRestMethod = @{
Method = 'DELETE'
Uri = $uri4
ContentType = 'application/json'
Headers = @{
Authorization = 'Bearer ' + $token
}
}
$query4 = (Invoke-RestMethod @paramInvokeRestMethod)
Write-Verbose -Message $query4
Write-Output -InputObject 'wikitab removed'
}
}
}
}
}
catch
{
# get error record
[Management.Automation.ErrorRecord]$e = $_
# retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
# output information. Post-process collected info, and log info (optional)
$info | Out-String | Write-Verbose
$paramWriteError = @{
Message = $e.Exception.Message
ErrorAction = 'Continue'
Exception = $e.Exception
TargetObject = $e.CategoryInfo.TargetName
}
Write-Error @paramWriteError
}
}
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,131 @@
<#
.SYNOPSIS
Replace the Domain for all UnifiedGroups (and Microsoft Teams) Primary SMTP Address
.DESCRIPTION
Replace the Domain for all UnifiedGroups (and Microsoft Teams) Primary SMTP Address
.PARAMETER OldDomain
The old Domain (e.g. contoso.com)
.PARAMETER NewDomain
The new Domain (e.g. contoso.net)
.EXAMPLE
PS C:\> .\ReplaceDomainForAllUnifiedGroups.ps1 -OldDomain 'contoso.com' -NewDomain 'contoso.net'
Replace the Primary SMTP Addresses for all UnifiedGroups (and Microsoft Teams) that are in the domain 'contoso.com' with the someone in the Domain 'contoso.net'
e.g. if an old address was myTeam@contoso.com would end up as myTeam@contoso.new
.LINK
https://docs.microsoft.com/en-us/powershell/exchange/exchange-online/connect-to-exchange-online-powershell/connect-to-exchange-online-powershell?view=exchange-ps
.LINK
http://hochwald.net
.NOTES
Quick and dirty approach, without any real Error handling.
A friend asked me for a solution after a merger to replace all Primary SMTP Addresses and get rid of the old domain (legal requirement in this case)
You need be be connected to an Exchange Online Session (NOT part of this script).
#>
[CmdletBinding(ConfirmImpact = 'Low',
SupportsShouldProcess = $true)]
param
(
[Parameter(Mandatory = $true,
ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true)]
[ValidateNotNullOrEmpty()]
[Alias('DomainToReplace')]
[string]
$OldDomain,
[Parameter(Mandatory = $true,
ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true)]
[ValidateNotNullOrEmpty()]
[string]
$NewDomain
)
begin
{
$OldMailFilter = ('@' + $OldDomain)
# Cleanup
$AllUnifiedGroups = $null
}
process
{
$AllUnifiedGroups = (Get-UnifiedGroup | Where-Object -FilterScript {
$_.PrimarySmtpAddress -like ('*' + $OldMailFilter)
} | Select-Object -Property Identity, DisplayName, PrimarySmtpAddress)
if ($AllUnifiedGroups)
{
foreach ($item in $AllUnifiedGroups)
{
if ($item.PrimarySmtpAddress -like ('*' + $OldMailFilter))
{
$OldMailAddress = $null
$OldMailAddress = (($item).PrimarySmtpAddress)
$NewMailAddress = $null
$NewMailAddress = ($OldMailAddress.Replace($OldMailFilter, ('@' + $NewDomain)))
Write-Verbose -Message ('Replace: {0} with: {1}' -f $OldMailAddress, $NewMailAddress)
# Add the new Address
$null = (Set-UnifiedGroup -Identity (($item).Identity) -EmailAddresses: @{
Add = $NewMailAddress
} -Confirm:$false)
# Make new Address the primary SMTP address
$null = (Set-UnifiedGroup -Identity (($item).Identity) -PrimarySmtpAddress $NewMailAddress -Confirm:$false)
# Remove the old SMTP Address
$null = (Set-UnifiedGroup -Identity (($item).Identity) -EmailAddresses: @{
Remove = $OldMailAddress
} -Confirm:$false)
}
else
{
Write-Warning -Message ('Sorry, the PrimarySmtpAddress of {0} is not in {1}' -f $item.DisplayName, $OldDomain)
}
}
}
else
{
Write-Output -InputObject 'Nothing to do!!!'
}
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,210 @@
#requires -Version 3.0 -Modules NetQos -RunAsAdministrator
<#
.SYNOPSIS
Apply QoS Settings for Microsoft Teams
.DESCRIPTION
Apply Network Quality of Service (QoS) settings for Microsoft Teams.
.PARAMETER AppPathNameMatchCondition
Specifies the name by which an application is run, such as application.exe or %ProgramFiles%\application.exe application.
.EXAMPLE
PS C:\> .\Set-QoSForMicrosoftTeams.ps1
.EXAMPLE
PS C:\> .\Set-QoSForMicrosoftTeamsRoom.ps1 -AppPathNameMatchCondition 'Teams.exe'
.NOTES
Changelog:
1.0.0: Initial Release (Adopted from Set-QoSForMicrosoftTeamsRoomDevices.ps1)
Version 1.0.0
.LINK
Get-NetQosPolicy
.LINK
New-NetQosPolicy
.LINK
https://docs.microsoft.com/en-us/microsoftteams/qos-in-teams-clients
.LINK
http://enatec.io
#>
[CmdletBinding(ConfirmImpact = 'Low',
SupportsShouldProcess)]
param
(
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName)]
[Alias('AppName')]
[string]
$AppPathNameMatchCondition = 'Teams.exe'
)
begin
{
Write-Output -InputObject 'Apply Network Quality of Service (QoS) settings for Microsoft Teams'
#region Defaults
$CNT = 'Continue'
$STP = 'Stop'
$SCT = 'SilentlyContinue'
[string]$AppSharingPolicy = 'Microsoft Teams AppSharing'
[string]$VideoPolicy = 'Microsoft Teams Video'
[string]$AudioPoliy = 'Microsoft Teams Audio'
#endregion Defaults
}
process
{
if ($pscmdlet.ShouldProcess('QoS-Settings', 'Apply'))
{
#region Audio
$paramGetNetQosPolicy = @{
Name = $AudioPoliy
ErrorAction = $SCT
WarningAction = $SCT
}
if (-not (Get-NetQosPolicy @paramGetNetQosPolicy))
{
try
{
# Splat the parameters
$paramNewNetQosPolicy = @{
NetworkProfile = 'All'
IPSrcPortStartMatchCondition = 50000
IPSrcPortEndMatchCondition = 50019
DSCPAction = 46
IPProtocolMatchCondition = 'Both'
Name = $AudioPoliy
Confirm = $false
WarningAction = $CNT
ErrorAction = $STP
}
# Do we have an application name?
if ($AppPathNameMatchCondition)
{
$paramNewNetQosPolicy.Add('AppPathNameMatchCondition', $AppPathNameMatchCondition)
}
$null = (New-NetQosPolicy @paramNewNetQosPolicy)
}
catch
{
Write-Warning -Message ('Unable to apply {0} QoS Poliy' -f $AudioPoliy)
}
}
#endregion Audio
#region Video
$paramGetNetQosPolicy = @{
Name = $VideoPolicy
ErrorAction = $SCT
WarningAction = $SCT
}
if (-not (Get-NetQosPolicy @paramGetNetQosPolicy))
{
try
{
# Splat the parameters
$paramNewNetQosPolicy = @{
NetworkProfile = 'All'
IPSrcPortStartMatchCondition = 50020
IPSrcPortEndMatchCondition = 50039
DSCPAction = 34
IPProtocolMatchCondition = 'Both'
Name = $VideoPolicy
Confirm = $false
WarningAction = $CNT
ErrorAction = $STP
}
# Do we have an application name?
if ($AppPathNameMatchCondition)
{
$paramNewNetQosPolicy.Add('AppPathNameMatchCondition', $AppPathNameMatchCondition)
}
$null = (New-NetQosPolicy @paramNewNetQosPolicy)
}
catch
{
Write-Warning -Message ('Unable to apply {0} QoS Poliy' -f $VideoPolicy)
}
}
#endregion Video
#region AppSharing
$paramGetNetQosPolicy = @{
Name = $AppSharingPolicy
ErrorAction = $SCT
WarningAction = $SCT
}
if (-not (Get-NetQosPolicy @paramGetNetQosPolicy))
{
try
{
# Splat the parameters
$paramNewNetQosPolicy = @{
NetworkProfile = 'All'
IPSrcPortStartMatchCondition = 50040
IPSrcPortEndMatchCondition = 50059
DSCPAction = 28
IPProtocolMatchCondition = 'Both'
Name = $AppSharingPolicy
Confirm = $false
WarningAction = $CNT
ErrorAction = $STP
}
# Do we have an application name?
if ($AppPathNameMatchCondition)
{
$paramNewNetQosPolicy.Add('AppPathNameMatchCondition', $AppPathNameMatchCondition)
}
$null = (New-NetQosPolicy @paramNewNetQosPolicy)
}
catch
{
Write-Warning -Message ('Unable to apply {0} QoS Poliy' -f $AppSharingPolicy)
}
}
#endregion AppSharing
}
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,203 @@
#requires -Version 3.0 -Modules NetQos -RunAsAdministrator
<#
.SYNOPSIS
Apply QoS Settings for Microsoft Teams Room Devices
.DESCRIPTION
Apply Network Quality of Service (QoS) settings for Microsoft Teams Room Devices.
I use this script to deploy the QoS settings to MTR devices via Intune.
.PARAMETER AppPathNameMatchCondition
Specifies the name by which an application is run, such as application.exe or %ProgramFiles%\application.exe application.
.EXAMPLE
PS C:\> .\Set-QoSForMicrosoftTeamsRoomDevices.ps1
.EXAMPLE
PS C:\> .\Set-QoSForMicrosoftTeamsRoomDevices.ps1 -AppPathNameMatchCondition 'Teams.exe'
.NOTES
Idea based on a Twitter chat with @StaleHansen
Please ensure to check the Ports!
They must match you Teams Admin Centr (TAC) settings.
.LINK
Get-NetQosPolicy
.LINK
New-NetQosPolicy
.LINK
https://docs.microsoft.com/en-us/microsoftteams/qos-in-teams-clients
.LINK
https://twitter.com/StaleHansen/status/1294341225647083522
#>
[CmdletBinding(ConfirmImpact = 'Low',
SupportsShouldProcess)]
param
(
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName)]
[Alias('AppName')]
[string]
$AppPathNameMatchCondition = $null
)
begin
{
$AppSharingPolicy = 'MTR AppSharing'
$VideoPolicy = 'MTR Video'
$AudioPoliy = 'MTR Audio'
}
process
{
if ($pscmdlet.ShouldProcess('QoS-Settings', 'Apply'))
{
#region Audio
$paramGetNetQosPolicy = @{
Name = $AudioPoliy
ErrorAction = $SCT
WarningAction = $SCT
}
if (-not (Get-NetQosPolicy @paramGetNetQosPolicy))
{
try
{
# Splat the parameters
$paramNewNetQosPolicy = @{
NetworkProfile = 'All'
IPSrcPortStartMatchCondition = 50000
IPSrcPortEndMatchCondition = 50019
DSCPAction = 46
IPProtocolMatchCondition = 'Both'
Name = $AudioPoliy
Confirm = $false
WarningAction = $CNT
ErrorAction = $STP
}
# Do we have an application name?
if ($AppPathNameMatchCondition)
{
$paramNewNetQosPolicy.Add('AppPathNameMatchCondition', $AppPathNameMatchCondition)
}
$null = (New-NetQosPolicy @paramNewNetQosPolicy)
}
catch
{
Write-Warning -Message ('Unable to apply {0} QoS Poliy' -f $AudioPoliy)
}
}
#endregion Audio
#region Video
$paramGetNetQosPolicy = @{
Name = $VideoPolicy
ErrorAction = $SCT
WarningAction = $SCT
}
if (-not (Get-NetQosPolicy @paramGetNetQosPolicy))
{
try
{
# Splat the parameters
$paramNewNetQosPolicy = @{
NetworkProfile = 'All'
IPSrcPortStartMatchCondition = 50020
IPSrcPortEndMatchCondition = 50039
DSCPAction = 34
IPProtocolMatchCondition = 'Both'
Name = $VideoPolicy
Confirm = $false
WarningAction = $CNT
ErrorAction = $STP
}
# Do we have an application name?
if ($AppPathNameMatchCondition)
{
$paramNewNetQosPolicy.Add('AppPathNameMatchCondition', $AppPathNameMatchCondition)
}
$null = (New-NetQosPolicy @paramNewNetQosPolicy)
}
catch
{
Write-Warning -Message ('Unable to apply {0} QoS Poliy' -f $VideoPolicy)
}
}
#endregion Video
#region AppSharing
$paramGetNetQosPolicy = @{
Name = $AppSharingPolicy
ErrorAction = $SCT
WarningAction = $SCT
}
if (-not (Get-NetQosPolicy @paramGetNetQosPolicy))
{
try
{
# Splat the parameters
$paramNewNetQosPolicy = @{
NetworkProfile = 'All'
IPSrcPortStartMatchCondition = 50040
IPSrcPortEndMatchCondition = 50059
DSCPAction = 28
IPProtocolMatchCondition = 'Both'
Name = $AppSharingPolicy
Confirm = $false
WarningAction = $CNT
ErrorAction = $STP
}
# Do we have an application name?
if ($AppPathNameMatchCondition)
{
$paramNewNetQosPolicy.Add('AppPathNameMatchCondition', $AppPathNameMatchCondition)
}
$null = (New-NetQosPolicy @paramNewNetQosPolicy)
}
catch
{
Write-Warning -Message ('Unable to apply {0} QoS Poliy' -f $AppSharingPolicy)
}
}
#endregion AppSharing
}
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,128 @@
<#
.SYNOPSIS
Create a Microsoft Teams Room Device in Office 365
.DESCRIPTION
Create and setup a Microsoft Teams Room Device in Microsoft Office 365
.NOTES
Review the variable here.
You must be connected to the following Services:
- Exchange Online
- MSOL (Not AzureAD!)
- Skype for Business Online (Not Teams!)
.LINK
https://hochwald.net/microsoft-teams-room-device-1-2/
.LINK
https://hochwald.net/microsoft-teams-room-device-2-2/
#>
[CmdletBinding(ConfirmImpact = 'Low')]
param ()
# Display Name of the Room
$RoomName = 'Your-Teams-Room'
# Alias of the Room (For the UPN, SMTP, and SIP Address)
$RoomAlias = 'YourTeamsRoom'
# Keep this safe
$RoomPassword = 'YourSuperSecretRoomPassword'
<#
At the moment, the password for a room/resource will never expire!
So keep this in a safe place. And there is no second factor (MFA).
#>
# The Domain for the UPN, and the SMTP address
$RoomDomain = 'contoso.com'
# The Response text for meeting requests
$RoomAdditionalResponse = 'This is a Microsoft Teams Team Room'
<#
Basic HTML is supported here!
This text will be in the meeting respoinse mail, so use it as a info or teaser
#>
# The ALIAS of the license to apply.
# In this case it is the MEETING_ROOM License in the tenant with the name contoso
$RoomLicence = 'contoso:MEETING_ROOM'
<#
The license must be availible (Buy it before create the room)
#>
# We need one User that we use to find the SIP Registrar Pool (For Skype for Business and Teams SIP handling)
$CsOnlineUser = 'john.doe'
#region AutomatedStrings
# Build some strings
$RoomUserPrincipalName = ($RoomAlias + '@' + $RoomDomain)
$CsOnlineUserTemplate = ($CsOnlineUser + '@' + $RoomDomain)
<#
I use the same domain for the UPN and the SMTP/SIP address,
and I highly recommend you to do the same!
#>
#endregion AutomatedStrings
#region NewMailbox
# Create the Mailbox
$paramNewMailbox = @{
Name = $RoomName
Alias = $RoomAlias
Room = $true
EnableRoomMailboxAccount = $true
MicrosoftOnlineServicesID = $RoomUserPrincipalName
RoomMailboxPassword = (ConvertTo-SecureString -String $RoomPassword -AsPlainText -Force)
}
New-Mailbox @paramNewMailbox
#endregion NewMailbox
#region SetCalendarProcessing
# Tweak Calendar settings
$paramSetCalendarProcessing = @{
Identity = $RoomName
AutomateProcessing = 'AutoAccept'
AddOrganizerToSubject = $false
DeleteComments = $false
DeleteSubject = $false
RemovePrivateProperty = $false
AddAdditionalResponse = $true
AdditionalResponse = $RoomAdditionalResponse
}
Set-CalendarProcessing @paramSetCalendarProcessing
<#
Please review the parameters above!
They might not match your taste or requirements
You can add more: Use 'Get-Help Set-CalendarProcessing -details' to see all supported paramaters
#>
#endregion SetCalendarProcessing
#region SetMsolUser
# Usage location and password tweak
$paramSetMsolUser = @{
UserPrincipalName = $RoomUserPrincipalName
PasswordNeverExpires = $true
UsageLocation = 'DE'
}
Set-MsolUser @paramSetMsolUser
#endregion SetMsolUser
#region SetMsolUserLicense
# Apply the license
$paramSetMsolUserLicense = @{
UserPrincipalName = $RoomUserPrincipalName
AddLicenses = $RoomLicence
}
Set-MsolUserLicense @paramSetMsolUserLicense
#endregion SetMsolUserLicense
#region EnableCsMeetingRoom
# Enable SIP (Skype for Business/Teams)
$paramEnableCsMeetingRoom = @{
Identity = $RoomUserPrincipalName
RegistrarPool = (Get-CsOnlineUser -Identity $CsOnlineUserTemplate | Select-Object -ExpandProperty RegistrarPool)
SipAddressType = 'EmailAddress'
}
Enable-CsMeetingRoom @paramEnableCsMeetingRoom
#endregion EnableCsMeetingRoom

View File

@@ -0,0 +1,227 @@
function Update-UnifiedGroupsToTeams
{
<#
.SYNOPSIS
Converts all Microsoft Office 365 Groups into a new Microsoft Teams Team
.DESCRIPTION
Converts all Microsoft Office 365 Groups into a new Microsoft Teams Team
Microsoft Office 365 Groups are also known as Unified Office 365 Groups
.PARAMETER ReportOnly
Shows a list of Microsoft Office 365 Groups that would be migrated to a new Microsoft Teams Team.
This is a DryRun only!
.EXAMPLE
PS C:\> Update-UnifiedGroupsToTeams
Converting all Microsoft Office 365 Groups into a new Microsoft Teams Team
.EXAMPLE
PS C:\> Update-UnifiedGroupsToTeams -ReportOnly
Do a DryRun (Just get a List of Unified Groups that do NOT have a Microsoft Teams Team)
.EXAMPLE
PS C:\> Compare-Object -ReferenceObject ((Get-Team | Select-Object -ExpandProperty GroupId)) -DifferenceObject ((Get-UnifiedGroup -ResultSize Unlimited | Select-Object -ExpandProperty ExternalDirectoryObjectId)) -PassThru
Get a short difference list (this function is not required to do so)
.NOTES
Releasenotes:
1.0.1 2019-04-21: Add a bit more error handling
1.0.0 2018-04-14: Internal Release
THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER.
Dependencies:
The script depends on Microsoft's Version 0.9.6, or newer, of the MicrosoftTeams PowerShell Module
Install it with PowerShellGet:
PS C:\> Install-Module MicrosoftTeams
You need to be connected to Office 365 (Exchange Online). The function will check that.
.LINK
https://www.powershellgallery.com/packages/MicrosoftTeams/0.9.6
.LINK
https://aka.ms/InstallModule
#>
[CmdletBinding(ConfirmImpact = 'Low')]
param
(
[Parameter(ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 0)]
[Alias('DryRun')]
[switch]
$ReportOnly
)
begin
{
#region Defaults
$CNT = 'Continue'
$STP = 'Stop'
#endregion Defaults
try
{
#region ConnectionCheck
if (-not (Get-Command -Name Get-UnifiedGroup -ErrorAction SilentlyContinue))
{
$ErrorParameter = @{
Message = 'Please connect to Office 365/Exchange Online before using this function!'
Category = 'ResourceUnavailable'
RecommendedAction = 'Connect to Office 365/Exchange Online before using this function'
ErrorAction = $STP
}
Write-Error @ErrorParameter
}
#endregion ConnectionCheck
#region GetUnifiedGroups
$GetUnifiedGroupParameter = @{
ResultSize = 'Unlimited'
ErrorAction = $STP
WarningAction = $CNT
}
$AllOffice365UnifiedGroups = (Get-UnifiedGroup @GetUnifiedGroupParameter | Select-Object -Property DisplayName, ExternalDirectoryObjectId)
#endregion GetUnifiedGroups
#region GetMicrosoftTeams
$GetTeamParameter = @{
ErrorAction = $STP
WarningAction = $CNT
}
$AllMicrosoftTeams = (Get-Team @GetTeamParameter | Select-Object -ExpandProperty GroupId)
#endregion GetMicrosoftTeams
}
catch
{
# Get error record
[Management.Automation.ErrorRecord]$e = $_
# Retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
# Dump the FULL error record
Write-Warning -Message ($info | Out-String)
Write-Error -Message $info.Exception -Exception $info.Exception -ErrorAction $STP
break
}
}
process
{
if ($AllOffice365UnifiedGroups)
{
#region Loop
foreach ($Office365UnifiedGroup in $AllOffice365UnifiedGroups)
{
if (-not ($AllMicrosoftTeams -match $Office365UnifiedGroup.ExternalDirectoryObjectId))
{
if ($ReportOnly)
{
#region ReportOnly
$SingleOffice365UnifiedGroup = $Office365UnifiedGroup.DisplayName
Write-Output -InputObject ('Microsoft Teams for Unified Group {0} is missing' -f $SingleOffice365UnifiedGroup)
#endregion ReportOnly
}
else
{
#region CreateMissingTeam
Write-Verbose -Message ('Create Microsoft Teams Team for Unified Group {0}' -f $SingleOffice365UnifiedGroup)
try
{
$NewTeamParameter = @{
Group = $Office365UnifiedGroup
ErrorAction = $STP
WarningAction = $CNT
}
$NewTeam = (New-Team @NewTeamParameter)
Write-Debug -Message $NewTeam
Write-Verbose -Message ('Created Microsoft Teams Team for Unified Group {0}' -f $SingleOffice365UnifiedGroup)
}
catch
{
# Get error record
[Management.Automation.ErrorRecord]$e = $_
# Retrieve information about runtime error
$info = [PSCustomObject]@{
Exception = $e.Exception.Message
Reason = $e.CategoryInfo.Reason
Target = $e.CategoryInfo.TargetName
Script = $e.InvocationInfo.ScriptName
Line = $e.InvocationInfo.ScriptLineNumber
Column = $e.InvocationInfo.OffsetInLine
}
$TheException = $info.Exception
Write-Warning -Message ('Microsoft Teams creation for {0} failed with {1} ' -f $SingleOffice365UnifiedGroup, $TheException)
# Dump the FULL error record
Write-Verbose -Message ($info | Out-String)
}
#endregion CreateMissingTeam
}
}
}
#endregion Loop
}
else
{
Write-Warning -Message 'No Unified Groups found in your Tenant...'
}
}
end
{
Write-Verbose -Message 'Done.'
}
}
#region LICENSE
<#
BSD 3-Clause License
Copyright (c) 2021, enabling Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
#endregion LICENSE
#region DISCLAIMER
<#
DISCLAIMER:
- Use at your own risk, etc.
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
- This is a third-party Software
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
- The Software is not supported by Microsoft Corp (MSFT)
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
#>
#endregion DISCLAIMER

View File

@@ -0,0 +1,23 @@
# Use the latest Microsoft Teams PowerShell Module to connect
# Not the Skype for Business Online Module (outdated)
# Get all Teams Meeting Policies
Get-CsTeamsMeetingPolicy | Select-Object -ExpandProperty Identity
# Get all Teams Meeting Policies, exclude all TAG Policies (You can not modify them with Get-CsTeamsMeetingPolicy)
Get-CsTeamsMeetingPolicy | Where-Object -FilterScript {
$_.Identity -notlike 'Tag:*'
} | Select-Object -ExpandProperty Identity
# Modify the Global Policy
Set-CsTeamsMeetingPolicy -Identity Global -AllowEngagementReport Enabled
# Modify any Policy by name
Set-CsTeamsMeetingPolicy -Identity 'Meetings' | Set-CsTeamsMeetingPolicy -AllowEngagementReport Enabled
# Modify all Policies (exclude the TAG Policies, because you can not modify them with Get-CsTeamsMeetingPolicy)
Get-CsTeamsMeetingPolicy | Where-Object -FilterScript {
$_.Identity -notlike 'Tag:*'
} | ForEach-Object -Process {
Set-CsTeamsMeetingPolicy -Identity $_.Identity -AllowEngagementReport Enabled -ErrorAction Continue
}