├── .gitignore
├── Entra Connect
└── Start-FullPasswordSync.ps1
├── Exchange Legacy
└── README.md
├── Exchange Online
├── Add-CustomCalendarEvents
│ ├── Add-CustomCalendarEvents.ps1
│ ├── Add-EntraIdAppRegistration.ps1
│ ├── LICENSE.md
│ ├── README.md
│ ├── events.json
│ └── settings.xml
├── Disable-RemotePowerShell
│ ├── Disable-RemotePowerShell.ps1
│ ├── LICENSE
│ └── README.md
├── Move-MigrationUser
│ ├── LICENSE.md
│ ├── Move-MigrationUser.ps1
│ └── README.md
├── Send-MonitoringEmail
│ ├── Add-EntraIdAppRegistration.ps1
│ ├── LICENSE.md
│ ├── Monitoring Attachment.docx
│ ├── Monitoring Attachment.pdf
│ ├── README.md
│ ├── Send-MonitoringEmail.ps1
│ └── settings.xml
└── Test-DomainAvailability
│ ├── LICENSE.md
│ ├── README.md
│ └── Test-DomainAvailability.ps1
├── Exchange Server
├── Add-HybridSendConnector
│ └── Add-HybridSendConnector.ps1
├── Copy-ReceiveConnector
│ ├── Copy-ReceiveConnector.ps1
│ ├── LICENSE.md
│ └── README.md
├── Export-MessageQueue
│ ├── Export-MessageQueue.ps1
│ ├── LICENSE.md
│ └── README.md
├── Export-ModernPublicFolderStatistics
│ ├── Export-ModernPublicFolderStatistics.ps1
│ ├── Export-PublicFolderStatisticsToExcel.ps1
│ └── styles.css
├── Export-PublicFolderPermissions
│ ├── Export-PublicFolderPermissions.ps1
│ └── Report-PFPermissions.ps1
├── Get-ExchangeADVersion
│ └── Get-ExchangeADVersion.ps1
├── Get-ExchangeEnvironmentReport
│ ├── EnvironmentReport.css
│ ├── Get-ExchangeEnvironmentReport.ps1
│ ├── LICENSE.md
│ ├── README.md
│ ├── Run-ExchangeEnvironmentReport.ps1
│ └── images
│ │ └── screenshot.png
├── Get-ExchangeServerCertificateReport
│ ├── Get-ExchangeCertificateReport.ps1
│ ├── README.md
│ └── styles.css
├── Get-NewPublicFolders
│ ├── Get-NewPublicFolders.ps1
│ ├── LICENSE.md
│ ├── README.md
│ └── styles.css
├── Get-PublicFolderCustomFormItems
│ ├── Get-PublicFolderCustomFormItems.ps1
│ ├── LICENSE.md
│ └── README.md
├── Get-RemoteSmtpServers
│ ├── Get-RemoteSmtpServers.ps1
│ ├── Get-RemoteSmtpServersTls.ps1
│ ├── LICENSE.md
│ └── README.md
├── Import-EdgeSubscription
│ ├── Import-EdgeSubscription.ps1
│ └── README.md
├── New-RoomMailbox
│ ├── LICENSE.md
│ ├── New-RoomMailbox.ps1
│ ├── README.md
│ ├── Run-NewRoomMailbox.ps1
│ └── settings.xml
├── New-TeamMailbox
│ ├── Create-TeamMailbox.ps1
│ ├── LICENSE.md
│ ├── New-TeamMailbox.ps1
│ ├── README.md
│ └── settings.xml
├── Purge-LogFiles
│ ├── LICENSE.md
│ ├── Purge-LogFiles.ps1
│ ├── README.md
│ └── Run-PurgeLogFiles.ps1
├── README.md
├── Remove-ProxyAddress
│ └── Remove-ProxyAddress.ps1
├── Set-EdgeDNSSuffix
│ └── Set-EdgeDNSSuffix.ps1
├── Set-ExchangePreferredRegistrySettings
│ ├── Disable-IPv6-Correctly.reg
│ ├── RPC-TimeOut-PortScaling.reg
│ ├── Set-ExchangePreferredRegistrySettings.ps1
│ └── Tcpip-KeepAliveTime.reg
├── Set-PublicFolderPermissions
│ └── Set-PublicFolderPermissions.ps1
├── Set-UserPicture
│ ├── LICENSE.md
│ ├── README.md
│ ├── Set-UserPicture.ps1
│ └── UserStatus.xml
├── Set-VirtualDirectoryUrl
│ ├── LICENSE
│ ├── README.md
│ └── Set-VirtualDirectoryUrl.ps1
├── Start-MailboxImport
│ ├── LICENSE.md
│ ├── README.md
│ └── Start-MailboxImport.ps1
└── Test-ExchangeServerHealth
│ ├── LICENSE.md
│ ├── README.md
│ └── Test-ExchangeServerHealth.ps1
├── LICENSE
├── Misc
├── Get-Diskspace
│ ├── Get-Diskspace.ps1
│ ├── Get-Diskspace.ps1.txt
│ ├── LICENSE.md
│ ├── README.md
│ ├── Screenshot-Email.PNG
│ └── Screenshot-Shell.PNG
├── Get-SoftwareInventory
│ ├── Get-SoftwareInventory.ps1
│ ├── Get-SoftwareInventory.txt
│ ├── LICENSE.md
│ ├── README.md
│ ├── Run-GetSoftwareInventory.ps1
│ └── Run-GetSoftwareInventoryExchange.ps1
├── New-TestUser
│ ├── LICENSE.md
│ ├── New-TestUser.ps1
│ ├── README.md
│ └── TestUserNames.xlsx
└── Set-PageFileSize
│ └── Set-PageFileSize.ps1
├── Network
├── Test-DNSRecords
│ ├── Domains.txt
│ └── Test-DNSRecords.ps1
└── Test-NetworkAdapters
│ └── Test-NetworkAdapters.ps1
├── README-IMAGES
├── Add-CustomCalendarEvents-OWA.png
└── Add-CustomCalendarEvents-PowerShell.png
├── README.md
└── Script Template
└── PowerShell-Script-Template.ps1
/.gitignore:
--------------------------------------------------------------------------------
1 | customer*.*
2 | secrect*.*
3 | **/logs/*
4 |
5 |
--------------------------------------------------------------------------------
/Entra Connect/Start-FullPasswordSync.ps1:
--------------------------------------------------------------------------------
1 | # Name of Entra Connect AD connect
2 | $adConnector = "varunagroup.de"
3 |
4 | # Name ENtra Connect Entra connector
5 | $aadConnector = "varunagroup.onmicrosoft.com - AAD"
6 |
7 |
8 | Import-Module adsync
9 | $c = Get-ADSyncConnector -Name $adConnector
10 | $p = New-Object Microsoft.IdentityManagement.PowerShell.ObjectModel.ConfigurationParameter "Microsoft.Synchronize.ForceFullPasswordSync", String, ConnectorGlobal, $null, $null, $null
11 | $p.Value = 1
12 | $c.GlobalParameters.Remove($p.Name)
13 | $c.GlobalParameters.Add($p)
14 | $c = Add-ADSyncConnector -Connector $c
15 |
16 |
17 | Set-ADSyncAADPasswordSyncConfiguration -SourceConnector $adConnector -TargetConnector $aadConnector -Enable $false
18 | Set-ADSyncAADPasswordSyncConfiguration -SourceConnector $adConnector -TargetConnector $aadConnector -Enable $true
19 |
20 |
21 | Get-Mailbox -ResultSize Unlimited | Get-MailboxFolderStatistics -IncludeAnalysis -FolderScope All | Where-Object {(($_.TopSubjectSize -Match "MB") -and ($_.TopSubjectSize -GE 50.0)) -or ($_.TopSubjectSize -Match "GB")} | Select-Object Identity, TopSubject, TopSubjectSize | Export-CSV -path "C:\report.csv" -notype
--------------------------------------------------------------------------------
/Exchange Legacy/README.md:
--------------------------------------------------------------------------------
1 | # Exchange Server PowerShell Scripts
2 |
3 | This GitHub Repository contains most of my public PowerShell scripts for legacy Exchange Server versions (2010, 2007).
4 |
5 | Please refer to the table of content in the main [README](https://github.com/Apoc70/PowerShell-Scripts) file.
6 |
7 | ### Stay connected
8 |
9 | * Blog: [https://blog.granikos.eu](https://blog.granikos.eu)
10 | * Bluesky: [https://bsky.app/profile/stensitzki.eu](https://bsky.app/profile/stensitzki.eu)
11 | * LinkedIn: [https://www.linkedin.com/in/thomasstensitzki](https://www.linkedin.com/in/thomasstensitzki)
12 | * YouTube: [https://www.youtube.com/@ThomasStensitzki](https://www.youtube.com/@ThomasStensitzki)
13 | * LinkTree: [https://linktr.ee/stensitzki](https://linktr.ee/stensitzki)
14 |
15 | For more Microsoft 365, Cloud Security, and Exchange Server stuff checkout services provided by Granikos
16 |
17 | * Website: [https://granikos.eu](https://granikos.eu)
18 | * Bluesky: [https://bsky.app/profile/granikos.eu](https://bsky.app/profile/granikos.eu)
19 |
--------------------------------------------------------------------------------
/Exchange Online/Add-CustomCalendarEvents/Add-EntraIdAppRegistration.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | .SYNOPSIS
3 | Add a custom app registration to Entra ID for Microsoft Graph access
4 |
5 | Thomas Stensitzki
6 |
7 | THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE
8 | RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER.
9 |
10 | Version 1.0, 2024-06-27
11 |
12 | Based on the work of Andres Bohren
13 | https://blog.icewolf.ch/archive/2022/12/02/create-azure-ad-app-registration-with-microsoft-graph-powershell
14 |
15 | .NOTES
16 | Requirements
17 | - PowerShell 7.1+
18 | - Account executing this script must be a role member of the Application Administrator or Global Administrator role
19 |
20 | Revision History
21 | --------------------------------------------------------------------------------
22 | 1.0 Initial community release
23 |
24 | .PARAMETER AppName
25 |
26 | The display name of the application in Entra ID
27 |
28 | .PARAMETER AppSecretName
29 |
30 | The name of the client secret in Entra ID
31 |
32 | .PARAMETER AppOwnerEmailAddress
33 |
34 | The email address of the application owner
35 |
36 | #>
37 | [CmdletBinding()]
38 | param(
39 | [string]$AppName = 'CustomCalendarItems-Application',
40 | [string]$AppSecretName = 'AppClientSecret',
41 | [string]$AppOwnerEmailAddress = 'Admin@TENANT.onmicrosoft.com'
42 | )
43 |
44 | if ($null -ne (Get-Module -Name Microsoft.Graph.Authentication -ListAvailable).Version) {
45 | Import-Module -Name Microsoft.Graph.Authentication
46 | }
47 | else {
48 | Write-Warning -Message 'Unable to load Import-Module Microsoft.Graph.Authentication PowerShell module.'
49 | Write-Warning -Message 'Open an administrative PowerShell session and run Install-Module Microsoft.Graph'
50 | exit
51 | }
52 | if ($null -ne (Get-Module -Name Microsoft.Graph.Applications -ListAvailable).Version) {
53 | Import-Module -Name Microsoft.Graph.Applications
54 | }
55 | else {
56 | Write-Warning -Message 'Unable to load Import-Module Microsoft.Graph.Applications PowerShell module.'
57 | Write-Warning -Message 'Open an administrative PowerShell session and run Install-Module Microsoft.Graph'
58 | exit
59 | }
60 |
61 | # Connect to Microsoft Graph
62 | Connect-MgGraph -Scopes "Application.Read.All", "Application.ReadWrite.All", "User.Read.All" -NoWelcome
63 |
64 | # Create a new application
65 | $newApp= New-MgApplication -DisplayName $AppName -Notes 'Application for adding custom events to user calendars. This app is used by the Add-CustomCalendarItems.ps1 script.'
66 | $appObjectId = $newApp.Id
67 |
68 | # Set the owner of the application
69 | $User = Get-MgUser -UserId $AppOwnerEmailAddress
70 | $ObjectId = $User.ID
71 | $NewOwner = @{
72 | "@odata.id" = "https://graph.microsoft.com/v1.0/directoryObjects/{$ObjectId}"
73 | }
74 | $null = New-MgApplicationOwnerByRef -ApplicationId $appObjectId -BodyParameter $NewOwner
75 |
76 | # Create a new application client secret with a validity of 12 months
77 | $newAppSecret = @{
78 | "displayName" = $AppSecretName
79 | "endDateTime" = (Get-Date).AddMonths(+12)
80 | }
81 | $appSecret = Add-MgApplicationPassword -ApplicationId $appObjectId -PasswordCredential $newAppSecret
82 |
83 | Write-Host 'Copy the following information to your settings file' -ForegroundColor Green
84 | Write-Host ('ClientSecret: {0}' -f $appSecret.SecretText) -ForegroundColor Green
85 |
86 | <#
87 | All permissions and IDs
88 | https://learn.microsoft.com/graph/permissions-reference#all-permissions-and-ids
89 |
90 | Calendars.ReadWrite Application ef54d2bf-783f-4e0f-bca1-3210c0444d99
91 | GroupMember.Read.All Application 98830695-27a2-44f7-8c18-0c3ebc9698f6
92 | User.ReadBasic.All Application 97235f07-e226-4f63-ace3-39588e11d3a1
93 | User.Read Delegated e1fe6dd8-ba31-4d61-89e7-88639da4683d
94 | #>
95 |
96 | $params = @{
97 | RequiredResourceAccess = @(
98 | @{
99 | ResourceAppId = "00000003-0000-0000-c000-000000000000"
100 | ResourceAccess = @(
101 | @{
102 | Id = "98830695-27a2-44f7-8c18-0c3ebc9698f6"
103 | Type = "Role"
104 | },
105 | @{
106 | Id = "ef54d2bf-783f-4e0f-bca1-3210c0444d99"
107 | Type = "Role"
108 | },
109 | @{
110 | Id = "97235f07-e226-4f63-ace3-39588e11d3a1"
111 | Type = "Role"
112 | },
113 | @{
114 | Id = "e1fe6dd8-ba31-4d61-89e7-88639da4683d"
115 | Type = "Scope"
116 | }
117 | )
118 | }
119 | )
120 | }
121 |
122 | # Add permissions to the application
123 | $null = Update-MgApplication -ApplicationId $appObjectId -BodyParameter $params
124 |
125 | # Return the application ID for the settings file
126 | Write-Host ('ClientId (App ID): {0}' -f $newApp.AppId) -ForegroundColor Green
127 |
128 | # Set the application as a public client with a redirect URI
129 | $RedirectURI = @()
130 | $RedirectURI += "https://login.microsoftonline.com/common/oauth2/nativeclient"
131 |
132 | $params = @{
133 | RedirectUris = @($RedirectURI)
134 | }
135 |
136 | $null = Update-MgApplication -ApplicationId $appObjectId -IsFallbackPublicClient -PublicClient $params
137 |
138 | # Open browser to grant admin consent
139 | $URL = ('https://entra.microsoft.com/#view/Microsoft_AAD_RegisteredApps/ApplicationMenuBlade/~/CallAnAPI/appId/{0}' -f $newApp.AppId)
140 |
141 | # Open the browser to grant admin consent
142 | # Wait for 30 seconds to allow Entra ID to provision the application
143 | Write-Host 'Browser will open in 30 seconds.'
144 | Start-Sleep -Seconds 30
145 | Start-Process $URL
--------------------------------------------------------------------------------
/Exchange Online/Add-CustomCalendarEvents/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) Thomas Stensitzki
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/Exchange Online/Add-CustomCalendarEvents/README.md:
--------------------------------------------------------------------------------
1 | # Add-CustomCalendarItems.ps1
2 |
3 | Add calendar items to the default calendar of users in a security group
4 |
5 | ## Description
6 |
7 | The script reads a JSON file with event data and creates calendar items in the default calendar of users in a security group.
8 |
9 | Use the Add-EntraIdAppRegistration.ps1 script to create a custom application registration in Entra ID.
10 |
11 | Adjust the settings in the Settings.xml file to match your environment.
12 |
13 | ### Screenshots
14 |
15 | #### PowerShell
16 |
17 | Simple to run PowerShell
18 |
19 | 
20 |
21 | #### Calendar
22 |
23 | This screenshot shows the result for a single event in Outlook on the Web
24 |
25 | 
26 |
27 | ## Requirements
28 |
29 | - PowerShell 7.1+
30 | - GlobalFunctions PowerShell module
31 | - Registered Entra ID application with access to Microsoft Graph
32 |
33 | ## Parameters
34 |
35 | ### EventFileName
36 |
37 | The name of the JSON file containing the event data located in the script directory.
38 |
39 | ### SettingsFileName
40 |
41 | The file name of the settings file located in the script directory.
42 |
43 | ## Example
44 |
45 | ``` PowerShell
46 | .\Add-CustomCalendarItems.ps1 -EventFileName CustomEvents.json -SettingsFileName CustomSettings.xml
47 | ```
48 |
49 | Create calendar items for users based on the JSON file CustomEvents.json and the settings file CustomSettings.xml
50 |
51 | ## Note
52 |
53 | THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE
54 | RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER.
55 |
56 | ## Credits
57 |
58 | Written by: Thomas Stensitzki
59 |
60 | The supporting script Add-EntraIdAppRegistration is based on content published by [Andres Bohren](https://blog.icewolf.ch/archive/2022/12/02/create-azure-ad-app-registration-with-microsoft-graph-powershell)
61 |
62 | ### Stay connected
63 |
64 | * Blog: [https://blog.granikos.eu](https://blog.granikos.eu)
65 | * Bluesky: [https://bsky.app/profile/stensitzki.eu](https://bsky.app/profile/stensitzki.eu)
66 | * LinkedIn: [https://www.linkedin.com/in/thomasstensitzki](https://www.linkedin.com/in/thomasstensitzki)
67 | * YouTube: [https://www.youtube.com/@ThomasStensitzki](https://www.youtube.com/@ThomasStensitzki)
68 | * LinkTree: [https://linktr.ee/stensitzki](https://linktr.ee/stensitzki)
69 |
70 | For more Microsoft 365, Cloud Security, and Exchange Server stuff checkout services provided by Granikos
71 |
72 | * Website: [https://granikos.eu](https://granikos.eu)
73 | * Bluesky: [https://bsky.app/profile/granikos.eu](https://bsky.app/profile/granikos.eu)
74 |
--------------------------------------------------------------------------------
/Exchange Online/Add-CustomCalendarEvents/events.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "eventAction": "create",
4 | "eventData": {
5 | "subject": "Betriebsruhe",
6 | "location": "Betriebsruhe",
7 | "start": {
8 | "dateTime" : "2024-01-01T00:00:00",
9 | "timeZone" : "W. Europe Standard Time"
10 | },
11 | "end": {
12 | "dateTime" : "2024-01-02T00:00:00",
13 | "timeZone" : "W. Europe Standard Time"
14 | },
15 | "isAllDay": true,
16 | "showAs": "oof",
17 | "isreminderOn": false
18 | }
19 | },
20 | {
21 | "eventAction": "create",
22 | "eventData": {
23 | "subject": "Betriebsruhe",
24 | "location": "Betriebsruhe",
25 | "start": {
26 | "dateTime" : "2024-10-04T00:00:00",
27 | "timeZone" : "W. Europe Standard Time"
28 | },
29 | "end": {
30 | "dateTime" : "2024-10-05T00:00:00",
31 | "timeZone" : "W. Europe Standard Time"
32 | },
33 | "isAllDay": true,
34 | "showAs": "oof",
35 | "isreminderOn": false
36 | }
37 | },
38 | {
39 | "eventAction": "create",
40 | "eventData": {
41 | "subject": "Betriebsruhe",
42 | "location": "Betriebsruhe",
43 | "start": {
44 | "dateTime" : "2024-12-24T00:00:00",
45 | "timeZone" : "W. Europe Standard Time"
46 | },
47 | "end": {
48 | "dateTime" : "2025-01-01T00:00:00",
49 | "timeZone" : "W. Europe Standard Time"
50 | },
51 | "isAllDay": true,
52 | "showAs": "oof",
53 | "isreminderOn": false
54 | }
55 | }
56 | ]
--------------------------------------------------------------------------------
/Exchange Online/Add-CustomCalendarEvents/settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | TenantId
4 | ClientId
5 | ClientSecret
6 | SecurityGroupObjectId
7 |
8 |
--------------------------------------------------------------------------------
/Exchange Online/Disable-RemotePowerShell/Disable-RemotePowerShell.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | .SYNOPSIS
3 |
4 | Disable Exchange Remote PowerShell for all users, except for members of a selected security group.
5 |
6 | Thomas Stensitzki
7 |
8 | THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE
9 | RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER.
10 |
11 | Version 1.0, 2023-02-03
12 |
13 | Please post ideas, comments, and suggestions at GitHub.
14 |
15 | .LINK
16 |
17 | https://scripts.Granikos.eu
18 |
19 | .DESCRIPTION
20 |
21 | This script sets the user attribute RemotePowerShellEnabled to FALSE for all users.
22 | You can and should provide an Active Directory universal security group containing all user accounts
23 | that should be able to use Exchange Remote PowerShell, e.g., Admins, Service Account, etc.
24 |
25 | .NOTES
26 |
27 | Requirements
28 | - Windows Server 2016 or newer
29 | - Global function PowerShell library, found here: http://scripts.granikos.eu
30 | - AciveDirectory PowerShell module
31 | - Exchange 2013+ Management Shell
32 |
33 | Revision History
34 | --------------------------------------------------------------------------------
35 | 1.0 Initial community release
36 |
37 | .PARAMETER DaysToKeep
38 |
39 | Number of days Exchange and IIS log files should be retained, default is 30 days
40 |
41 | .PARAMETER AllowRPSGroupName
42 |
43 | Name of the Active Directory security group containing all user accounts that must have Remote PowerShell enabled.
44 | Add all user accounts (administrators, users, service accounts) to that security group.
45 |
46 | .PARAMETER LogAllowedUsers
47 |
48 | Switch to write information about RPS allowed users to the log file.
49 |
50 | .PARAMETER LogDisabledUsers
51 |
52 | Switch to write information about users RBS disabled to the log file.
53 |
54 | .EXAMPLE
55 |
56 | Disable Exchange Remote PowerShell for all users using default settings, and do not write any user details to a log file.
57 |
58 | .\Disable-RemotePowerShell
59 |
60 | #>
61 |
62 |
63 | [CmdletBinding()]
64 | Param(
65 | [int]$DaysToKeep = 30,
66 | [string]$AllowRPSGroupName = 'AllowRemotePS',
67 | [switch]$LogAllowedUsers,
68 | [switch]$LogDisabledUsers
69 | )
70 |
71 | # Some error variables
72 | $ERR_OK = 0
73 | $ERR_GLOBALFUNCTIONSMISSING = 1098
74 | $ERR_NONELEVATEDMODE = 1099
75 |
76 | # Load Exchange Management Shell PowerShell Snap-In
77 | Add-PSSnapin Microsoft.Exchange.Management.PowerShell.SnapIn
78 |
79 | # Import Active Directory Module
80 | Import-Module -Name ActiveDirectory
81 |
82 | # Import GlobalFunctions
83 | if($null -ne (Get-Module -Name GlobalFunctions -ListAvailable).Version) {
84 | Import-Module -Name GlobalFunctions
85 | }
86 | else {
87 | Write-Warning -Message 'Unable to load GlobalFunctions PowerShell module.'
88 | Write-Warning -Message 'Open an administrative PowerShell session and run Import-Module GlobalFunctions'
89 | Write-Warning -Message 'Please check http://bit.ly/GlobalFunctions for further instructions'
90 | exit $ERR_GLOBALFUNCTIONSMISSING
91 | }
92 |
93 | $ScriptDir = Split-Path -Path $script:MyInvocation.MyCommand.Path
94 | $ScriptName = $MyInvocation.MyCommand.Name
95 | $logger = New-Logger -ScriptRoot $ScriptDir -ScriptName $ScriptName -LogFileRetention 14
96 | $logger.Write('Script started')
97 |
98 | # Check if we are running in elevated mode
99 | # function (c) by Michel de Rooij
100 | function script:Test-IsAdmin {
101 | $currentPrincipal = New-Object -TypeName Security.Principal.WindowsPrincipal -ArgumentList ( [Security.Principal.WindowsIdentity]::GetCurrent() )
102 |
103 | If( $currentPrincipal.IsInRole( [Security.Principal.WindowsBuiltInRole]::Administrator )) {
104 | return $true
105 | }
106 | else {
107 | return $false
108 | }
109 | }
110 |
111 | If (Test-IsAdmin) {
112 | # We are running in elevated mode. Let's continue.
113 |
114 | $logger.Write(('{1} started, keeping last {0} days of log files.' -f $DaysToKeep, $ScriptName))
115 | $logger.Purge()
116 |
117 | # Get all users with enabled Remote PowerShell
118 | $AllRPSUsers = Get-User -ResultSize Unlimited -Filter 'RemotePowerShellEnabled -eq $true' | select SamAccountName, RemotePowerShellEnabled
119 |
120 | # log number of users with RemotePowerShellEnabled enabled
121 | $logger.Write( ('Users with RemotePowerShellEnabled: {0}' -f ($AllRPSUsers | Measure-Object).Count ))
122 |
123 | # Get all users from AllowRPSGroupName
124 |
125 | $AllowedRPSUsers = Get-ADGroupMember -Identity $AllowRPSGroupName -Recursive | ForEach-Object { Get-User -Identity $_.SamAccountName | Select-Object SamAccountName, RemotePowerShellEnabled }
126 |
127 | # log number of users that are allowed to have RemotePowerShellEnabled
128 | $logger.Write( ('Number of allowed users: {0}' -f ($AllowedRPSUsers | Measure-Object).Count ))
129 |
130 | # Enable Remote PowerShell for allowed users
131 | foreach ($AllowedUser in $AllowedRPSUsers) {
132 | if ($AllowedUser.RemotePowerShellEnabled -eq $false) {
133 | if($LogAllowedUsers) {
134 | # write user information to log file
135 | $logger.Write( ('Setting RemotePowerShellEnabled to TRUE for: {0}' -f $AllowedUser.SamAccountName ))
136 | }
137 |
138 | # Set RemotePowerShellEnabled to TRUE for the user
139 | Set-User $AllowedUser.SamAccountName -RemotePowerShellEnabled $true
140 | }
141 | }
142 |
143 | # Disable Remote PowerShell for all users
144 | foreach ($User in $AllRPSUsers) {
145 |
146 | if ($AllowedRPSUsers.SamAccountName -notcontains $User.SamAccountName) {
147 | if($LogDisabledUsers) {
148 | # write user information to log file
149 | $logger.Write( ('Setting RemotePowerShellEnabled to FALSE for: {0}' -f $User.SamAccountName ))
150 | }
151 |
152 | # Set RemotePowerShellEnabled to FALSE for the user
153 | Set-User $User.SamAccountName -RemotePowerShellEnabled $false
154 |
155 | }
156 | }
157 |
158 | $logger.Write('Script finished')
159 |
160 | Return $ERR_OK
161 | }
162 | else {
163 | # Ooops, the admin did it again.
164 | Write-Warning -Message 'You must run the script in elevated mode. Please start the PowerShell-Session with administrative privileges.'
165 |
166 | Return $ERR_NONELEVATEDMODE
167 | }
--------------------------------------------------------------------------------
/Exchange Online/Disable-RemotePowerShell/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 Thomas Stensitzki
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Exchange Online/Disable-RemotePowerShell/README.md:
--------------------------------------------------------------------------------
1 | # Disable-RemotePowerShell
2 |
3 | Disable Exchange Remote PowerShell for all users, except for members of a selected security group.
4 |
5 | ## Description
6 |
7 | This script disables the use of RemotePoweShell und EXOPowerSHell module for Microsoft 365 users, except for members of a given security group.
8 |
9 | ## Requirements
10 |
11 | - Exchange Online Management Shell v2
12 |
13 | ## Parameters
14 |
15 | ### DaysToKeep
16 |
17 | Number of days Exchange and IIS log files should be retained, default is 30 days
18 |
19 | ### AllowRPSGroupName
20 |
21 | Name of the Active Directory security group containing all user accounts that must have Remote PowerShell enabled.
22 | Add all user accounts (administrators, users, service accounts) to that security group.
23 |
24 | ### LogAllowedUsers
25 |
26 | Switch to write information about RPS allowed users to the log file.
27 |
28 | ### LogDisabledUsers
29 |
30 | Switch to write information about users RBS disabled to the log file.
31 |
32 | ## Example
33 |
34 | ``` PowerShell
35 | .\Disable-RemotePowerShell
36 | ```
37 |
38 | Disable Exchange Remote PowerShell for all users using default settings, and do not write any user details to a log file.
39 |
40 | ## Note
41 |
42 | THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE
43 | RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER.
44 |
45 | ### Stay connected
46 |
47 | * Blog: [https://blog.granikos.eu](https://blog.granikos.eu)
48 | * Bluesky: [https://bsky.app/profile/stensitzki.eu](https://bsky.app/profile/stensitzki.eu)
49 | * LinkedIn: [https://www.linkedin.com/in/thomasstensitzki](https://www.linkedin.com/in/thomasstensitzki)
50 | * YouTube: [https://www.youtube.com/@ThomasStensitzki](https://www.youtube.com/@ThomasStensitzki)
51 | * LinkTree: [https://linktr.ee/stensitzki](https://linktr.ee/stensitzki)
52 |
53 | For more Microsoft 365, Cloud Security, and Exchange Server stuff checkout services provided by Granikos
54 |
55 | * Website: [https://granikos.eu](https://granikos.eu)
56 | * Bluesky: [https://bsky.app/profile/granikos.eu](https://bsky.app/profile/granikos.eu)
57 |
--------------------------------------------------------------------------------
/Exchange Online/Move-MigrationUser/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) Thomas Stensitzki
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Exchange Online/Move-MigrationUser/Move-MigrationUser.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | .SYNOPSIS
3 | This script creates a new migration batch and moves migration users from one batch to the new batch.
4 |
5 | Thomas Stensitzki
6 |
7 | THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE
8 | RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER.
9 |
10 | Version 1.0, 2022-08-29
11 |
12 | Please use GitHub repository for ideas, comments, and suggestions.
13 |
14 | .LINK
15 | https://github.com/Apoc70/PowerShell-Scripts
16 | https://scripts.granikos.eu
17 |
18 | .DESCRIPTION
19 |
20 | This script moves Exchange Online migration users to a new migration batch. The script creates
21 | the new batch automatically. The name of the new batch is based on the following:
22 | * Custom prefix, provided by the BatchName parameter
23 | * Batch completion date
24 | * Source batch name
25 |
26 | The new batch name helps you to easily identify the planned completion date and source of the
27 | migration users. This is helpful during an agile mailbox migration process.
28 |
29 | .NOTES
30 | Requirements
31 | - Exchange Online Management Shell v2
32 |
33 | Revision History
34 | --------------------------------------------------------------------------------
35 | 1.0 Initial community release
36 |
37 | .PARAMETER Users
38 |
39 | List of migration user email addresses that should move to a new migration batch
40 |
41 | .PARAMETER UsersCsvFile
42 |
43 | Path to a CSV file containing a migration users, one user migration email address per line
44 |
45 | .PARAMETER BatchName
46 |
47 | The name of the new migration batch. The BatchName is the first part of the final full Name, e.g. BATCHNAME_2022-08-17_SOURCEBATCHNAME
48 |
49 | .PARAMETER Autostart
50 |
51 | Switch indicating that the new migration batch should start automatically
52 |
53 | .PARAMETER AutoComplete
54 |
55 | Switch indicating that the new migration should complete migration automatically
56 | NOT implemented yet
57 |
58 | .PARAMETER CompleteDateTime
59 |
60 | [DateTime] defining the completion date and time for the new batch
61 |
62 | .PARAMETER NotificationEmails
63 |
64 | Email addresses for batch notification emails
65 |
66 | .PARAMETER DateTimePattern
67 |
68 | The string pattern used for date information in the batch name
69 |
70 | .EXAMPLE
71 |
72 | ./Move-MigrationUsers -Users JohnDoe@varunagroup.de,JaneDoe@varunagroup.de -CompleteDateTime '2022/08/31 18:00'
73 |
74 | Move two migration users from the their current migration batch to a new migration batch
75 | #>
76 |
77 | [CmdletBinding()]
78 | param(
79 |
80 | $Users = @( 'JohnDoe@varunagroup.de' ),
81 | # $UsersCsvFile = '',
82 | [string]$BatchName = 'BATCH',
83 | [switch]$Autostart,
84 | # [switch]$AutoComplete,
85 | [Parameter(Mandatory=$true)]
86 | [datetime]$CompleteDateTime,
87 | $NotificationEmails = 'VarunaAdmin@varunagroup.de',
88 | [string]$DateTimePattern = 'yyyy-MM-dd'
89 | )
90 |
91 | if (($Users | Measure-Object).Count -ne 0) {
92 |
93 | # Create name for en migration batch
94 | $BatchNamePrefix = ('{0}_{1}' -f $BatchName, ($CompleteDateTime.ToString($DateTimePattern)))
95 | Write-Verbose -Message (('New target batch name: {0}' -f $BatchName))
96 |
97 | # Fetching users
98 | Write-Verbose -Message ('Parsing {0} users' -f (($Users | Measure-Object).Count))
99 |
100 | # Fetch migration users and group by BatchId
101 | # ToDo: Pre-Check users for existence as migration user and a non-completed migration status
102 | $MigrationsUsers = $Users | ForEach-Object { Get-MigrationUser -Identity $_ | Select-Object Identity, BatchId } | Group-Object BatchId
103 |
104 | # Parse migration users
105 | $MigrationsUsers | ForEach-Object {
106 |
107 | Write-Output ('{1} - {0}'-f $_.Name, $_.Group.Identity)
108 |
109 | # assemble new batch name
110 | # adjust to your personal needs
111 | $NewBatchName = ('{0}_{1}' -f $BatchNamePrefix, $_.Name)
112 |
113 | # Create new migration batch
114 | if($Autostart) {
115 | $Batch = New-MigrationBatch -Name $NewBatchName -UserIds $_.Group.Identity -DisableOnCopy -AutoStart -NotificationEmails $NotificationEmails
116 | }
117 | else {
118 | $Batch = New-MigrationBatch -Name $NewBatchName -UserIds $_.Group.Identity -DisableOnCopy -NotificationEmails $NotificationEmails
119 | }
120 |
121 | # Output detailed batch information
122 | $Batch | Format-List
123 |
124 | # Set auto completion date and time if not null
125 | # Set time as Universal Time
126 | if($null -ne $CompleteDateTime) {
127 |
128 | Write-Verbose ('Setting CompleteAfter to: {0}' -f ($CompleteTime).ToUniversalTime())
129 |
130 | Set-MigrationBatch -Identity $NewBatchName -CompleteAfter ($CompleteTime).ToUniversalTime()
131 | }
132 |
133 | }
134 | }
135 | else {
136 | Write-Output 'The list of users is empty. Nothing to do today.'
137 | }
138 |
--------------------------------------------------------------------------------
/Exchange Online/Move-MigrationUser/README.md:
--------------------------------------------------------------------------------
1 | # Move-MigrationUser.ps1
2 |
3 | This script creates a new migration batch and moves migration users from one batch to the new batch.
4 |
5 | ## Description
6 |
7 | This script moves Exchange Online migration users to a new migration batch. The script creates the new batch automatically. The name of the new batch is based on the following:
8 |
9 | - Custom prefix, provided by the BatchName parameter
10 | - Batch completion date
11 | - Source batch name
12 |
13 | The new batch name helps you to easily identify the planned completion date and source of the migration users. This is helpful during an agile mailbox migration process.
14 |
15 | ## Requirements
16 |
17 | - Exchange Online Management Shell v2
18 |
19 | ## Parameters
20 |
21 | ### Users
22 |
23 | List of migration user email addresses that should move to a new migration batch
24 |
25 | ### UsersCsvFile
26 |
27 | Path to a CSV file containing a migration users, one user migration email address per line
28 |
29 | ### BatchName
30 |
31 | The name of the new migration batch. The BatchName is the first part of the final full Name, e.g. BATCHNAME_2022-08-17_SOURCEBATCHNAME
32 |
33 | ### Autostart
34 |
35 | Switch indicating that the new migration batch should start automatically
36 |
37 | ### AutoComplete
38 |
39 | Switch indicating that the new migration should complete migration automatically
40 | NOT implemented yet
41 |
42 | ### CompleteDateTime
43 |
44 | [DateTime] defining the completion date and time for the new batch
45 |
46 | ### NotificationEmails
47 |
48 | Email addresses for batch notification emails
49 |
50 | ### DateTimePattern
51 |
52 | The string pattern used for date information in the batch name
53 |
54 | ## Example
55 |
56 | ``` PowerShell
57 | ./Move-MigrationUsers -Users JohnDoe@varunagroup.de,JaneDoe@varunagroup.de -CompleteDateTime '2022/08/31 18:00'
58 | ```
59 |
60 | Move two migration users from the their current migration batch to a new migration batch
61 |
62 | ## Note
63 |
64 | THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE
65 | RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER.
66 |
67 | ### Stay connected
68 |
69 | * Blog: [https://blog.granikos.eu](https://blog.granikos.eu)
70 | * Bluesky: [https://bsky.app/profile/stensitzki.eu](https://bsky.app/profile/stensitzki.eu)
71 | * LinkedIn: [https://www.linkedin.com/in/thomasstensitzki](https://www.linkedin.com/in/thomasstensitzki)
72 | * YouTube: [https://www.youtube.com/@ThomasStensitzki](https://www.youtube.com/@ThomasStensitzki)
73 | * LinkTree: [https://linktr.ee/stensitzki](https://linktr.ee/stensitzki)
74 |
75 | For more Microsoft 365, Cloud Security, and Exchange Server stuff checkout services provided by Granikos
76 |
77 | * Website: [https://granikos.eu](https://granikos.eu)
78 | * Bluesky: [https://bsky.app/profile/granikos.eu](https://bsky.app/profile/granikos.eu)
79 |
--------------------------------------------------------------------------------
/Exchange Online/Send-MonitoringEmail/Add-EntraIdAppRegistration.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | .SYNOPSIS
3 | Add a custom app registration to Entra ID for Microsoft Graph access
4 |
5 | Thomas Stensitzki
6 |
7 | THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE
8 | RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER.
9 |
10 | Version 1.0, 2024-07-22
11 |
12 | Based on the work of Andres Bohren
13 | https://blog.icewolf.ch/archive/2022/12/02/create-azure-ad-app-registration-with-microsoft-graph-powershell
14 |
15 | .NOTES
16 | Requirements
17 | - PowerShell 7.1+
18 | - Account executing this script must be a role member of the Application Administrator or Global Administrator role
19 |
20 | Use Connect-MgGraph -Scopes "Directory.ReadWrite.All" -NoWelcome to connect to Microsoft Graph when creating the application.
21 |
22 | When using application permissions for Microsoft Grapg, consider restricting access to the application to specific users or groups:
23 | https://bit.ly/LimitExoAppAccess
24 |
25 | #>
26 | [CmdletBinding()]
27 | param(
28 | [string]$AppName = 'CustomSendEmail-Application',
29 | [string]$AppSecretName = 'AppClientSecret',
30 | # Update the email address to the email address of the user that should be the owner of the application
31 | [string]$AppOwnerEmailAddress = 'admin@egxde.onmicrosoft.com',
32 | [int]$AppSecretLifetime = 12
33 | )
34 |
35 | # Load required modules
36 |
37 | if ($null -ne (Get-Module -Name Microsoft.Graph.Authentication -ListAvailable).Version) {
38 | # Import the Microsoft.Graph.Authentication module
39 | Import-Module -Name Microsoft.Graph.Authentication
40 | }
41 | else {
42 | # Display a warning and exit the script
43 | Write-Warning -Message 'Unable to load Import-Module Microsoft.Graph.Authentication PowerShell module.'
44 | Write-Warning -Message 'Open an administrative PowerShell session and run Install-Module Import-Module Microsoft.Graph'
45 | exit
46 | }
47 | if ($null -ne (Get-Module -Name Microsoft.Graph.Applications -ListAvailable).Version) {
48 | # Import the Microsoft.Graph.Applications module
49 | Import-Module -Name Microsoft.Graph.Applications
50 | }
51 | else {
52 | # Display a warning and exit the script
53 | Write-Warning -Message 'Unable to load Import-Module Microsoft.Graph.Applications PowerShell module.'
54 | Write-Warning -Message 'Open an administrative PowerShell session and run Install-Module Import-Module Microsoft.Graph'
55 | exit
56 | }
57 |
58 | # Connect to Microsoft Graph
59 | Connect-MgGraph -Scopes "Application.Read.All", "Application.ReadWrite.All", "User.Read.All" -NoWelcome
60 |
61 | # Create a new application
62 | $newApp= New-MgApplication -DisplayName $AppName -Notes 'Application for sending monitoring emails. This app is used by the Send-MonitoringEmail.ps1 script.'
63 | $appObjectId = $newApp.Id
64 |
65 | # Set the owner of the application
66 | $User = Get-MgUser -UserId $AppOwnerEmailAddress
67 | $ObjectId = $User.ID
68 | $NewOwner = @{
69 | "@odata.id" = "https://graph.microsoft.com/v1.0/directoryObjects/{$ObjectId}"
70 | }
71 |
72 | $null = New-MgApplicationOwnerByRef -ApplicationId $appObjectId -BodyParameter $NewOwner
73 |
74 | # Create a new application client secret with a validity of 12 months
75 | $newAppSecret = @{
76 | "displayName" = $AppSecretName
77 | "endDateTime" = (Get-Date).AddMonths($AppSecretLifetime)
78 | }
79 | $appSecret = Add-MgApplicationPassword -ApplicationId $appObjectId -PasswordCredential $newAppSecret
80 |
81 | Write-Host 'Copy the following information to your settings file' -ForegroundColor Green
82 | Write-Host ('ClientSecret: {0}' -f $appSecret.SecretText) -ForegroundColor Green
83 |
84 | <#
85 | All permissions and IDs
86 | https://learn.microsoft.com/graph/permissions-reference#all-permissions-and-ids
87 |
88 | Mail.ReadWrite Application e2a3a72e-5f79-4c64-b1b1-878b674786c9
89 | Mail.Send Application b633e1c5-b582-4048-a93e-9f11b44c7e96
90 | User.Read Delegated e1fe6dd8-ba31-4d61-89e7-88639da4683d
91 | User.Read.All Application df021288-bdef-4463-88db-98f22de89214
92 | #>
93 |
94 | $params = @{
95 | RequiredResourceAccess = @(
96 | @{
97 | ResourceAppId = "00000003-0000-0000-c000-000000000000"
98 | ResourceAccess = @(
99 | @{
100 | Id = "df021288-bdef-4463-88db-98f22de89214"
101 | Type = "Role"
102 | },
103 | @{
104 | Id = "e2a3a72e-5f79-4c64-b1b1-878b674786c9"
105 | Type = "Role"
106 | },
107 | @{
108 | Id = "b633e1c5-b582-4048-a93e-9f11b44c7e96"
109 | Type = "Role"
110 | },
111 | @{
112 | Id = "e1fe6dd8-ba31-4d61-89e7-88639da4683d"
113 | Type = "Scope"
114 | }
115 | )
116 | }
117 | )
118 | }
119 |
120 | # Add permissions to the application
121 | $null = Update-MgApplication -ApplicationId $appObjectId -BodyParameter $params
122 |
123 | # Return the application ID for the settings file
124 | Write-Host ('ClientId (App ID): {0}' -f $newApp.AppId) -ForegroundColor Green
125 |
126 | # Set the application as a public client with a redirect URI
127 | $RedirectURI = @()
128 | $RedirectURI += "https://login.microsoftonline.com/common/oauth2/nativeclient"
129 |
130 | $params = @{
131 | RedirectUris = @($RedirectURI)
132 | }
133 |
134 | $null = Update-MgApplication -ApplicationId $appObjectId -IsFallbackPublicClient -PublicClient $params
135 |
136 | # Open browser to grant admin consent
137 | $URL = ('https://entra.microsoft.com/#view/Microsoft_AAD_RegisteredApps/ApplicationMenuBlade/~/CallAnAPI/appId/{0}' -f $newApp.AppId)
138 |
139 | # Open the browser to grant admin consent
140 | # Wait for 30 seconds to allow Entra ID to provision the application
141 | Write-Host 'Browser will open in 30 seconds.'
142 | Start-Sleep -Seconds 30
143 | Start-Process $URL
--------------------------------------------------------------------------------
/Exchange Online/Send-MonitoringEmail/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) Thomas Stensitzki
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/Exchange Online/Send-MonitoringEmail/Monitoring Attachment.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Apoc70/PowerShell-Scripts/ef43113b389502d309e246891fa9c7a3ad5a147e/Exchange Online/Send-MonitoringEmail/Monitoring Attachment.docx
--------------------------------------------------------------------------------
/Exchange Online/Send-MonitoringEmail/Monitoring Attachment.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Apoc70/PowerShell-Scripts/ef43113b389502d309e246891fa9c7a3ad5a147e/Exchange Online/Send-MonitoringEmail/Monitoring Attachment.pdf
--------------------------------------------------------------------------------
/Exchange Online/Send-MonitoringEmail/README.md:
--------------------------------------------------------------------------------
1 | # Add-CustomCalendarItems.ps1
2 |
3 | Send a single monitoring email to a recipient
4 |
5 | ## Description
6 |
7 | This script send a single monitoring email to a recipient. The script is intended to be used as a monitoring script.
8 | The message body contains a GUID and the current date and time in ticks.
9 |
10 | Use the Add-EntraIdAppRegistration.ps1 script to create a custom application registration in Entra ID.
11 |
12 | Adjust the settings in the Settings.xml file to match your environment.
13 |
14 | When using application permissions for Microsoft Grapg, consider restricting access to the application to specific users or groups:
15 | [https://bit.ly/LimitExoAppAccess](https://bit.ly/LimitExoAppAccess)
16 |
17 | ## Requirements
18 |
19 | - PowerShell 7.1+
20 | - GlobalFunctions PowerShell module
21 | - Registered Entra ID application with access to Microsoft Graph
22 |
23 | ## Parameters
24 |
25 | ### SettingsFileName
26 |
27 | The file name of the settings file located in the script directory.
28 |
29 | ## Example
30 |
31 | ``` PowerShell
32 | .\Add-CustomCalendarItems.ps1 -SettingsFileName CustomSettings.xml
33 | ```
34 |
35 | Send a monitoring email using the selected settings file
36 |
37 | ## Note
38 |
39 | THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE
40 | RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER.
41 |
42 | ## Credits
43 |
44 | Written by: Thomas Stensitzki
45 |
46 | THe supporting script Add-EntraIdAppRegistration is based on content published by [Andres Bohren](https://blog.icewolf.ch/archive/2022/12/02/create-azure-ad-app-registration-with-microsoft-graph-powershell)
47 |
48 | ### Stay connected
49 |
50 | * Blog: [https://blog.granikos.eu](https://blog.granikos.eu)
51 | * Bluesky: [https://bsky.app/profile/stensitzki.eu](https://bsky.app/profile/stensitzki.eu)
52 | * LinkedIn: [https://www.linkedin.com/in/thomasstensitzki](https://www.linkedin.com/in/thomasstensitzki)
53 | * YouTube: [https://www.youtube.com/@ThomasStensitzki](https://www.youtube.com/@ThomasStensitzki)
54 | * LinkTree: [https://linktr.ee/stensitzki](https://linktr.ee/stensitzki)
55 |
56 | For more Microsoft 365, Cloud Security, and Exchange Server stuff checkout services provided by Granikos
57 |
58 | * Website: [https://granikos.eu](https://granikos.eu)
59 | * Bluesky: [https://bsky.app/profile/granikos.eu](https://bsky.app/profile/granikos.eu)
60 |
--------------------------------------------------------------------------------
/Exchange Online/Send-MonitoringEmail/settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | TenantId
5 |
6 | ClientId
7 |
8 | ClientSecret
9 |
10 |
11 |
12 | user@yourdomain.com
13 |
14 | recipient@somedomain.com
15 |
16 | Monitoring Email
17 |
18 | Text
19 |
20 | Monitoring Attachment.pdf
21 |
22 | false
23 |
24 |
--------------------------------------------------------------------------------
/Exchange Online/Test-DomainAvailability/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Thomas Stensitzki
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Exchange Online/Test-DomainAvailability/README.md:
--------------------------------------------------------------------------------
1 | # Test-DomainAvailability.ps1
2 |
3 | ## Description
4 |
5 | The script queries the login uri for the selected Office 365 region. The response contains metadata about the domain queried.
6 |
7 | If the domain already exists in the specified region the metadata contains information if the domain is verified and/or federated
8 |
9 | Load function into your current PowerShell session:
10 |
11 | ``` PowerShell
12 | . .\Test-DomainAvailability.ps1
13 | ```
14 |
15 | ## Parameters
16 |
17 | ### DomainName
18 |
19 | The domain name you want to verify. Example: example.com
20 |
21 | ### LookupRegion
22 |
23 | The Office 365 region where you want to verify the domain.
24 | Currently implemented: Global, Germany, China
25 |
26 | ## Examples
27 |
28 | ``` PowerShell
29 | Test-DomainAvailability -DomainName example.com
30 | ```
31 |
32 | Test domain availability in the default region - Office 365 Global
33 |
34 | ``` PowerShell
35 | Test-DomainAvailability -DomainName example.com -LookupRegion China
36 | ```
37 |
38 | Test domain availability in Office 365 China
39 |
40 | ## Note
41 |
42 | THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE
43 | RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER.
44 |
45 | ## Credits
46 |
47 | Written by: Thomas Stensitzki
48 |
49 | ### Stay connected
50 |
51 | * Blog: [https://blog.granikos.eu](https://blog.granikos.eu)
52 | * Bluesky: [https://bsky.app/profile/stensitzki.eu](https://bsky.app/profile/stensitzki.eu)
53 | * LinkedIn: [https://www.linkedin.com/in/thomasstensitzki](https://www.linkedin.com/in/thomasstensitzki)
54 | * YouTube: [https://www.youtube.com/@ThomasStensitzki](https://www.youtube.com/@ThomasStensitzki)
55 | * LinkTree: [https://linktr.ee/stensitzki](https://linktr.ee/stensitzki)
56 |
57 | For more Microsoft 365, Cloud Security, and Exchange Server stuff checkout services provided by Granikos
58 |
59 | * Website: [https://granikos.eu](https://granikos.eu)
60 | * Bluesky: [https://bsky.app/profile/granikos.eu](https://bsky.app/profile/granikos.eu)
61 |
--------------------------------------------------------------------------------
/Exchange Online/Test-DomainAvailability/Test-DomainAvailability.ps1:
--------------------------------------------------------------------------------
1 | function Test-DomainAvailability {
2 | <#
3 | .SYNOPSIS
4 | Check the availability of a domain in a selected Office 365 region.
5 |
6 | .DESCRIPTION
7 | The script queries the login uri for the selected Office 365 region. The response contains metadata about the domain queried.
8 |
9 | If the domain already exists in the specified region the metadata contains information if the domain is verified and/or federated
10 |
11 | Load function into your current PowerShell session:
12 | . .\Test-DomainAvailability.ps1
13 |
14 | .PARAMETER DomainName
15 | The domain name you want to verify. Example: example.com
16 |
17 | .PARAMETER LookupRegion
18 | The Office 365 region where you want to verify the domain.
19 | Currently implemented: Global, Germany, China
20 |
21 | .NOTES
22 |
23 | Author: ?
24 | (Source: https://blogs.technet.microsoft.com/tip_of_the_day/2017/02/16/cloud-tip-of-the-day-use-powershell-to-check-domain-availability-for-office-365-and-azure/)
25 | Enhancement: Thomas Stensitzki
26 |
27 | THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE
28 | RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER.
29 |
30 | Version 1.1, 2024-08-20
31 |
32 | .LINK
33 | https://scripts.Granikos.eu
34 |
35 | .EXAMPLE
36 | Test-DomainAvailability -DomainName example.com
37 |
38 | Test domain availability in the default region - Office 365 Global
39 |
40 | .EXAMPLE
41 | Test-DomainAvailability -DomainName example.com -LookupRegion China
42 |
43 | Test domain availability in Office 365 China
44 | #>
45 |
46 | param(
47 | [Parameter(Mandatory=$true,HelpMessage='A domain name (e.g. example.com) is required')]
48 | [string]$DomainName,
49 | [ValidateSet('Global','Germany','China')]
50 | [string]$LookupRegion = 'Global'
51 | )
52 |
53 | # Define descriptions for status codes
54 | $descriptions = @{
55 | Unknown = 'Domain does not exist in Office 365/Azure AD'
56 | Managed = 'Domain is verified but not federated'
57 | Federated = 'Domain is verified and federated'
58 | }
59 |
60 | # Define login Uris for Office 365 regions, extend as needed and add hashtable key to param validate set
61 | $Regions = @{
62 | Global = 'login.microsoftonline.com'
63 | Germany = 'login.microsoftonline.de'
64 | China = 'login.partner.microsoftonline.cn'
65 | }
66 |
67 | # Select Lookup uri
68 | $LookupUri = $Regions[$LookupRegion]
69 |
70 | if($LookupUri -ne '') {
71 |
72 | #
73 | $response = Invoke-WebRequest -Uri ('https://{0}/getuserrealm.srf?login=user@{1}&xml=1' -f $LookupUri, $DomainName)
74 |
75 | if($response -and $response.StatusCode -eq 200) {
76 |
77 | # Check namespace
78 | $namespaceType = ([xml]($response.Content)).RealmInfo.NameSpaceType
79 |
80 | New-Object -TypeName PSObject -Property @{
81 |
82 | DomainName = $DomainName
83 |
84 | NamespaceType = $namespaceType
85 |
86 | Details = $descriptions[$namespaceType]
87 |
88 | } | Select-Object -Property DomainName, NamespaceType, Details
89 |
90 | }
91 | else {
92 | # We were not ablte to connect to lookup uri. Do wen have an Internet connection?
93 |
94 | Write-Error -Message 'Domain could not be verified. Please check your connectivity to login.microsoftonline.com'
95 |
96 | }
97 |
98 | }
99 |
100 | }
--------------------------------------------------------------------------------
/Exchange Server/Add-HybridSendConnector/Add-HybridSendConnector.ps1:
--------------------------------------------------------------------------------
1 | # Send Connector Parameters
2 |
3 | # The name of the connector
4 | $sendConnectorName = "E-Mail to Internet via ExchangeOnline"
5 |
6 | # Use the same Fqdn as the one used for your hybrid send connector created by HCW
7 | $sendConnectorFqdn = "smtpo.varunagroup.de"
8 |
9 | # Source transpport server(s) for the send connector
10 | # Might be an Edge Transport Server
11 | $sendConnectorSourceTransportServers = "EX01"
12 |
13 | # Maximum message size for the send connector
14 | $sendConnectorMaxMessageSize = 100MB
15 |
16 | # Use the MX host provided in the Microsoft 365 Admin Center for your primary custom domain
17 | $sendConnectorTargetSmartHost = "varunagroup-de.mail.protection.outlook.com"
18 |
19 | # Create new send connector
20 | # The new send connector is not enabled by default!
21 | # Check additional settings, e.g., MaxMessageSize, before enabling the send connector
22 | New-SendConnector -Name $sendConnectorName -AddressSpaces * -CloudServicesMailEnabled $true `
23 | -Fqdn $sendConnectorFqdn `
24 | -SmartHosts $sendConnectorTargetSmartHost `
25 | -SourceTransportServers $sendConnectorSourceTransportServers`
26 | -MaxMessageSize $sendConnectorMaxMessageSize `
27 | -RequireTLS $true -DNSRoutingEnabled $false `
28 | -TlsAuthLevel CertificateValidation -Enabled:$false
--------------------------------------------------------------------------------
/Exchange Server/Copy-ReceiveConnector/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) Thomas Stensitzki
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/Exchange Server/Copy-ReceiveConnector/README.md:
--------------------------------------------------------------------------------
1 | # Copy-ReceiveConnector.ps1
2 |
3 | Copy a selected receive connector and it's configuration and permissions to other Exchange Servers
4 |
5 | ## Description
6 |
7 | This script copies a receive connector from a source Exchange Server to a single target Exchange server or to all Exchange servers.
8 |
9 | Configured permissions are copied as well, if required.
10 |
11 | ## Requirements
12 |
13 | - Windows Server 2016, Windows Server 2019
14 | - Exchange Server 2013/2016/2019 Management Shell
15 |
16 | ## Parameters
17 |
18 | ### ConnectorName
19 |
20 | Name of the connector the new IP addresses should be added to
21 |
22 | ### SourceServer
23 |
24 | Name of the receive connector to copy
25 |
26 | ### TargetServer
27 |
28 | Target Exchange server to copy the selected receive connector to
29 |
30 | ### DomainController
31 |
32 | Domain Controller name
33 |
34 | ### CopyToAllOther
35 |
36 | Switch to copy to all other Exchange servers
37 |
38 | ### CopyPermissions
39 |
40 | Copy non inherited source receive AD permissions to target receive connector. Inherited permissions will not be copied
41 |
42 | ### MoveToFrontend
43 |
44 | Change source connector transport role to FrontendTransport. This is required when you copy a receive connector from Exchange 2007 to Exchange 2013+
45 |
46 | ### ResetBindings
47 |
48 | Do not copy bindings but reset receive connector network bindings to 0.0.0.0:25
49 |
50 | ### UpdateExistingConnector
51 |
52 | Update an existing receive connector without confirmation prompt.
53 |
54 | ### ViewEntireForest
55 |
56 | View entire Active Directory forest
57 |
58 | ## Examples
59 |
60 | ``` PowerShell
61 | .\Copy-ReceiveConnector.ps1 -SourceServer MBX01 -ConnectorName nikos-one-RC2 -TargetServer MBX2 -DomainController MYDC1.mcsmemail.de
62 | ```
63 |
64 | Copy Exchange 2013 receive connector nikos-one-RC2 from server MBX01 to server MBX2
65 |
66 | ``` PowerShell
67 | .\Copy-ReceiveConnector.ps1 -SourceServer MBX01 -ConnectorName nikos-one-RC1 -CopyToAllOther -DomainController MYDC1.mcsmemail.de
68 | ```
69 |
70 | Copy Exchange 2013 receive connector nikos-one-RC2 from server MBX01 to all other Exchange 2013 servers
71 |
72 | ``` PowerShell
73 | .\Copy-ReceiveConnector.ps1 -SourceServer MBX2007 -ConnectorName "varunagroup relay" -TargetServer MBX01 -MoveToFrontend -ResetBindings -DomainController MYDC1.mcsmemail.de
74 | ```
75 |
76 | Copy Exchange 2013 receive connector "nikos-two relay" from Exchange 2007 server MBX2007 to Exchange 2013+ server MBX01 and reset network binding
77 |
78 | ``` PowerShell
79 | .\Copy-ReceiveConnector.ps1 -SourceServer MBX01 -ConnectorName MYRECEIVECONNECTOR -CopyToAllOther -DomainController MYDC1.mcsmemail.de -UpdateExitingConnector
80 | ```
81 |
82 | Copy Exchange 2013/2016/2019 receive connector MYRECEIVECONNECTOR from server MBX01 to all other Exchange 2013+ servers without confirmation prompt if connectors already exists
83 |
84 | ## Note
85 |
86 | THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE
87 | RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER.
88 |
89 | ## Credits
90 |
91 | Written by: Thomas Stensitzki
92 |
93 | ### Stay connected
94 |
95 | * Blog: [https://blog.granikos.eu](https://blog.granikos.eu)
96 | * Bluesky: [https://bsky.app/profile/stensitzki.eu](https://bsky.app/profile/stensitzki.eu)
97 | * LinkedIn: [https://www.linkedin.com/in/thomasstensitzki](https://www.linkedin.com/in/thomasstensitzki)
98 | * YouTube: [https://www.youtube.com/@ThomasStensitzki](https://www.youtube.com/@ThomasStensitzki)
99 | * LinkTree: [https://linktr.ee/stensitzki](https://linktr.ee/stensitzki)
100 |
101 | For more Microsoft 365, Cloud Security, and Exchange Server stuff checkout services provided by Granikos
102 |
103 | * Website: [https://granikos.eu](https://granikos.eu)
104 | * Bluesky: [https://bsky.app/profile/granikos.eu](https://bsky.app/profile/granikos.eu)
105 |
--------------------------------------------------------------------------------
/Exchange Server/Export-MessageQueue/Export-MessageQueue.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | .SYNOPSIS
3 | Export messages from a transport queue to file system for manual replay
4 |
5 | Thomas Stensitzki
6 |
7 | THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE
8 | RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER.
9 |
10 | Version 1.2, 2022-09-05
11 |
12 | Leave ideas, comments, and suggestions at GitHub
13 |
14 | .LINK
15 | https://github.com/Apoc70/Export-MessageQueue
16 |
17 | .DESCRIPTION
18 |
19 | This script suspends a transport queue, exports the messages to the local file system. After successful export the messages are optionally deleted from the queue.
20 |
21 | .NOTES
22 | Requirements
23 | - Exchange Server 2016+
24 | - Windows Server 2016+
25 | - Utilizes global functions library
26 |
27 | Revision History
28 | --------------------------------------------------------------------------------
29 | 1.0 Initial community release
30 | 1.1 Some PowerShell hygiene
31 | 1.2 PowerSHell Typo fixed and updated for Exchange Server 2019
32 |
33 | .PARAMETER Queue
34 | Full name of the transport queue, e.g. SERVER\354
35 | Use Get-Queue -Server SERVERNAME to identify message queue
36 |
37 | .PARAMETER Path
38 | Path to folder for exprted messages
39 |
40 | .PARAMETER DeleteAfterExport
41 | Switch to delete per Exchange Server subfolders and creating new folders
42 |
43 | .EXAMPLE
44 | Export messages from queue MCMEP01\45534 to D:\ExportedMessages and do not delete messages after export
45 |
46 | .\Export-MessageQueue -Queue MCMEP01\45534 -Path D:\ExportedMessages
47 |
48 | .EXAMPLE
49 | Export messages from queue MCMEP01\45534 to D:\ExportedMessages and delete messages after export
50 |
51 | .\Export-MessageQueue -Queue MCMEP01\45534 -Path D:\ExportedMessages -DeleteAfterExport
52 |
53 | #>
54 | param(
55 | [parameter(Mandatory=$true,HelpMessage='Transport queue holding messages to be exported (e.g. SERVER\354)')]
56 | [string] $Queue,
57 | [parameter(Mandatory=$true,HelpMessage='File path to local folder for exprted messages (e.g. E:\Export)')]
58 | [string] $Path,
59 | [switch] $DeleteAfterExport
60 | )
61 |
62 | # Set-StrictMode -Version Latest
63 |
64 | # Implementation of global module
65 | # Import GlobalFunctions
66 | if($null -ne (Get-Module -Name GlobalFunctions -ListAvailable).Version) {
67 | Import-Module -Name GlobalFunctions
68 | }
69 | else {
70 | Write-Warning -Message 'Unable to load GlobalFunctions PowerShell module.'
71 | Write-Warning -Message 'Open an administrative PowerShell session and run Import-Module GlobalFunctions'
72 | Write-Warning -Message 'Please check http://bit.ly/GlobalFunctions for further instructions'
73 | exit
74 | }
75 |
76 | $ScriptDir = Split-Path -Path $script:MyInvocation.MyCommand.Path
77 | $ScriptName = $MyInvocation.MyCommand.Name
78 | $logger = New-Logger -ScriptRoot $ScriptDir -ScriptName $ScriptName -LogFileRetention 14
79 | $logger.Write('Script started')
80 | $logger.Write(('Working on message queue {0}, Export folder: {1}, DeleteAfterExport: {2}' -f $Queue, $Path, $DeleteAfterExport))
81 |
82 | ### FUNCTIONS -----------------------------
83 |
84 | function Request-Choice {
85 | [CmdletBinding()]
86 | param([string]$Caption)
87 | $choices = [System.Management.Automation.Host.ChoiceDescription[]]@("&Yes","&No")
88 | [int]$defaultChoice = 1
89 |
90 | $choiceReturn = $Host.UI.PromptForChoice($Caption, "", $choices, $defaultChoice)
91 |
92 | return $choiceReturn
93 | }
94 |
95 | function Check-Folders {
96 | # Check, if export folder exists
97 | if(!(Test-Path -Path $Path)) {
98 | # Folder does not exist, lets create a new root folder
99 | New-Item -Path $Path -ItemType Directory | Out-Null
100 |
101 | $logger.Write(('Folder {0} created' -f $Path))
102 | }
103 | }
104 |
105 |
106 | function Check-Queue {
107 | # Check message queue
108 | $messageCount = -1
109 | try {
110 | $messageQueue = Get-Queue $Queue
111 | $messageCount = $messageQueue.MessageCount
112 |
113 | $logger.Write(('{0} message(s) found in queue {1}' -f $messageCount, $Queue))
114 | }
115 | catch {
116 | $logger.Write(('Queue {0} cannot be accessed' -f $Queue))
117 | }
118 | $messageCount
119 | }
120 |
121 | function Export-Messages {
122 | # Export suspended messages
123 | try {
124 | # Suspend messages in queue
125 | $logger.Write(('Suspending queue {0}' -f $Queue))
126 | Get-Queue $Queue | Get-Message -ResultSize Unlimited | Suspend-Message -Confirm:$false
127 |
128 | # Fetch suspended messages
129 | $logger.Write(('Fetching suspended messages from queue {0}' -f $Queue))
130 |
131 | $messages = @(Get-Queue $Queue | Get-Message -ResultSize Unlimited | Where-Object{$_.Status -eq "Suspended"} )
132 |
133 | $logger.Write( ('{0} suspended messages fetched from queue {1}' -f $messages.Count, $Queue) )
134 |
135 | # Export fetched messages
136 | $messages | ForEach-Object {$m++;Export-Message $_.Identity | AssembleMessage -Path (Join-Path -ChildPath ('{0}.eml' -f $m) -Path $Path)}
137 | }
138 | catch {
139 | # get error record
140 | [Management.Automation.ErrorRecord]$e = $_
141 |
142 | # retrieve information about runtime error
143 | $info = [PSCustomObject]@{
144 | Exception = $e.Exception.Message
145 | Reason = $e.CategoryInfo.Reason
146 | Target = $e.CategoryInfo.TargetName
147 | Script = $e.InvocationInfo.ScriptName
148 | Line = $e.InvocationInfo.ScriptLineNumber
149 | Column = $e.InvocationInfo.OffsetInLine
150 | }
151 |
152 | Write-Error $info
153 | }
154 | }
155 |
156 | function Delete-Messages {
157 | # Delete suspended messages from queue
158 |
159 | $logger.Write( ('Delete suspended messages from queue {0}' -f $Queue) )
160 |
161 | Get-Message -Queue $Queue -ResultSize Unlimited | Where-Object{$_.Status -eq "Suspened"} | Remove-Message -WithNDR $false -Confirm:$false
162 | }
163 |
164 |
165 | # MAIN ####################################################
166 |
167 | # 1. Check export folder
168 | Check-Folders
169 |
170 | # 2. Check queue
171 | if((Check-Queue -gt 0)) {
172 | if((Request-Choice -Caption ('Do you want to suspend and export all messages in queue {0}?' -f $Queue)) -eq 0) {
173 | # Yes, we want to suspend and delete messages
174 | Export-Messages
175 | }
176 | else {
177 | # No, we do not want to delete message
178 | $logger.Write("User choice: Do not suspend and export messages")
179 | }
180 | if($DeleteAfterExport) {
181 | if((Request-Choice -Caption ('Do you want to DELETE all suspended messages from queue {0}?' -f $Queue)) -eq 0) {
182 | $logger.Write("User choice: DELETE suspended")
183 | Write-Output "Suspended messages will be deleted WITHOUT sending a NDR!"
184 | Delete-Messages
185 | }
186 | else {
187 | $logger.Write("User choice: DO NOT DELETE suspended")
188 | Write-Output "Exported messages have NOT been deleted from queue!"
189 | Write-Output "Remove messages manually and be sure, if you want to send a NDR!"
190 | }
191 | }
192 | }
193 | else {
194 | Write-Output ('Queue {0} does not contain any messages' -f $Queue)
195 | $logger.Write(('Queue {0} does not contain any messages' -f $Queue))
196 | }
197 |
198 | $logger.Write("Script finished")
199 | Write-Host "Script finished"
--------------------------------------------------------------------------------
/Exchange Server/Export-MessageQueue/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) Thomas Stensitzki
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Exchange Server/Export-MessageQueue/README.md:
--------------------------------------------------------------------------------
1 | # Export-MessageQueue.ps1
2 |
3 | Export messages from a transport queue to file system for manual replay
4 |
5 | ## Description
6 |
7 | This script suspends a transport queue, exports the messages to the local file system. After successful export the messages are optionally deleted from the queue.
8 |
9 | This script utilizes the GlobalFunctions library [https://github.com/Apoc70/GlobalFunctions](https://github.com/Apoc70/GlobalFunctions)
10 |
11 | ## Parameters
12 |
13 | ### Queue
14 |
15 | Full name of the transport queue, e.g. SERVER\354
16 | Use Get-Queue -Server SERVERNAME to identify message queue
17 |
18 | ### Path
19 |
20 | Path to folder for exported messages
21 |
22 | ### DeleteAfterExport
23 |
24 | Switch to delete per Exchange Server subfolders and creating new folders
25 |
26 | ## Examples
27 |
28 | ``` PowerShell
29 | .\Export-MessageQueue -Queue MCMEP01\45534 -Path D:\ExportedMessages
30 | ```
31 |
32 | Export messages from queue MCMEP01\45534 to D:\ExportedMessages and do not delete messages after export
33 |
34 | ``` PowerShell
35 | .\Export-MessageQueue -Queue MCMEP01\45534 -Path D:\ExportedMessages -DeleteAfterExport
36 | ```
37 |
38 | Export messages from queue MCMEP01\45534 to D:\ExportedMessages and delete messages after export
39 |
40 | ## Note
41 |
42 | THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE
43 | RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER.
44 |
45 | ## Credits
46 |
47 | Written by: Thomas Stensitzki
48 |
49 | ### Stay connected
50 |
51 | * Blog: [https://blog.granikos.eu](https://blog.granikos.eu)
52 | * Bluesky: [https://bsky.app/profile/stensitzki.eu](https://bsky.app/profile/stensitzki.eu)
53 | * LinkedIn: [https://www.linkedin.com/in/thomasstensitzki](https://www.linkedin.com/in/thomasstensitzki)
54 | * YouTube: [https://www.youtube.com/@ThomasStensitzki](https://www.youtube.com/@ThomasStensitzki)
55 | * LinkTree: [https://linktr.ee/stensitzki](https://linktr.ee/stensitzki)
56 |
57 | For more Microsoft 365, Cloud Security, and Exchange Server stuff checkout services provided by Granikos
58 |
59 | * Website: [https://granikos.eu](https://granikos.eu)
60 | * Bluesky: [https://bsky.app/profile/granikos.eu](https://bsky.app/profile/granikos.eu)
61 |
--------------------------------------------------------------------------------
/Exchange Server/Export-ModernPublicFolderStatistics/Export-PublicFolderStatisticsToExcel.ps1:
--------------------------------------------------------------------------------
1 | # Parameter section
2 | [CmdletBinding()]
3 | param(
4 | [switch]$OpenExcel,
5 | [switch]$SendMail,
6 | [string]$MailFrom = '',
7 | [string]$MailTo = '',
8 | [string]$MailServer = ''
9 | )
10 |
11 | $ScriptDir = Split-Path $script:MyInvocation.MyCommand.Path
12 |
13 | # Use current date as file timestamp
14 | $now = Get-Date -Format 'yyyy-MM-dd'
15 |
16 | $cssFile = Join-Path -Path $ScriptDir -ChildPath styles.css
17 | $reportTitle = "Public Folder Statistics - $($now)"
18 |
19 | function Import-PublicFolderStatsToExcelWithChart {
20 | param (
21 | [string]$csvPath = "PublicFolderSummary.csv",
22 | [string]$excelPath = "PublicFolderSummary.xlsx"
23 | )
24 |
25 | # Check if the ImportExcel module is installed
26 | if (-not (Get-Module -ListAvailable -Name ImportExcel)) {
27 | Write-Host "ImportExcel module is not installed. Installing now..."
28 | Install-Module -Name ImportExcel -Force -Scope CurrentUser
29 | }
30 |
31 | Remove-Item $excelPath -ErrorAction Ignore
32 |
33 | # Import data from CSV
34 | $data = Import-Csv -Path $csvPath
35 |
36 | $workSheetName = "PublicFolderStats"
37 |
38 | # Export data to Excel
39 | $excel = $data | Export-Excel -Path $excelPath -WorksheetName $workSheetName -ClearSheet -TableName PublicFolder -AutoSize -PassThru -AutoNameRange
40 |
41 |
42 |
43 | # some chart parameters
44 | $width = 1000
45 | $height = 500
46 | $row = 1
47 | $column = 1
48 |
49 | # Add chart sheet
50 | $null = Add-Worksheet -ExcelPackage $excel -WorksheetName 'Overview' -Activate -MoveToEnd
51 |
52 | # Add a line chart to the Excel file
53 | $excelChartParams = @{
54 | Worksheet = $excel.Overview
55 | ChartType = "LineMarkersStacked"
56 | Title = "Entwicklung der Public Folder (Anzahl)"
57 | #XRange = "A2:A" + ($data.Count + 1)
58 | #YRange = "B2:B" + ($data.Count + 1) #+ ",D2:D" + ($data.Count + 1) # PublicFolderCount and PublicFolderItemSizeInMB columns
59 | XRange = ('{0}!Date' -f $workSheetName)
60 | YRange = ('{0}!Count' -f $workSheetName)# ,('{0}!SizeInMB' -f $workSheetName)
61 | SeriesHeader = "Anzahl Public Folder"# , "Größe in MB"
62 | XAxisTitleText = "Datum"
63 | XAxisTitleSize = 10
64 | #XAxisNumberformat = "Date-Time"
65 | YAxisTitleText = "Anzahl"
66 | YAxisTitleSize = 10
67 | LegendPosition = "bottom"
68 | Row = $row
69 | Column = $column
70 | Width = $width
71 | Height = $height
72 |
73 | }
74 |
75 | Add-ExcelChart @excelChartParams
76 |
77 | # Add chart sheet
78 | $null = Add-Worksheet -ExcelPackage $excel -WorksheetName 'Size' -Activate -MoveToEnd
79 |
80 | # Add a line chart to the Excel file
81 | $excelChartParams = @{
82 | Worksheet = $excel.Size
83 | ChartType = "LineMarkersStacked"
84 | Title = "Entwicklung der Public Folder (Size)"
85 | #XRange = "A2:A" + ($data.Count + 1)
86 | #YRange = "B2:B" + ($data.Count + 1) #+ ",D2:D" + ($data.Count + 1) # PublicFolderCount and PublicFolderItemSizeInMB columns
87 | XRange = ('{0}!Date' -f $workSheetName)
88 | YRange = ('{0}!SizeInMB' -f $workSheetName)
89 | SeriesHeader = "Size in MB"
90 | XAxisTitleText = "Datum"
91 | XAxisTitleSize = 10
92 | YAxisNumberformat = "#,##0"
93 | YAxisTitleText = "Size [MB]"
94 | YAxisTitleSize = 10
95 | LegendPosition = "bottom"
96 | Row = $row
97 | Column = $column
98 | Width = $width
99 | Height = $height
100 |
101 | }
102 |
103 | Add-ExcelChart @excelChartParams
104 |
105 | # Add chart sheet
106 | $null = Add-Worksheet -ExcelPackage $excel -WorksheetName 'First Level' -Activate -MoveToEnd
107 |
108 | # Add a line chart to the Excel file
109 | $excelChartParams = @{
110 | Worksheet = $excel."First Level"
111 | ChartType = "LineMarkersStacked"
112 | Title = "Public Folder Erste Ebene (Anzahl)"
113 | #XRange = "A2:A" + ($data.Count + 1)
114 | #YRange = "B2:B" + ($data.Count + 1) #+ ",D2:D" + ($data.Count + 1) # PublicFolderCount and PublicFolderItemSizeInMB columns
115 | XRange = ('{0}!Date' -f $workSheetName)
116 | YRange = ('{0}!RootFolders' -f $workSheetName)
117 | SeriesHeader = "Anzahl"
118 | XAxisTitleText = "Datum"
119 | XAxisTitleSize = 10
120 | YAxisNumberformat = "#,##0"
121 | YAxisTitleText = "Anzahl"
122 | YAxisTitleSize = 10
123 | LegendPosition = "bottom"
124 | Row = $row
125 | Column = $column
126 | Width = $width
127 | Height = $height
128 |
129 | }
130 |
131 | Add-ExcelChart @excelChartParams
132 |
133 | if($OpenExcel) {
134 | # Open the Excel file
135 | Close-ExcelPackage $excel -Show
136 | }
137 | esle{
138 | # Save the Excel file
139 | Close-ExcelPackage $excel
140 | }
141 |
142 | Write-Host "Data and chart have been successfully exported to $excelPath"
143 | }
144 |
145 | # Call the function to create the Excel file
146 | Import-PublicFolderStatsToExcelWithChart
147 |
148 | if ($SendMail) {
149 |
150 |
151 |
152 | $head = @"
153 |
154 |
$($reportTitle)
155 |
156 | $($reportTitle)
157 | Here are the current public folder statistics.
158 | "@
159 |
160 | [string]$htmlreport = ConvertTo-Html -Body $html -Head $head -Title $reportTitle
161 |
162 | Send-Mail -From $MailFrom -To $MailTo -SmtpServer $MailServer -MessageBody $htmlreport -Subject $reportTitle -Attachments $excelPath -BodyAsHtml
163 | }
--------------------------------------------------------------------------------
/Exchange Server/Export-ModernPublicFolderStatistics/styles.css:
--------------------------------------------------------------------------------
1 | @charset "UTF-8";
2 | body {
3 | font-family: Verdana, Geneva, Arial, Helvetica, sans-serif;
4 | font-size: 10pt;
5 | }
6 | h1 {
7 | font-family: Verdana, Geneva, Arial, Helvetica, sans-serif;
8 | font-size: 16px;
9 | }
10 | h2 {
11 | font-family: Verdana, Geneva, Arial, Helvetica, sans-serif;
12 | font-size: 14px;
13 | }
14 | h3 {
15 | font-family: Verdana, Geneva, Arial, Helvetica, sans-serif;
16 | font-size: 12px;
17 | }
18 | table {
19 | font: 10pt Verdana, Geneva, Arial, Helvetica, sans-serif;
20 | border: 1px solid black;
21 | border-collapse:
22 | collapse; font-size: 10pt;
23 | }
24 | table th {
25 | border: 1px solid black;
26 | background: #dddddd;
27 | padding: 5px;
28 | color: #000000;
29 | }
30 | table td {
31 | border: 1px solid black;
32 | padding: 5px;
33 | }
34 | table td.pass {
35 | background: #7FFF00;
36 | }
37 | table td.warn {
38 | background: #FFE600;
39 | }
40 | table td.fail {
41 | background: #FF0000; color: #ffffff;
42 | }
43 | table td.info {
44 | background: #85D4FF;
45 | }
46 | .error {
47 | background: #FF0000; color: #ffffff;
48 | }
49 | .danger {background-color: red}
50 | .warn {background-color: yellow}
51 | .ok {background-color: green}
--------------------------------------------------------------------------------
/Exchange Server/Export-PublicFolderPermissions/Export-PublicFolderPermissions.ps1:
--------------------------------------------------------------------------------
1 | $ScriptDir = Split-Path $script:MyInvocation.MyCommand.Path
2 |
3 | # Use current date as file timestamp
4 | $now = Get-Date -Format 'yyyy-MM-dd'
5 |
6 | $cssFile = Join-Path -Path $ScriptDir -ChildPath styles.css
7 |
8 | $reportFileName = "Public Folder Permissions - $($now).csv"
9 |
10 | $pf = Get-PublicFolder -ResultSize Unlimited -Recurse
11 |
12 | $pf | .\Report-PFPermissions.ps1 | Export-Csv -Path (Join-Path -Path $ScriptDir -ChildPath $reportFileName) -NoTypeInformation -Encoding UTF8 -Force
--------------------------------------------------------------------------------
/Exchange Server/Export-PublicFolderPermissions/Report-PFPermissions.ps1:
--------------------------------------------------------------------------------
1 | # Copyright Frank Carius
2 | # https://www.msxfaq.de/exchange/tools/auswertung/reportpfpermissions.htm
3 |
4 | Begin {
5 | }
6 |
7 | Process {
8 | $pf = $_
9 | if($pf.parentpath -eq '\') {
10 | $folder = $pf.parentpath + $pf.name
11 | }
12 | else {
13 | $folder = $pf.parentpath + "\" + $pf.name
14 | }
15 | write-host "Processing Folder:" $folder
16 | $permission = get-PublicFolderClientPermission -identity $pf.Identity
17 | foreach ($perm in $permission) {
18 | $rights = [string]$perm.accessRights
19 | $User = [string]$perm.User
20 | # write-host "Rechte" $rights "User:" $User
21 | $pso = New-Object PSObject
22 | Add-Member -InputObject $pso noteproperty Folder $folder
23 | Add-Member -InputObject $pso noteproperty User $User
24 | Add-Member -InputObject $pso noteproperty AccessRights $rights
25 | $pso
26 | }
27 | }
28 |
29 | End {
30 | }
--------------------------------------------------------------------------------
/Exchange Server/Get-ExchangeADVersion/Get-ExchangeADVersion.ps1:
--------------------------------------------------------------------------------
1 | # This script retrieves the version of the Active Directory schema used by Exchange.
2 | # It uses the ADSI provider to access the Active Directory schema and retrieves the version information.
3 |
4 | # schema and object version strings
5 | # The version strings are used to map the schema and object versions to the corresponding Exchange version.
6 | # The mapping is done using a hashtable, where the keys are the schema and object version strings, and the values are the corresponding Exchange version strings.
7 | # The hashtable is used to simplify the mapping process and make it easier to retrieve the Exchange version based on the schema and object versions.
8 | $VersionStrings = @{
9 | '17003.16763.13243' = @{Long = 'Exchange Server 2019 CU15'; Short = 'Ex19CU15' }
10 | '17003.16762.13243' = @{Long = 'Exchange Server 2019 CU14'; Short = 'Ex19CU14' }
11 | '17003.16761.13243' = @{Long = 'Exchange Server 2019 CU13'; Short = 'Ex19CU13' }
12 | '17003.16760.13243' = @{Long = 'Exchange Server 2019 CU12'; Short = 'Ex19CU12' }
13 | '17003.16759.13242' = @{Long = 'Exchange Server 2019 CU11'; Short = 'Ex19CU11' }
14 |
15 | '15334.16223.13243' = @{Long = 'Exchange Server 2016 CU23'; Short = 'Ex16CU23' }
16 | '15334.16222.13242' = @{Long = 'Exchange Server 2016 CU22'; Short = 'Ex16CU22' }
17 |
18 | '15312.16133.13237' = @{Long = 'Exchange Server 2013 CU23'; Short = 'Ex13CU23' }
19 | }
20 |
21 |
22 | $exchangeVersion = $null
23 |
24 | # Forest version
25 | # This script retrieves the version of the Active Directory schema used by Exchange.
26 | $NetBiosDomainName = 'varunagroup'
27 |
28 | $RootDSE = ([ADSI]"").distinguishedName
29 | $schemaVersion = ([ADSI]"LDAP://CN=ms-Exch-Schema-Version-Pt,CN=Schema,CN=Configuration,$RootDSE").rangeUpper.ToString()
30 | $adObjectVersion = ([ADSI]"LDAP://cn=$($NetBiosDomainName),cn=Microsoft Exchange,cn=Services,cn=Configuration,$RootDSE").objectVersion.ToString()
31 | $NetBiosDomainNameObjectVersion = ([ADSI]"LDAP://CN=Microsoft Exchange System Objects,$RootDSE").objectVersion.ToString()
32 |
33 | Write-Host ('Schema rangeUpper : {0}' -f $schemaVersion)
34 | Write-Host ('Forest objectVersion: {0}' -f $adObjectVersion )
35 |
36 | # Domain version
37 |
38 | $RootDSE = ([ADSI]"").distinguishedName
39 | Write-Host ('Domain objectVersion: {0}' -f $NetBiosDomainNameObjectVersion)
40 |
41 | # Exchange version
42 | $exchangeVersion = $VersionStrings[('{0}.{1}.{2}' -f $schemaVersion, $adObjectVersion, $NetBiosDomainNameObjectVersion)].Long
43 | if ($null -eq $exchangeVersion) {
44 | Write-Host 'No matching Exchange schema/object version found!'
45 | }
46 | else {
47 | Write-Host ('Exchange Version : {0}' -f $exchangeVersion)
48 | }
--------------------------------------------------------------------------------
/Exchange Server/Get-ExchangeEnvironmentReport/EnvironmentReport.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-family: Verdana, Geneva, Arial, Helvetica, sans-serif;
3 | font-size: 10pt;
4 | }
5 | h1 {
6 | font-family: Verdana, Geneva, Arial, Helvetica, sans-serif;
7 | font-size: 16px;
8 | font-weight
9 | }
10 | h2 {
11 | font-family: Verdana, Geneva, Arial, Helvetica, sans-serif;
12 | font-size: 14px;
13 | }
14 | h3 {
15 | font-family: Verdana, Geneva, Arial, Helvetica, sans-serif;
16 | font-size: 12px;
17 | }
18 | h4 {
19 | font-family: Verdana, Geneva, Arial, Helvetica, sans-serif;
20 | font-size: 10px;
21 | }
22 |
23 | /* Header, first table row showing labels for totals */
24 | table.header {
25 | font-family: Verdana, Geneva, Arial, Helvetica, sans-serif;
26 | border: 0px;
27 | padding: 3px;
28 | font-size: 11px;
29 | }
30 | table tr.header {
31 | background: #009900;
32 | }
33 | table th.header {
34 | font-family: Verdana, Geneva, Arial, Helvetica, sans-serif;
35 | color: #ffffff;
36 | }
37 |
38 | /* Site overview */
39 | table.overview {
40 | font-family: Verdana, Geneva, Arial, Helvetica, sans-serif;
41 | font-size: 11px;
42 | font-weicht: bold;
43 | padding: 3px;
44 | border: 0px;
45 | width: 100%
46 | }
47 | table th.overview {
48 | color: #ffffff;
49 | }
50 |
51 | table tr.overviewcolheader {
52 | background: #000099;
53 | }
54 | table tr.overviewcolsubheader {
55 | background: #0000FF;
56 | }
57 |
58 | table tr.pre2007overviewcolheader {
59 | background: #880099;
60 | }
61 | table tr.pre2007overviewcolsubheader {
62 | background: #8800CC;
63 | }
64 |
65 | table tr.alternaterow {
66 | background: #dddddd
67 | }
68 | table td.roledata {
69 | text-align: center;
70 | background: #00FF00;
71 | }
72 |
73 |
74 | /* Subheader, Exchange Server Versions, Org and roles */
75 | table tr.subheader {
76 | background: #009900;
77 | }
78 | table th.subheader {
79 | font-family: Verdana, Geneva, Arial, Helvetica, sans-serif, monospace, Lucida Console;
80 | color: #ffffff;
81 | font-size: 11px;
82 | text-align: center;
83 | }
84 |
85 | /* Header data, Mailbox numbers in header table */
86 | table tr.headerdata {
87 | background: #dddddd;
88 | }
89 |
90 | table td.headerdata {
91 | font-family: Verdana, Geneva, Arial, Helvetica, sans-serif;
92 | color: #000000;
93 | font-size: 11px;
94 | text-align: center;
95 | }
96 |
97 | table.dagsummary {
98 | border: 0px;
99 | padding: 3px;
100 | width: 100%;
101 | font-size:8pt;
102 | font-family: Verdana, Geneva, Arial, Helvetica, sans-serif;
103 | text-align: center;
104 | }
105 | table tr.dagsummary {
106 | text-align: center;
107 | background: #FF8000;
108 | }
109 | table tr.dagsummarynondag {
110 | text-align: center;
111 | background: #FF8000;
112 | }
113 |
114 | table.databases {
115 | border: 0px;
116 | padding: 3px;
117 | width: 100%;
118 | font-size:9pt;
119 | font-family: Verdana, Geneva, Arial, Helvetica, sans-serif;
120 | }
121 | table tr.databases {
122 | background: #FFD700;
123 | text-align: center;
124 | }
125 |
126 | .dagtablefooter {
127 | font-family: Verdana, Geneva, Arial, Helvetica, sans-serif;
128 | font-size:8pt;
129 | }
130 |
131 | .center {
132 | text-align: center;
133 | }
134 |
135 | .alert {
136 | color: red;
137 | }
138 |
139 | .highlight {
140 | color: violet;
141 | }
142 |
143 | .tooltip {
144 | position: relative;
145 | display: inline-block;
146 | border-bottom: 1px dotted black;
147 | }
148 |
149 | .tooltip .tooltiptext {
150 | visibility: hidden;
151 | width: 120px;
152 | background-color: #555;
153 | color: #fff;
154 | text-align: center;
155 | border-radius: 6px;
156 | padding: 5px 0;
157 | position: absolute;
158 | z-index: 1;
159 | bottom: 125%;
160 | left: 50%;
161 | margin-left: -60px;
162 | opacity: 0;
163 | transition: opacity 0.3s;
164 | }
165 |
166 | .tooltip .tooltiptext::after {
167 | content: "";
168 | position: absolute;
169 | top: 100%;
170 | left: 50%;
171 | margin-left: -5px;
172 | border-width: 5px;
173 | border-style: solid;
174 | border-color: #555 transparent transparent transparent;
175 | }
176 |
177 | .tooltip:hover .tooltiptext {
178 | visibility: visible;
179 | opacity: 1;
180 | }
--------------------------------------------------------------------------------
/Exchange Server/Get-ExchangeEnvironmentReport/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) Thomas Stensitzki
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/Exchange Server/Get-ExchangeEnvironmentReport/README.md:
--------------------------------------------------------------------------------
1 | # Get-ExchangeEnvironmentReport
2 |
3 | Creates an HTML report describing the On-Premises Exchange environment.
4 |
5 | Based on the original 1.6.2 version by Steve Goodman
6 |
7 | ## Description
8 |
9 | This script creates an HTML report showing the following information about an Exchange 2019, 2016, 2013, 2010, and, to a lesser extent, 2007 and 2003 environment.
10 |
11 | The HTML report requires the CSS file that is part of this repository for proper HTML formatting.
12 |
13 | The report shows the following:
14 |
15 | * As summary
16 | * Total number of servers per Exchange Server version
17 | * Total number of mailboxes per On-Premises Exchange Server version, Office 365, and Exchange Organisation
18 | * Total number of Exchange Server functional roles
19 |
20 | * Per Active Directory Site
21 | * Total number of mailboxes
22 | * Internal, External, and CAS Array names
23 | * Exchange Server computers
24 | * Product version
25 | * Service Pack, Update Rollup, and/or Cumulative Update
26 | * Number of preferred and maximum active databases
27 | * Functional Roles
28 | * Operating System with Service Pack
29 |
30 | * Per Database Availability Group
31 | * Total number of member servers
32 | * List of member servers
33 | * DAG databases
34 | * Number of mailboxes and average mailbox size
35 | * Number of archive mailboxes and average archive mailbox size
36 | * Database size
37 | * Database whitespace
38 | * Disk space available for database and log file volume
39 | * Last full backup timestamp
40 | * Circular logging enabled
41 | * Mailbox server hosting an active copy
42 | * List of mailbox servers hosting database copies
43 |
44 | * Per Database (Non-DAG, pre-DAG Exchange Server)
45 | * Storage group and database name
46 | * Server name hosting the database
47 | * Number of mailboxes and average mailbox size
48 | * Number of archive mailboxes and average archive mailbox size
49 | * Database size
50 | * Database whitespace
51 | * Disk space available for database and log file volume
52 | * Last full backup timestamp
53 | * Circular logging enabled
54 |
55 | The PowerShell script does not gather information on public folders or analyzes Exchange cluster technologies like Exchange Server 2007/2003 CCR/SCR.
56 |
57 | ## Requirements
58 |
59 | * Exchange Server Management Shell 2010 or newer
60 | * WMI and Remote Registry access from the computer running the script to all internal Exchange Servers
61 | * CSS file for HTML formatting
62 |
63 | ## Release
64 |
65 | * 2.0 : Initial Community Release of the updated original script
66 | * 2.1 : Table header label updated for a more consistent labeling
67 | * 2.2 : Bug fixes and enhancements
68 | * CCS fixes for Html header tags (issue #5)
69 | * New script parameter _ShowDriveNames_ added to optionally show drive names for EDB/LOG file paths in database table (issue #4)
70 | * Exchange organization name added to report header
71 | * 2.4 : Bug fix for empty ExternalUrl parameter values
72 | * 2.5 : Issue #6 fixed - CSS file check added
73 | * 2.7 : ShowDisconnectedMailboxCount added
74 |
75 | ## Example Report
76 |
77 | 
78 |
79 | ## Parameters
80 |
81 | ### HTMLReport
82 |
83 | File name to write HTML Report to
84 |
85 | ### SendMail
86 |
87 | Send Mail after completion. Set to $True to enable. If enabled, -MailFrom, -MailTo, -MailServer are mandatory
88 |
89 | ### MailFrom
90 |
91 | Email address to send from. Passed directly to Send-MailMessage as -From
92 |
93 | ### MailTo
94 |
95 | Email address to send to. Passed directly to Send-MailMessage as -To
96 |
97 | ### MailServer
98 |
99 | SMTP Mail server to attempt to send through. Passed directly to Send-MailMessage as -SmtpServer
100 |
101 | ### ViewEntireForest
102 |
103 | By default, true. Set the option in Exchange 2007 or 2010 to view all Exchange servers and recipients in the forest.
104 |
105 | ### ServerFilter
106 |
107 | Use a text based string to filter Exchange Servers by, e.g., NL-*
108 | Note the use of the wildcard (*) character to allow for multiple matches.
109 |
110 | ### ShowDriveNames
111 |
112 | Include drive names of EDB file path and LOG file folder in database report table
113 |
114 | ### ShowDisconnectedMailboxCount
115 |
116 | Show the number of disconnected mailboxes in the database report table
117 |
118 | ### ShowProvisioningStatus
119 |
120 | Show IsExludedFromProvisioning or IsExcludedFromProvisioningByOperator status in the report
121 |
122 | ### CssFileName
123 |
124 | The filename containing the Cascading Style Sheet (CSS) information fpr the HTML report
125 | Default: EnvironmentReport.css
126 |
127 | ## Examples
128 |
129 | ### Example 1
130 |
131 | Generate an HTML report and send the result as HTML email with attachment to the specified recipient using a dedicated smart host
132 |
133 | ``` PowerShell
134 | .\Get-ExchangeEnvironmentReport.ps1 -HTMReport ExchangeEnvironment.html -SendMail -ViewEntireForet $true -MailFrom roaster@mcsmemail.de -MailTo grillmaster@mcsmemail.de -MailServer relay.mcsmemail.de
135 | ```
136 |
137 | ### Example 2
138 |
139 | Generate an HTML report and save the report as 'report.html'
140 |
141 | ``` PowerShell
142 | .\Get-ExchangeEnvironmentReport.ps1 -HTMLReport .\report.html
143 | ```
144 |
145 | ### Example 3
146 |
147 | Generate the HTML report including EDB and LOG drive names
148 |
149 | ``` PowerShell
150 | .\Get-ExchangeEnvironmentReport.ps1 -ShowDriveNames -HTMLReport .\report.html
151 | ```
152 |
153 | ### Example 4
154 |
155 | Generate the HTML report using a custom CSS file
156 |
157 | ``` PowerShell
158 | .\Get-ExchangeEnvironmentReport.ps1 -HTMLReport .\report.html -CssFileName MyCustomCSSFile.css
159 | ```
160 |
161 | ## Note
162 |
163 | THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE
164 | RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER.
165 |
166 | ## Credits
167 |
168 | Based on the original 1.6.2 version by Steve Goodman.
169 |
170 | ### Stay connected
171 |
172 | * Blog: [https://blog.granikos.eu](https://blog.granikos.eu)
173 | * Bluesky: [https://bsky.app/profile/stensitzki.eu](https://bsky.app/profile/stensitzki.eu)
174 | * LinkedIn: [https://www.linkedin.com/in/thomasstensitzki](https://www.linkedin.com/in/thomasstensitzki)
175 | * YouTube: [https://www.youtube.com/@ThomasStensitzki](https://www.youtube.com/@ThomasStensitzki)
176 | * LinkTree: [https://linktr.ee/stensitzki](https://linktr.ee/stensitzki)
177 |
178 | For more Microsoft 365, Cloud Security, and Exchange Server stuff checkout services provided by Granikos
179 |
180 | * Website: [https://granikos.eu](https://www.granikos.eu)
181 | * Bluesky: [https://bsky.app/profile/granikos.eu](https://bsky.app/profile/granikos.eu)
182 |
--------------------------------------------------------------------------------
/Exchange Server/Get-ExchangeEnvironmentReport/Run-ExchangeEnvironmentReport.ps1:
--------------------------------------------------------------------------------
1 | .\Get-ExchangeEnvironmentReport.ps1 -HTMLReport ExchangeEnvironment.html -SendMail -ViewEntireForest $true -MailFrom roaster@mcsmemail.de -MailTo grillmaster@mcsmemail.de -MailServer relay.mcsmemail.de
--------------------------------------------------------------------------------
/Exchange Server/Get-ExchangeEnvironmentReport/images/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Apoc70/PowerShell-Scripts/ef43113b389502d309e246891fa9c7a3ad5a147e/Exchange Server/Get-ExchangeEnvironmentReport/images/screenshot.png
--------------------------------------------------------------------------------
/Exchange Server/Get-ExchangeServerCertificateReport/README.md:
--------------------------------------------------------------------------------
1 | # Import-EdgeSubscription.ps1
2 |
3 | This script creates an Exchange Server SSL Certificate Report
4 |
5 | ## Description
6 |
7 | The script queries all Exchange Servers an fetches the computer certificates.
8 | It generates a table report of the available certificates per server.
9 | The scripts sorts the certificates by subject name and expiry date.
10 |
11 | ## Parameters
12 |
13 | ### SendMail
14 |
15 | Send the report as an HTML email.
16 |
17 | ### MailFrom
18 |
19 | Sender address for result summary.
20 |
21 | ### MailTo
22 |
23 | Recipient address for result summary.
24 |
25 | ### MailServer
26 |
27 | SMTP Server address for sending result summary.
28 |
29 | ## Example
30 |
31 | ``` PowerShell
32 | .\Get-ExchangeCertificateReport.ps1
33 | ```
34 |
35 | Generate an Html report file in the script directory.
36 |
37 | ## Credits
38 |
39 | Originally written by: Paul Cunningham
40 |
41 | Updated by: Thomas Stensitzki
42 |
43 | ### Stay connected
44 |
45 | * Blog: [https://blog.granikos.eu](https://blog.granikos.eu)
46 | * Bluesky: [https://bsky.app/profile/stensitzki.eu](https://bsky.app/profile/stensitzki.eu)
47 | * LinkedIn: [https://www.linkedin.com/in/thomasstensitzki](https://www.linkedin.com/in/thomasstensitzki)
48 | * YouTube: [https://www.youtube.com/@ThomasStensitzki](https://www.youtube.com/@ThomasStensitzki)
49 | * LinkTree: [https://linktr.ee/stensitzki](https://linktr.ee/stensitzki)
50 |
51 | For more Microsoft 365, Cloud Security, and Exchange Server stuff checkout services provided by Granikos
52 |
53 | * Website: [https://granikos.eu](https://granikos.eu)
54 | * Bluesky: [https://bsky.app/profile/granikos.eu](https://bsky.app/profile/granikos.eu)
55 |
--------------------------------------------------------------------------------
/Exchange Server/Get-ExchangeServerCertificateReport/styles.css:
--------------------------------------------------------------------------------
1 | @charset "UTF-8";
2 | body {
3 | font-family: Verdana, Geneva, Arial, Helvetica, sans-serif;
4 | font-size: 10pt;
5 | }
6 | h1 {
7 | font-family: Verdana, Geneva, Arial, Helvetica, sans-serif;
8 | font-size: 16px;
9 | }
10 | h2 {
11 | font-family: Verdana, Geneva, Arial, Helvetica, sans-serif;
12 | font-size: 14px;
13 | }
14 | h3 {
15 | font-family: Verdana, Geneva, Arial, Helvetica, sans-serif;
16 | font-size: 12px;
17 | }
18 | table {
19 | font: 10pt Verdana, Geneva, Arial, Helvetica, sans-serif;
20 | border: 1px solid black;
21 | border-collapse:
22 | collapse; font-size: 10pt;
23 | }
24 | table th {
25 | border: 1px solid black;
26 | background: #dddddd;
27 | padding: 5px;
28 | color: #000000;
29 | }
30 | table td {
31 | border: 1px solid black;
32 | padding: 5px;
33 | }
34 | table td.pass {
35 | background: #7FFF00;
36 | }
37 | table td.warn {
38 | background: #FFE600;
39 | }
40 | table td.fail {
41 | background: #FF0000; color: #ffffff;
42 | }
43 | table td.info {
44 | background: #85D4FF;
45 | }
46 | .error {
47 | background: #FF0000; color: #ffffff;
48 | }
49 | .danger {background-color: red}
50 | .warn {background-color: yellow}
51 | .ok {background-color: green}
52 | .link {
53 | font-size: 8pt;
54 | }
--------------------------------------------------------------------------------
/Exchange Server/Get-NewPublicFolders/Get-NewPublicFolders.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | .SYNOPSIS
3 |
4 | Get all public folders created during the las X days
5 |
6 | Thomas Stensitzki
7 |
8 | THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE
9 | RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER.
10 |
11 | Version 2.0, 2024-10-15
12 |
13 | Please send ideas, comments and suggestions to support@granikos.eu
14 |
15 | .LINK
16 | https://scripts.granikos.eu
17 |
18 | .DESCRIPTION
19 |
20 | This script gathers all public folders created during the last X days and exportes the gathered data to a CSV file.
21 |
22 | .NOTES
23 |
24 | Requirements Legacy Public Folder
25 | - Windows Server 2008R2+
26 | - Exchange 2010/2013 Management Shell (aka EMS)
27 |
28 | Requirements Modern Public Folder
29 | - Windows Server 2012R2+
30 | - Exchange 2013+ Management Shell (aka EMS)
31 |
32 | Revision History
33 | --------------------------------------------------------------------------------
34 | 1.0 Initial community release
35 | 2.0 Enhanced reporting for modern public folders
36 |
37 | .PARAMETER Days
38 | Number of last X days to filter newly created public folders. Default: 14
39 |
40 | .PARAMETER Legacy
41 | Switch to define that you want to query legacy public folders
42 |
43 | .PARAMETER ServerName
44 | Name of Exchange server hostingl egacy public folders
45 |
46 | .EXAMPLE
47 | Query legacy public folder server MYPFSERVER01 for all public folders created during the last 31 days
48 |
49 | .\Get-NewPublicFolders.ps1 -Days 31 -ServerName MYPFSERVER01 -Legacy
50 |
51 | .EXAMPLE
52 | Query modern public folders for all public folders created during the last 31 days
53 |
54 | .\Get-NewPublicFolders.ps1 -Days 31
55 |
56 | #>
57 | [CmdletBinding()]
58 | param(
59 | [int]$Days = 30,
60 | [Parameter(ParameterSetName='Legacy')]
61 | [switch]$Legacy,
62 | [Parameter(ParameterSetName='Legacy')]
63 | [string]$ServerName = 'MYSERVER',
64 | [switch]$FetchFolderPermissions,
65 | [string]$ExcludeUser = 'ExchangePublicFolderManager',
66 | [switch]$SendMail,
67 | [string]$MailFrom = "",
68 | [string]$MailTo = "",
69 | [string]$MailServer = ""
70 | )
71 |
72 | # Fetch new public folders created over the last 7 days
73 | $CreationDate = (Get-Date).AddDays(-($Days))
74 |
75 | $ScriptDir = Split-Path $script:MyInvocation.MyCommand.Path
76 |
77 | # Use current date as file timestamp
78 | $now = Get-Date -Format 'yyyy-MM-dd'
79 | $CsvFilePath = Join-Path -Path $ScriptDir -ChildPath ('NewPublicFolders {0}.csv' -f ($now))
80 |
81 | $cssFile = Join-Path -Path $ScriptDir -ChildPath styles.css
82 | $now = Get-Date -Format F
83 | $reportTitle = "New Public Folder Report - $($now)"
84 |
85 | # Gather legacy public folder statistics
86 | if($Legacy) {
87 | # Query legacy public folders
88 |
89 | Get-PublicFolderStatistics -Server $ServerName | Where-Object{$_.CreationTime -ge $CreationDate} | Select-Object -Property FolderPath,Name,ItemCount | Sort-Object -Property FolderPath | Export-Csv -Path $CsvFilePath -Encoding UTF8 -NoTypeInformation -Force -Delimiter '|'
90 | }
91 | else {
92 | # Query modern public folders
93 |
94 | $PublicFolder = Get-PublicFolderStatistics -ResultSize Unlimited | Where-Object{$_.CreationTime -ge $CreationDate} | Select-Object -Property FolderPath,Name,ItemCount,CreationTime,LastModificationTime,EntryId | Sort-Object -Property FolderPath
95 |
96 | $exportFolders = New-Object System.Collections.ArrayList -ArgumentList ( ($PublicFolder | Measure-Object).Count )
97 |
98 | if($FetchFolderPermissions) {
99 |
100 | Write-Verbose 'Fetching Public Folder Permissions'
101 |
102 | foreach($Folder in $PublicFolder) {
103 |
104 | Write-Verbose ('{0}' -f $Folder.FolderPath)
105 | Write-Verbose ('{0}' -f $Folder.EntryId)
106 |
107 | $folderPermPath = $Folder.EntryId
108 |
109 | Write-Verbose ('Processiong {0}' -f $folderPermPath)
110 |
111 | $folderPermissions = Get-PublicFolderClientPermission $folderPermPath | Where-Object{ ($_.AccessRights -like 'Owner') -and (([string]$_.User) -ne $ExcludeUser)} | Select-Object User
112 |
113 | if( ($folderPermissions | Measure-Object).Count -ne 0) {
114 | $ownerList = [string]::Join('; ',$folderPermissions.User.DisplayName)
115 | }
116 | else {
117 | $ownerList = 'No users with OWNER role'
118 | }
119 |
120 | $property = [ordered]@{
121 | FolderPath = $Folder.FolderPath
122 | Name = $Folder.Name
123 | ItemCount = $Folder.ItemCount
124 | CreationTime = $Folder.CreationTime
125 | LastModificationTime = $Folder.LastModificationTime
126 | Owner = $ownerList
127 | }
128 |
129 | $folderObject = New-Object -TypeName PSObject -Property $property
130 |
131 | $null = $exportFolders.Add($folderObject)
132 | }
133 |
134 | }
135 | else {
136 |
137 | foreach($Folder in $PublicFolder) {
138 |
139 | $property = [ordered]@{
140 | FolderPath = $Folder.FolderPath
141 | Name = $Folder.Name
142 | ItemCount = $Folder.ItemCount
143 | CreationTime = $Folder.CreationTime
144 | LastModificationTime = $Folder.LastModificationTime
145 | }
146 |
147 | $folderObject = New-Object -TypeName PSObject -Property $property
148 |
149 | $null = $exportFolders.Add($folderObject)
150 | }
151 | }
152 | }
153 |
154 | # Export to CSV
155 | if(($PublicFolder | Measure-Object).Count -gt 0) {
156 |
157 | $exportFolders | Export-Csv -Path $CsvFilePath -Encoding UTF8 -NoTypeInformation -Force -Delimiter '|'
158 |
159 | $PublicFolderCount = ($PublicFolder | Measure-Object).Count
160 | }
161 | else {
162 | $PublicFolderCount = 0
163 |
164 | Write-Verbose -Message 'Nothing to export.'
165 | }
166 |
167 | if($SendMail) {
168 |
169 | if( $PublicFolderCount -gt 0) {
170 | # new public fodlers found
171 | $html = $exportFolders | ConvertTo-Html -Fragment -PreContent "New Public Folders
"
172 | }
173 | else {
174 | # no new public folders found
175 | $html = 'No new public folders found.
'
176 | }
177 |
178 | $head = @"
179 |
180 | $($reportTitle)
181 |
182 | $($reportTitle)
183 | Public folder timeframe: $($Days) days
184 | New public folders created: $($PublicFolderCount)
185 | Excluded from lost of owners: $($ExcludeUser)
186 | "@
187 |
188 | [string]$htmlreport = ConvertTo-Html -Body $html -Head $head -Title $reportTitle
189 |
190 | Send-Mail -From $MailFrom -To $MailTo -SmtpServer $MailServer -MessageBody $htmlreport -Subject $reportTitle
191 | }
192 |
193 | exit 0
--------------------------------------------------------------------------------
/Exchange Server/Get-NewPublicFolders/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Thomas Stensitzki
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Exchange Server/Get-NewPublicFolders/README.md:
--------------------------------------------------------------------------------
1 | # Get-NewPublicFolders.ps1
2 |
3 | Get all public folders created during the last X days
4 |
5 | ## Description
6 |
7 | This script gathers all public folders created during the last X days and exportes the gathered data to a CSV file.
8 |
9 | ## Parameters
10 |
11 | ### Days
12 |
13 | Number of last X days to filter newly created public folders. Default: 14
14 |
15 | ### Legacy
16 |
17 | Switch to define that you want to query legacy public folders
18 |
19 | ### ServerName
20 |
21 | Name of Exchange server hostingl egacy public folders
22 |
23 | ## Examples
24 |
25 | ``` PowerShell
26 | .\Get-NewPublicFolders.ps1 -Days 31 -ServerName MYPFSERVER01 -Legacy
27 | ```
28 |
29 | Query legacy public folder server MYPFSERVER01 for all public folders created during the last 31 days
30 |
31 | ``` PowerShell
32 | .\Get-NewPublicFolders.ps1 -Days 31
33 | ```
34 |
35 | Query modern public folders for all public folders created during the last 31 days
36 |
37 | ## Note
38 |
39 | THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE
40 | RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER.
41 |
42 | ## Credits
43 |
44 | Written by: Thomas Stensitzki
45 |
46 | ### Stay connected
47 |
48 | * Blog: [https://blog.granikos.eu](https://blog.granikos.eu)
49 | * Bluesky: [https://bsky.app/profile/stensitzki.eu](https://bsky.app/profile/stensitzki.eu)
50 | * LinkedIn: [https://www.linkedin.com/in/thomasstensitzki](https://www.linkedin.com/in/thomasstensitzki)
51 | * YouTube: [https://www.youtube.com/@ThomasStensitzki](https://www.youtube.com/@ThomasStensitzki)
52 | * LinkTree: [https://linktr.ee/stensitzki](https://linktr.ee/stensitzki)
53 |
54 | For more Microsoft 365, Cloud Security, and Exchange Server stuff checkout services provided by Granikos
55 |
56 | * Website: [https://granikos.eu](https://granikos.eu)
57 | * Bluesky: [https://bsky.app/profile/granikos.eu](https://bsky.app/profile/granikos.eu)
58 |
--------------------------------------------------------------------------------
/Exchange Server/Get-NewPublicFolders/styles.css:
--------------------------------------------------------------------------------
1 | @charset "UTF-8";
2 | body {
3 | font-family: Verdana, Geneva, Arial, Helvetica, sans-serif;
4 | font-size: 10pt;
5 | }
6 | h1 {
7 | font-family: Verdana, Geneva, Arial, Helvetica, sans-serif;
8 | font-size: 16px;
9 | }
10 | h2 {
11 | font-family: Verdana, Geneva, Arial, Helvetica, sans-serif;
12 | font-size: 14px;
13 | }
14 | h3 {
15 | font-family: Verdana, Geneva, Arial, Helvetica, sans-serif;
16 | font-size: 12px;
17 | }
18 | table {
19 | font: 10pt Verdana, Geneva, Arial, Helvetica, sans-serif;
20 | border: 1px solid black;
21 | border-collapse:
22 | collapse; font-size: 10pt;
23 | }
24 | table th {
25 | border: 1px solid black;
26 | background: #dddddd;
27 | padding: 5px;
28 | color: #000000;
29 | }
30 | table td {
31 | border: 1px solid black;
32 | padding: 5px;
33 | }
34 | table td.pass {
35 | background: #7FFF00;
36 | }
37 | table td.warn {
38 | background: #FFE600;
39 | }
40 | table td.fail {
41 | background: #FF0000; color: #ffffff;
42 | }
43 | table td.info {
44 | background: #85D4FF;
45 | }
46 | .error {
47 | background: #FF0000; color: #ffffff;
48 | }
49 | .danger {background-color: red}
50 | .warn {background-color: yellow}
51 | .ok {background-color: green}
--------------------------------------------------------------------------------
/Exchange Server/Get-PublicFolderCustomFormItems/Get-PublicFolderCustomFormItems.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | .SYNOPSIS
3 | Get public folder items of a specific type and export the results to a CSV file.
4 |
5 | .DESCRIPTION
6 | This script retrieves all public folders under a specified path, counts the items of a specific type in each folder, and exports the results to a CSV file.
7 | It is particularly useful for counting custom forms in public folders in an Exchange environment.
8 | You must have the Exchange Management Shell or the Exchange Online PowerShell module installed to run this script.
9 |
10 | .LINK
11 | https://granikos.eu/go/ZtsJ
12 |
13 | .PARAMETER PublicFolderPath
14 | The path to the public folder from which to retrieve items.
15 |
16 | .PARAMETER ItemType
17 | The type of item to count in the public folders. Default is 'IPM.Post.FORMNAME'.
18 | Adjust this parameter to specify the item type you are interested in.
19 | You can find the form names by using the `Get-PublicFolderItemStatistics` cmdlet.
20 |
21 | .EXAMPLE
22 | .\Get-PublicFolderCustomFormItems.ps1 -PublicFolderPath '\Department\HR' -ItemType 'IPM.Post.FORMNAME'
23 |
24 | Retrieves all public folders under '\Department\HR', counts the items of type 'IPM.Post.FORMNAME' in each folder, and exports the results to a CSV file. Adjust the name of the item type as needed.
25 | #>
26 |
27 | [CmdletBinding()]
28 | param(
29 | [Parameter(
30 | Mandatory=$true,
31 | HelpMessage = "Path to public folder")]
32 | [ValidateNotNull()]
33 | [string]$PublicFolderPath,
34 | [string]$ItemType = 'IPM.Post.FORMNAME'
35 | )
36 |
37 | $i = 1
38 | $scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
39 |
40 | $publicFolders = Get-PublicFolder $PublicFolderPath -Recurse -ResultSize Unlimited
41 |
42 | # Ensure we have some public folders to work with
43 | $publicFolderCount = ($publicFolders | Measure-Object).Count
44 |
45 | Write-Host ('Fetched {0} public folders' -f $publicFolderCount )
46 |
47 | # Initialize an array to hold the folder information
48 | $objFolderItems = @()
49 |
50 | if($publicFolderCount -gt 0){
51 |
52 | foreach($folder in $publicFolders) {
53 |
54 | Write-Progress -Activity ('Working on ({2}/{1}): {0}' -f $folder.Identity, $publicFolderCount, $i ) -PercentComplete (($i / $publicFolderCount) * 100)
55 |
56 | $folderItems = Get-PublicFolderItemStatistics -Identity $folder.Identity
57 |
58 | $folderItemTypeCount = ($folderItems | Group-Object ItemType | Where-Object{$_.name -eq $ItemType}).Count
59 |
60 | $objFolderItems += [PSCustomObject]@{
61 | FolderPath = $folder.Identity
62 | ItemCount = ($FolderItems |Measure-Object).Count
63 | ItemTypeCount =$folderItemTypeCount
64 | ItemCreationTime = ($folderItems | ?{$_.ItemType -eq $ItemType} | Sort-Object CreationTime -Descending | Select-Object -First 1).CreationTime
65 | ItemLastModificationTime = ($folderItems | ?{$_.ItemType -eq $ItemType} | Sort-Object LastModificationTime -Descending | Select-Object -First 1).LastModificationTime
66 | }
67 |
68 | $i++
69 | }
70 |
71 | # Output the results to the console
72 | $objFolderItems | Format-Table -autosize
73 |
74 | # Export the results to a CSV file
75 | $objFolderItems | export-csv -Path (Join-Path -Path $scriptDir -ChildPath 'CustomFormsReport.csv') -Encoding UTF8 -NoTypeInformation -Force
76 |
77 | }
78 | else {
79 | Write-Host 'No public folders found.'
80 | }
--------------------------------------------------------------------------------
/Exchange Server/Get-PublicFolderCustomFormItems/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) Thomas Stensitzki
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Exchange Server/Get-PublicFolderCustomFormItems/README.md:
--------------------------------------------------------------------------------
1 | # Get-PublicFolderCustomFormItems.ps1
2 |
3 | Get public folder items of a specific type and export the results to a CSV file.
4 |
5 | ## Description
6 |
7 | This script retrieves all public folders under a specified path, counts the items of a specific type in each folder, and exports the results to a CSV file.
8 | It is particularly useful for counting custom forms in public folders in an Exchange environment.
9 | You must have the Exchange Management Shell or the Exchange Online PowerShell module installed to run this script.
10 |
11 | ## Parameters
12 |
13 | ### PublicFolderPath
14 |
15 | The path to the public folder from which to retrieve items.
16 |
17 | ### ItemType
18 |
19 | The type of item to count in the public folders. Default is 'IPM.Post.FORMNAME'.
20 | Adjust this parameter to specify the item type you are interested in.
21 | You can find the form names by using the `Get-PublicFolderItemStatistics` cmdlet.
22 |
23 | ## Examples
24 |
25 | ``` PowerShell
26 | .\Get-PublicFolderCustomFormItems.ps1 -PublicFolderPath '\Department\HR' -ItemType 'IPM.Post.FORMNAME'
27 | ```
28 |
29 | Retrieves all public folders under '\Department\HR', counts the items of type 'IPM.Post.FORMNAME' in each folder, and exports the results to a CSV file. Adjust the name of the item type as needed.
30 |
31 | ## Note
32 |
33 | THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE
34 | RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER.
35 |
36 | ## Credits
37 |
38 | Written by: Thomas Stensitzki
39 |
40 | ### Stay connected
41 |
42 | * Blog: [https://blog.granikos.eu](https://blog.granikos.eu)
43 | * Bluesky: [https://bsky.app/profile/stensitzki.eu](https://bsky.app/profile/stensitzki.eu)
44 | * LinkedIn: [https://www.linkedin.com/in/thomasstensitzki](https://www.linkedin.com/in/thomasstensitzki)
45 | * YouTube: [https://www.youtube.com/@ThomasStensitzki](https://www.youtube.com/@ThomasStensitzki)
46 | * LinkTree: [https://linktr.ee/stensitzki](https://linktr.ee/stensitzki)
47 |
48 | For more Microsoft 365, Cloud Security, and Exchange Server stuff checkout services provided by Granikos
49 |
50 | * Website: [https://granikos.eu](https://granikos.eu)
51 | * Bluesky: [https://bsky.app/profile/granikos.eu](https://bsky.app/profile/granikos.eu)
52 |
--------------------------------------------------------------------------------
/Exchange Server/Get-RemoteSmtpServers/Get-RemoteSmtpServersTls.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | .SYNOPSIS
3 | Fetch all remote SMTP servers from Exchange receive connector logs, establishing a TLS connection
4 |
5 | Thomas Stensitzki
6 |
7 | THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE
8 | RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER.
9 |
10 | Version 1.0, 2021-06-04
11 |
12 | Ideas, comments and suggestions to support@granikos.eu
13 |
14 | .LINK
15 | http://scripts.granikos.eu
16 |
17 | .DESCRIPTION
18 | This scripts fetches remote SMTP servers by searching the Exchange receive connector logs for successful TLS connections.
19 | Fetched servers can be exported to a single CSV file for all receive connectors across Exchange Servers or
20 | exported to a separate CSV file per Exchange Server.
21 | You can use this script to identify remote servers connecting using TLS 1.0 or TLS 1.1.
22 |
23 |
24 | .NOTES
25 | Requirements
26 | - Exchange Server 2013+
27 |
28 | Revision History
29 | --------------------------------------------------------------------------------
30 | 1.0 Initial community release
31 |
32 | .PARAMETER Servers
33 | List of Exchange servers, modern and legacy Exchange servers cannot be mixed
34 |
35 | .PARAMETER Backend
36 | Search backend transport (aka hub transport) log files, instead of frontend transport, which is the default
37 |
38 | .PARAMETER ToCsv
39 | Export search results to a single CSV file for all servers
40 |
41 | .PRAMATER ToCsvPerServer
42 | Export search results to a separate CSV file per servers
43 |
44 | .PARAMETER AddDays
45 | File selection filter, -5 will select log files changed during the last five days. Default: -10
46 |
47 |
48 | .EXAMPLE
49 | .\Get-RemoteSmtpServers.ps1 -Servers SRV01,SRV02 -LegacyExchange -AddDays -4 -ToCsv
50 |
51 | Search legacy Exchange servers SMTP receive log files for the last 4 days and save search results in a single CSV file
52 |
53 | #>
54 |
55 |
56 | [CmdletBinding()]
57 | param(
58 | $Servers = @('MYEXCHANGE'),
59 | [switch]$Backend,
60 | [switch]$ToCsv,
61 | [switch]$ToCsvPerServer,
62 | [int]$AddDays = -10
63 | )
64 |
65 | $ScriptDir = Split-Path -Path $script:MyInvocation.MyCommand.Path
66 |
67 | $CsvFileName = ('RemoteSMTPServersTls-%SERVER%-%ROLE%-%TLS%-{0}.csv' -f ((Get-Date).ToString('s').Replace(':','-')))
68 |
69 | # ToDo: Update to Get-TransportServer/Get-TransportService
70 | # Currently pretty static
71 | $BackendPath = '\\%SERVER%\d$\Program Files\Microsoft\Exchange Server\V15\TransportRoles\Logs\Hub\ProtocolLog\SmtpReceive'
72 | $FrontendPath = '\\%SERVER%\d$\Program Files\Microsoft\Exchange Server\V15\TransportRoles\Logs\FrontEnd\ProtocolLog\SmtpReceive'
73 |
74 | # The TLS version to search
75 | $TlsProtocols = @('TLS1_2','TLS1_1','TLS1_0')
76 |
77 | # The SMTP receive log search pattern
78 | $Pattern = '(.)*SP_PROT_%TLS%(.)*succeeded'
79 |
80 | # An empty array for storing remote servers
81 | $RemoteServers = @()
82 |
83 | # Create an empty array
84 | $RemoteServersOutput = @()
85 |
86 | function Write-RemoteServers {
87 | [CmdletBinding()]
88 | param(
89 | [string]$FilePath = ''
90 | )
91 |
92 | # sort servers
93 | $RemoteServers = $RemoteServers | Select-Object -Unique | Sort-Object
94 |
95 | if(($RemoteServers| Measure-Object).Count -gt 0) {
96 |
97 | # Create an empty array
98 | $RemoteServersOutput = @()
99 |
100 | foreach($Server in $RemoteServers) {
101 |
102 | if($Server.Trim() -ne '') {
103 | $obj = New-Object -TypeName PSObject
104 | $obj | Add-Member -MemberType NoteProperty -Name 'Remote Server' -Value $Server
105 | $RemoteServersOutput += $obj
106 | }
107 | }
108 |
109 | if($ToCsv -or $ToCsvPerServer) {
110 | # save remote servers list as csv
111 | $null = $RemoteServersOutput | Export-Csv -Path $FilePath -Encoding UTF8 -NoTypeInformation -Force -Confirm:$false
112 |
113 | Write-Verbose -Message ('Remote server list written to: {0}' -f $FilePath)
114 | }
115 |
116 | $RemoteServersOutput
117 |
118 | }
119 | else {
120 | Write-Host 'No remote servers found!'
121 | }
122 |
123 | }
124 |
125 | ## MAIN ###########################################
126 | $LogPath = $FrontendPath
127 |
128 | # Adjust CSV file name to reflect either HUB or FRONTEND transport
129 | if($Backend) {
130 | $LogPath = $BackendPath
131 | $CsvFileName = $CsvFileName.Replace('%ROLE%','HUB')
132 | }
133 | else {
134 | $CsvFileName = $CsvFileName.Replace('%ROLE%','FE')
135 | }
136 |
137 | Write-Verbose -Message ('CsvFileName: {0}' -f ($CsvFileName))
138 |
139 | # Fetch each Exchange Server server
140 | foreach($Server in $Servers) {
141 |
142 | $Server = $Server.ToUpper()
143 |
144 | $Path = $LogPath.Replace('%SERVER%', $Server)
145 |
146 | Write-Verbose -Message ('Working on Server {0} | {1}' -f $Server, $Path)
147 |
148 | # fetching log files requires an account w/ administrative access to the target server
149 | $LogFiles = Get-ChildItem -Path $Path -File | Where-Object {$_.LastWriteTime -gt (Get-Date).AddDays($AddDays)} | Select-Object -First 2
150 |
151 | $LogFileCount = ($LogFiles | Measure-Object).Count
152 |
153 | foreach($Tls in $TlsProtocols) {
154 | Write-Host ('Working on: {0}' -f $Tls)
155 | $FileCount = 1
156 |
157 | foreach($File in $LogFiles) {
158 |
159 | Write-Progress -Activity ('{3} | {4} | File [{0}/{1}] : {2}' -f $FileCount, $LogFileCount, $File.Name, $Server, $Tls) -PercentComplete(($FileCount/$LogFileCount)*100)
160 |
161 |
162 | # find results in selected log files
163 | $SearchPattern = $Pattern.Replace('%TLS%', $Tls)
164 |
165 | $results = (Select-String -Path $File.FullName -Pattern $SearchPattern)
166 |
167 | Write-Verbose -Message ('Results {0} : {1}' -f $File.FullName, ($results | Measure-Object).Count)
168 |
169 | # Get remote server information from search string result
170 | foreach($record in $results) {
171 |
172 | # Fetch remote hostname
173 | # $HostName = ($record.line -replace $Pattern,'').Replace(',','').Trim().ToUpper()
174 | $HostIp = (($record.Line).Split(',')[5]).Split(':')[0]
175 |
176 | # Try to resolve remote IP address as the line does not contain a server name
177 | $HostName = Resolve-DnsName $HostIp -ErrorAction Ignore |Select-Object -ExpandProperty NameHost
178 |
179 | if(-not $RemoteServers.Contains($HostName)) {
180 | $RemoteServers += $HostName
181 | }
182 | }
183 |
184 | $FileCount++
185 |
186 | }
187 |
188 | if($ToCsvPerServer) {
189 |
190 | $CsvFile = $CsvFileName.Replace('%SERVER%',$Server).Replace('%TLS%',$Tls)
191 |
192 | Write-Verbose -Message $CsvFile
193 |
194 | Write-RemoteServers -FilePath $CsvFile
195 |
196 | $RemoteServers = @()
197 | }
198 | }
199 | }
200 |
201 | if($ToCsv) {
202 | $CsvFile = $CsvFileName.Replace('%SERVER%','ALL')
203 |
204 | Write-Verbose -Message $CsvFile
205 |
206 | Write-RemoteServers -FilePath $CsvFile
207 | }
--------------------------------------------------------------------------------
/Exchange Server/Get-RemoteSmtpServers/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) Thomas Stensitzki
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Exchange Server/Get-RemoteSmtpServers/README.md:
--------------------------------------------------------------------------------
1 | # Get-RemoteSmtpServers.ps1
2 |
3 | Fetch all remote SMTP servers from Exchange receive connector logs
4 |
5 | ## Description
6 |
7 | This scripts fetches remote SMTP servers by searching the Exchange receive connector logs for the EHLO string.
8 | Fetched remote servers can be exported to a single CSV file for all receive connectors across Exchange Servers or exported to a separate CSV file per Exchange Server.
9 |
10 | ## Requirements
11 |
12 | - Exchange Server 2010, Exchange Server 2013+
13 |
14 | ## Parameters
15 |
16 | ### Servers
17 |
18 | List of Exchange servers, modern and legacy Exchange servers cannot be mixed
19 |
20 | ### ServersToExclude
21 |
22 | List of host names that you want to exclude from the outout
23 |
24 | ### Backend
25 |
26 | Search backend transport (aka hub transport) log files, instead of frontend transport, which is the default
27 |
28 | ### LegacyExchange
29 |
30 | Search legacy Exchange servers (Exchange 2010) log file location
31 |
32 | ### ToCsv
33 |
34 | Export search results to a single CSV file for all servers
35 |
36 | ### ToCsvPerServer
37 |
38 | Export search results to a separate CSV file per servers
39 |
40 | ### UniqueIPs
41 |
42 | Simplify the out list by reducing the output to unique IP address
43 |
44 | ### AddDays
45 |
46 | File selection filter, -5 will select log files changed during the last five days. Default: -10
47 |
48 | ## Examples
49 |
50 | ``` PowerShell
51 | .\Get-RemoteSmtpServers.ps1 -Servers SRV01,SRV02 -LegacyExchange -AddDays -4 -ToCsv
52 | ```
53 |
54 | Search legacy Exchange servers SMTP receive log files for the last 4 days and save search results in a single CSV file
55 |
56 | ``` PowerShell
57 | .\Get-RemoteSmtpServers.ps1 -Servers SRV03,SRV04 -AddDays -4 -ToCsv -UniqueIPs
58 | ```
59 |
60 | Search Exchange servers SMTP receive log files for the last 4 days and save search results in a single CSV file, with unique IP addresses only
61 |
62 |
63 | ## Note
64 |
65 | THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE
66 | RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER.
67 |
68 | ## Credits
69 |
70 | Written by: Thomas Stensitzki | MVP
71 |
72 | Related blog post: [https://granikos.eu/fetch-remote-smtp-servers-connecting-to-exchange/](https://granikos.eu/fetch-remote-smtp-servers-connecting-to-exchange/)
73 |
74 | ### Stay connected
75 |
76 | * Blog: [https://blog.granikos.eu](https://blog.granikos.eu)
77 | * Bluesky: [https://bsky.app/profile/stensitzki.eu](https://bsky.app/profile/stensitzki.eu)
78 | * LinkedIn: [https://www.linkedin.com/in/thomasstensitzki](https://www.linkedin.com/in/thomasstensitzki)
79 | * YouTube: [https://www.youtube.com/@ThomasStensitzki](https://www.youtube.com/@ThomasStensitzki)
80 | * LinkTree: [https://linktr.ee/stensitzki](https://linktr.ee/stensitzki)
81 |
82 | For more Microsoft 365, Cloud Security, and Exchange Server stuff checkout services provided by Granikos
83 |
84 | * Website: [https://granikos.eu](https://granikos.eu)
85 | * Bluesky: [https://bsky.app/profile/granikos.eu](https://bsky.app/profile/granikos.eu)
86 |
--------------------------------------------------------------------------------
/Exchange Server/Import-EdgeSubscription/Import-EdgeSubscription.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | .SYNOPSIS
3 | This script imports an Edge Subscription file for a specific Active Directory site.
4 |
5 | Thomas Stensitzki
6 |
7 | THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE
8 | RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER.
9 |
10 | Version 1.0, 2024-02-15
11 |
12 | Please send ideas, comments and suggestions to support@granikos.eu
13 |
14 | .LINK
15 | https://scripts.granikos.eu
16 |
17 | .DESCRIPTION
18 | The script takes two parameters: the path to the Edge Subscription file and the name of the Active Directory site.
19 | It then imports the Edge Subscription file for the specified Active Directory site and does not create an Internet send connector.
20 | This script is primarily used for hybrid Exchange deployments where a different internet send connector already exists.
21 |
22 | .PARAMETER edgeSubscriptionFile
23 | The full path to the Edge Subscription file.
24 |
25 | .PARAMETER activeDirectorySite
26 | The name of the Active Directory site for subscribign the Edge Transport Server to.
27 |
28 | .EXAMPLE
29 | .\Import-EdgeSubscription.ps1 -edgeSubscriptionFile "C:\Import\EdgeSubscription.xml" -activeDirectorySite "ADSiteName"
30 |
31 | #>
32 | param(
33 | [string]$edgeSubscriptionFile = "C:\Import\EdgeSubscription.xml",
34 | [string]$activeDirectorySite = "ADSiteName"
35 | )
36 |
37 | Import-EdgeSubscription -FileData ([byte[]]$(Get-Content -Path $edgeSubscriptionFile -Encoding Byte -ReadCount 0)) -Site $activeDirectorySite -CreateInternetSendConnector $false
38 |
39 | Write-Output @(
40 | ('Imported Edge Subscription from {0} for Active Directory site {1}' -f $edgeSubscriptionFile, $activeDirectorySite),
41 | 'One of the Exchange Servers in the Active Directory site will use the imported BootStrap account to initiate the Edge Subscription process.',
42 | 'Use Test-EdgeSynchronization to verify that Exchange mailbox servers communicate with subscribed Edge Transport Server.',
43 | 'More information: https://learn.microsoft.com/powershell/module/exchange/test-edgesynchronization?view=exchange-ps'
44 | )
45 |
46 |
47 |
--------------------------------------------------------------------------------
/Exchange Server/Import-EdgeSubscription/README.md:
--------------------------------------------------------------------------------
1 | # Import-EdgeSubscription.ps1
2 |
3 | This script imports an Edge Subscription file for a specific Active Directory site.
4 |
5 | ## Description
6 |
7 | The script takes two parameters: the path to the Edge Subscription file and the name of the Active Directory site.
8 | It then imports the Edge Subscription file for the specified Active Directory site and does not create an Internet send connector.
9 | This script is primarily used for hybrid Exchange deployments where a different internet send connector already exists.
10 |
11 | ## Parameters
12 |
13 | ### edgeSubscriptionFile
14 |
15 | The full path to the Edge Subscription file.
16 |
17 | ### activeDirectorySite
18 |
19 | The name of the Active Directory site for subscribign the Edge Transport Server to.
20 |
21 | ## Example
22 |
23 | ``` PowerShell
24 | .\Import-EdgeSubscription.ps1 -edgeSubscriptionFile "C:\Import\EdgeSubscription.xml" -activeDirectorySite "Munich"
25 | ```
26 | Import an Edge Transport subscription to an Exchange organization to Active Directory site "Munich"
27 |
28 | ## Credits
29 |
30 | Written by: Thomas Stensitzki
31 |
32 | ### Stay connected
33 |
34 | * Blog: [https://blog.granikos.eu](https://blog.granikos.eu)
35 | * Bluesky: [https://bsky.app/profile/stensitzki.eu](https://bsky.app/profile/stensitzki.eu)
36 | * LinkedIn: [https://www.linkedin.com/in/thomasstensitzki](https://www.linkedin.com/in/thomasstensitzki)
37 | * YouTube: [https://www.youtube.com/@ThomasStensitzki](https://www.youtube.com/@ThomasStensitzki)
38 | * LinkTree: [https://linktr.ee/stensitzki](https://linktr.ee/stensitzki)
39 |
40 | For more Microsoft 365, Cloud Security, and Exchange Server stuff checkout services provided by Granikos
41 |
42 | * Website: [https://granikos.eu](https://granikos.eu)
43 | * Bluesky: [https://bsky.app/profile/granikos.eu](https://bsky.app/profile/granikos.eu)
44 |
--------------------------------------------------------------------------------
/Exchange Server/New-RoomMailbox/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) Thomas Stensitzki
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Exchange Server/New-RoomMailbox/README.md:
--------------------------------------------------------------------------------
1 | # New-RoomMailbox.ps1
2 |
3 | Creates a new room mailbox, security groups for full access and send-as permission and adds the security groups to the room mailbox configuration.
4 |
5 | ## Description
6 |
7 | This scripts creates a new room mailbox and additonal security groups for full access and and send-as delegation. The security groups are created using a confgurable naming convention.
8 | You can add existing groups to the full access and send-as to a room mailbox. This is useful, if you have a room management department and want to grant permissions.
9 | The script adds the room mailbox to an existing room list, if configured.
10 |
11 | ## Parameters
12 |
13 | ### RoomMailboxName
14 |
15 | Name attribute of the new room mailbox
16 |
17 | ### RoomMailboxDisplayName
18 |
19 | Display name attribute of the new room mailbox
20 |
21 | ### RoomMailboxAlias
22 |
23 | Alias attribute of the new room mailbox
24 |
25 | ### RoomMailboxSmtpAddress
26 |
27 | Primary SMTP address attribute the new room mailbox
28 |
29 | ### DepartmentPrefix
30 |
31 | Department prefix for automatically generated security groups (optional)
32 |
33 | ### GroupFullAccessMembers
34 |
35 | String array containing full access members
36 |
37 | ### GroupSendAsMembers
38 |
39 | String array containing send as members
40 |
41 | ### GroupCalendarBookingMembers
42 |
43 | String array containing users having calendar booking rights
44 |
45 | ### RoomPhoneNumber
46 |
47 | Phone number of a phone located in the room, this value will show in the Outlook room list
48 |
49 | ### RoomList
50 |
51 | Add the new room mailbox to this existing room list
52 |
53 | ### AutoAccept
54 |
55 | Set room mailbox to automatically accept booking requests
56 |
57 | ### Language
58 |
59 | Locale setting for calendar regional configuration language, e.g., de-DE, en-US
60 |
61 | ## Examples
62 |
63 | ``` PowerShell
64 | .\New-RoomMailbox.ps1 -RoomMailboxName "MB - Conference Room" -RoomMailboxDisplayName "Board Conference Room" -RoomMailboxAlias "MB-ConferenceRoom" -RoomMailboxSmtpAddress "ConferenceRoom@mcsmemail.de" -DepartmentPrefix "C"
65 | ```
66 | Create a new room mailbox, empty full access and empty send-as security groups
67 |
68 | ## Credits
69 |
70 | Written by: Thomas Stensitzki
71 |
72 | ### Stay connected
73 |
74 | * Blog: [https://blog.granikos.eu](https://blog.granikos.eu)
75 | * Bluesky: [https://bsky.app/profile/stensitzki.eu](https://bsky.app/profile/stensitzki.eu)
76 | * LinkedIn: [https://www.linkedin.com/in/thomasstensitzki](https://www.linkedin.com/in/thomasstensitzki)
77 | * YouTube: [https://www.youtube.com/@ThomasStensitzki](https://www.youtube.com/@ThomasStensitzki)
78 | * LinkTree: [https://linktr.ee/stensitzki](https://linktr.ee/stensitzki)
79 |
80 | For more Microsoft 365, Cloud Security, and Exchange Server stuff checkout services provided by Granikos
81 |
82 | * Website: [https://granikos.eu](https://granikos.eu)
83 | * Bluesky: [https://bsky.app/profile/granikos.eu](https://bsky.app/profile/granikos.eu)
84 |
--------------------------------------------------------------------------------
/Exchange Server/New-RoomMailbox/Run-NewRoomMailbox.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | Run-NewRoomMailbox.ps1
3 |
4 | Helper script to simplify the use of New-RoomMailbox.ps1
5 | #>
6 |
7 | $roomMailboxName = 'MB-Conference Room'
8 | $roomMailboxDisplayName = 'Board Conference Room'
9 | $roomMailboxAlias = 'MB-ConferenceRoom'
10 | $roomMailboxSmtpAddress = 'ConferenceRoom@mcsmemail.de'
11 | $departmentPrefix = 'C'
12 | $groupFullAccessMembers = @('JohnDoe','JaneDoe') # Empty = @()
13 | $groupSendAsMembers = @()
14 | $groupCalendarBookingMembers = @()
15 | $RoomCapacity = 0
16 | $RoomList = 'AllRoomsHQ'
17 | $Language = 'de-DE' # en-US, de-DE, fr-FR, ja-JP, pt-BR, zh-CN, zh-TW, ...
18 |
19 | .\New-RoomMailbox.ps1 -RoomMailboxName $roomMailboxName -RoomMailboxDisplayName $roomMailboxDisplayName -RoomMailboxAlias $roomMailboxAlias -RoomMailboxSmtpAddress $roomMailboxSmtpAddress -DepartmentPrefix $departmentPrefix -GroupFullAccessMembers $groupFullAccessMembers -GroupSendAsMembers $groupSendAsMembers -RoomCapacity $RoomCapacity -AutoAccept -RoomList $RoomList -Language $Language
20 |
21 | if ($roomMailboxSmtpAddress -ne '') {
22 | # Use the provided room mailbox SMTP address
23 | .\New-RoomMailbox.ps1 -RoomMailboxName $roomMailboxName -RoomMailboxDisplayName $roomMailboxDisplayName -RoomMailboxAlias $roomMailboxAlias -RoomMailboxSmtpAddress $roomMailboxSmtpAddress -GroupFullAccessMembers $groupFullAccessMembers -GroupSendAsMember $groupSendAsMembers -RoomCapacity $RoomCapacity -AutoAccept -RoomList $RoomList -Language $Language
24 | }
25 | else {
26 | # Generate the room mailbox SMTP address automatically
27 | .\New-RoomMailbox.ps1 -roomMailboxName $roomMailboxName -RoomMailboxDisplayName $roomMailboxDisplayName -roomMailboxAlias $roomMailboxAlias -GroupFullAccessMembers $groupFullAccessMembers -GroupSendAsMember $groupSendAsMembers -RoomCapacity $RoomCapacity -AutoAccept -RoomList $RoomList -Language $Language
28 | }
--------------------------------------------------------------------------------
/Exchange Server/New-RoomMailbox/settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | pre_
5 | _SA
6 | _FA
7 | _CB
8 | mcsmemail.de/IT/Groups/Mail/Rooms
9 | mcsmemail.de
10 | -
11 |
12 |
13 | mcsmemail.de/IT/Mail/RoomMailboxes
14 |
15 |
16 | 10
17 |
18 |
19 |
20 |
21 |
22 | AADSync
23 |
24 |
--------------------------------------------------------------------------------
/Exchange Server/New-TeamMailbox/Create-TeamMailbox.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | Create-TeamMailbox.ps1
3 |
4 | Helper script to simplify the use of New-TeamMailbox.ps1
5 | #>
6 |
7 | $teamMailboxName = 'TM-Exchange Admin'
8 | $teamMailboxDisplayName = 'Exchange Admins'
9 | $teamMailboxAlias = 'TM-ExchangeAdmin'
10 | $teamMailboxSmtpAddress = 'ExchangeAdmins@mcsmemails.de'
11 | $departmentPrefix = 'IT'
12 | $groupFullAccessMembers = @('exAdmin1','exAdmin2')
13 | $groupSendAsMember = @('exAdmin1','exAdmin2')
14 |
15 | .\New-TeamMailbox.ps1 -TeamMailboxName $teamMailboxName -TeamMailboxDisplayName $teamMailboxDisplayName -TeamMailboxAlias $teamMailboxAlias -TeamMailboxSmtpAddress $teamMailboxSmtpAddress -DepartmentPrefix $departmentPrefix -GroupFullAccessMembers $groupFullAccessMembers -GroupSendAsMember $groupSendAsMember -Verbose
--------------------------------------------------------------------------------
/Exchange Server/New-TeamMailbox/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) Thomas Stensitzki
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Exchange Server/New-TeamMailbox/README.md:
--------------------------------------------------------------------------------
1 | # New-TeamMailbox.ps1
2 |
3 | Creates a new shared mailbox, security groups for full access and send-as permission and adds the security groups to the shared mailbox configuration.
4 |
5 | ## Description
6 |
7 | This scripts creates a new shared mailbox (aka team mailbox) and security groups for full access and and send-as delegation. Security groups are created using a naming convention.
8 |
9 | Starting with v1.4 the script sets the sAMAccountName of the security groups to the group name to avoid numbered name extension of sAMAccountName.
10 |
11 | ## Parameters
12 |
13 | ### TeamMailboxName
14 |
15 | Name attribute of the new team mailbox
16 |
17 | ### TeamMailboxDisplayName
18 |
19 | Display name attribute of the new team mailbox
20 |
21 | ### TeamMailboxAlias
22 |
23 | Alias attribute of the new team mailbox
24 |
25 | ### TeamMailboxSmtpAddress
26 |
27 | Primary SMTP address attribute the new team mailbox
28 |
29 | ### DepartmentPrefix
30 |
31 | Department prefix for automatically generated security groups (optional)
32 |
33 | ### GroupFullAccessMembers
34 |
35 | String array containing full access members
36 |
37 | ### GroupFullAccessMembers
38 |
39 | String array containing send as members
40 |
41 | ## Examples
42 |
43 | ``` PowerShell
44 | .\New-TeamMailbox.ps1 -TeamMailboxName "TM-Exchange Admins" -TeamMailboxDisplayName "Exchange Admins" -TeamMailboxAlias "TM-ExchangeAdmins" -TeamMailboxSmtpAddress "ExchangeAdmins@mcsmemail.de" -DepartmentPrefix "IT"
45 | ```
46 |
47 | Create a new team mailbox, empty full access and empty send-as security groups
48 |
49 | ## Note
50 |
51 | THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE
52 | RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER.
53 |
54 | ## Credits
55 |
56 | Written by: Thomas Stensitzki
57 |
58 | ### Stay connected
59 |
60 | * Blog: [https://blog.granikos.eu](https://blog.granikos.eu)
61 | * Bluesky: [https://bsky.app/profile/stensitzki.eu](https://bsky.app/profile/stensitzki.eu)
62 | * LinkedIn: [https://www.linkedin.com/in/thomasstensitzki](https://www.linkedin.com/in/thomasstensitzki)
63 | * YouTube: [https://www.youtube.com/@ThomasStensitzki](https://www.youtube.com/@ThomasStensitzki)
64 | * LinkTree: [https://linktr.ee/stensitzki](https://linktr.ee/stensitzki)
65 |
66 | For more Microsoft 365, Cloud Security, and Exchange Server stuff checkout services provided by Granikos
67 |
68 | * Website: [https://granikos.eu](https://granikos.eu)
69 | * Bluesky: [https://bsky.app/profile/granikos.eu](https://bsky.app/profile/granikos.eu)
70 |
--------------------------------------------------------------------------------
/Exchange Server/New-TeamMailbox/settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | pre_
5 | _SA
6 | _FA
7 | _CB
8 | mcsmemail.de/IT/Groups/Mail
9 | mcsmemail.de
10 | -
11 |
12 |
13 | mcsmemail.de/IT/SharedMailboxes
14 |
15 |
16 | 60
17 | TENANT.mail.onmicrosoft.com
18 |
19 |
20 | AADSync
21 |
22 |
--------------------------------------------------------------------------------
/Exchange Server/Purge-LogFiles/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) Thomas Stensitzki
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/Exchange Server/Purge-LogFiles/README.md:
--------------------------------------------------------------------------------
1 | # IMPORTANT NOTE
2 |
3 | Update to most current release if you are using _v2.1_
4 |
5 | # Purge-LogFiles.ps1
6 |
7 | PowerShell script for modern Exchange Server environments to clean up Exchange Server and IIS log files.
8 |
9 | ## Description
10 |
11 | This script deletes all Exchange Server and IIS logs older than X days from all Exchange 2013+ servers that are fetched using the Get-ExchangeServer cmdlet.
12 |
13 | The Exchange log file location is read from a variable and used to build an administrative UNC path for file deletions. It is assumed that the Exchange setup path is IDENTICAL across all Exchange servers.
14 |
15 | Optionally, you can use the Active Directory configuration partition to determine the Exchange install path dynamically, if supported in your Active Directory environment.
16 |
17 | The IIS log file location is read from the local IIS metabase of the LOCAL server and is used to build an administrative UNC path for IIS log file deletions.
18 |
19 | Currently, it is assumed that the IIS log file location is identical across all Exchange servers.
20 |
21 | ## Requirements
22 |
23 | - Windows Server 2012 R2 or newer
24 | - Utilizes the global function library found here: [http://scripts.granikos.eu](http://scripts.granikos.eu)
25 | - AciveDirectory PowerShell module
26 | - Exchange Server 2013+
27 | - Exchange Management Shell (EMS)
28 |
29 | ## Updates
30 |
31 | - 2020-05-14, v2.3.1, Issues #14, #15 fixed to properly support Edge Transport Role
32 | - 2020-03-12, v2.3, Option for HTTPERR added, Option for dynamic Exchange install paths added, Html formatting added, tested with Exchange Server 2019
33 |
34 | ## Parameters
35 |
36 | ### DaysToKeep
37 |
38 | Number of days Exchange and IIS log files should be retained, default is 30 days
39 |
40 | ### Auto
41 |
42 | Switch to use automatic detection of the IIS and Exchange log folder paths
43 |
44 | ### IsEdge
45 |
46 | Indicates the the script is executed on an Exchange Server holding the EDGE role. Without the switch servers holding the EDGE role are excluded.
47 |
48 | ### IncludeHttpErr
49 |
50 | Include the HTTPERR log files in the purge routine. Those logs are normally stored at _C:\Windows\System32\LogFiles\HTTPERR_.
51 |
52 | ### UseDynamicExchangePaths
53 |
54 | Determine the Exchange install path by querying the server object in AD configuration partition. This helps if your Exchange servers do not have a unified install path across all servers.
55 |
56 | ### RepositoryRootPath
57 |
58 | Absolute path to a repository folder for storing copied log files and compressed archives. Preferably an UNC path. A new subfolder will be created for each Exchange server.
59 |
60 | ### ArchiveMode
61 |
62 | Log file copy and archive mode. Possible values
63 |
64 | - _None_ = All log files will be purged without being copied
65 | - _CopyOnly_ = Simply copy log files to the RepositoryRootPath
66 | - _CopyAndZip_ = Copy logfiles and send copied files to compressed archive
67 | - _CopyZipAndDelete_ = Same as CopyAndZip, but delete copied log files from RepositoryRootPath
68 |
69 | ### SendMail
70 |
71 | Switch to send an Html report
72 |
73 | ### MailFrom
74 |
75 | Email address of report sender
76 |
77 | ### MailTo
78 |
79 | Email address of report recipient
80 |
81 | ### MailServer
82 |
83 | SMTP Server for email report
84 |
85 | ## Examples
86 |
87 | ``` PowerShell
88 | .\Purge-LogFiles -DaysToKeep 14
89 | ```
90 |
91 | Delete Exchange and IIS log files older than 14 days
92 |
93 | ``` PowerShell
94 | .\Purge-LogFiles -DaysToKeep 7 -Auto
95 | ```
96 |
97 | Delete Exchange and IIS log files older than 7 days with automatic discovery
98 |
99 | ``` PowerShell
100 | .\Purge-LogFiles -DaysToKeep 7 -Auto -SendMail -MailFrom postmaster@sedna-inc.com -MailTo exchangeadmin@sedna-inc.com -MailServer mail.sedna-inc.com
101 | ```
102 |
103 | Delete Exchange and IIS log files older than 7 days with automatic discovery and send email report
104 |
105 | ``` PowerShell
106 | .\Purge-LogFiles -DaysToKeep 14 -RepositoryRootPath \\OTHERSERVER\OtherShare\LOGS -ArchiveMode CopyZipAndDelete`
107 | ```
108 |
109 | Delete Exchange and IIS log files older than 14 days, but copy files to a central repository and compress the log files before final deletion
110 |
111 | ``` PowerShell
112 | .\Purge-LogFiles.ps1 -DaysToKeep 7 -SendMail -MailFrom postmaster@sedna-inc.com -MailTo exchangeadmin@sedna-inc.com -MailServer mail.sedna-inc.com -UseDynamicExchangePaths -IncludeHttpErr
113 | ```
114 |
115 | Delete Exchange Server, IIS, and HTTPERR log files older than 7 days, and send an HTML email. Identify Exchange file paths using AD configuration objects.
116 |
117 | ## Note
118 |
119 | THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE
120 | RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER.
121 |
122 | ## Credits
123 |
124 | Written by: Thomas Stensitzki
125 |
126 | ### Stay connected
127 |
128 | * Blog: [https://blog.granikos.eu](https://blog.granikos.eu)
129 | * Bluesky: [https://bsky.app/profile/stensitzki.eu](https://bsky.app/profile/stensitzki.eu)
130 | * LinkedIn: [https://www.linkedin.com/in/thomasstensitzki](https://www.linkedin.com/in/thomasstensitzki)
131 | * YouTube: [https://www.youtube.com/@ThomasStensitzki](https://www.youtube.com/@ThomasStensitzki)
132 | * LinkTree: [https://linktr.ee/stensitzki](https://linktr.ee/stensitzki)
133 |
134 | For more Microsoft 365, Cloud Security, and Exchange Server stuff checkout services provided by Granikos
135 |
136 | * Website: [https://granikos.eu](https://granikos.eu)
137 | * Bluesky: [https://bsky.app/profile/granikos.eu](https://bsky.app/profile/granikos.eu)
138 |
139 | ### Additional Credits
140 |
141 | - Is-Admin function (c) by Michel de Rooij, michel@eightwone.com
--------------------------------------------------------------------------------
/Exchange Server/Purge-LogFiles/Run-PurgeLogFiles.ps1:
--------------------------------------------------------------------------------
1 | # Example file to help execute Purge-LogFiles.ps1 with a single PowerShell script, e.g. used for Scheduled Tasks
2 |
3 | .\Purge-LogFiles.ps1 -DaysToKeep 1 -SendMail -MailFrom postmaster@mcsmemail.de -MailTo it@mcsmemail.de -MailServer mail.mcsmemail.de
--------------------------------------------------------------------------------
/Exchange Server/README.md:
--------------------------------------------------------------------------------
1 | # Exchange Server PowerShell Scripts
2 |
3 | This GitHub Repository contains most of my public PowerShell scripts for modern Exchange Server versions (2013, 2016, 2019).
4 |
5 | Please refer to the table of content in the main [README](https://github.com/Apoc70/PowerShell-Scripts) file.
6 |
7 | ### Stay connected
8 |
9 | * Blog: [https://blog.granikos.eu](https://blog.granikos.eu)
10 | * Bluesky: [https://bsky.app/profile/stensitzki.eu](https://bsky.app/profile/stensitzki.eu)
11 | * LinkedIn: [https://www.linkedin.com/in/thomasstensitzki](https://www.linkedin.com/in/thomasstensitzki)
12 | * YouTube: [https://www.youtube.com/@ThomasStensitzki](https://www.youtube.com/@ThomasStensitzki)
13 | * LinkTree: [https://linktr.ee/stensitzki](https://linktr.ee/stensitzki)
14 |
15 | For more Microsoft 365, Cloud Security, and Exchange Server stuff checkout services provided by Granikos
16 |
17 | * Website: [https://granikos.eu](https://granikos.eu)
18 | * Bluesky: [https://bsky.app/profile/granikos.eu](https://bsky.app/profile/granikos.eu)
19 |
--------------------------------------------------------------------------------
/Exchange Server/Remove-ProxyAddress/Remove-ProxyAddress.ps1:
--------------------------------------------------------------------------------
1 | # Description: Remove email addresses from users in Active Directory
2 |
3 | $DomainFilter = "*mail.onmicrosoft.com"
4 |
5 | # Get all users with email addresses in scope
6 | Get-ADUser -Properties proxyaddresses -Filter {ProxyAddresses -like $DomainFilter} |
7 | ForEach-Object {
8 | # Remove the email addresses
9 | ForEach ($proxyAddress in $_.proxyAddresses) {
10 | # Check if the email address is in scope
11 | If ($proxyAddress -like $DomainFilter) {
12 | # Output the action to the console
13 | Write-Verbose -Message ('Removing $proxyAddress from {0}' -f $_.SamAccountName)
14 | # Remove the email address
15 | Set-ADUser $_.SamAccountName -Remove @{ProxyAddresses=$proxyAddress}
16 | }
17 | }
18 | }
--------------------------------------------------------------------------------
/Exchange Server/Set-EdgeDNSSuffix/Set-EdgeDNSSuffix.ps1:
--------------------------------------------------------------------------------
1 | [cmdletbinding()]
2 | param()
3 |
4 | # DNS Suffix for Edge Transport Server
5 | $DNSSuffix = "varunagroup.de"
6 |
7 | # Fetch current DNS Suffix
8 | $keyPath = 'HKLM:\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\'
9 | $valueName = 'NV Domain'
10 |
11 | try{
12 | # $oldDNSSuffix = Get-ItemProperty -Path $keyPath -Name $valueName -ErrorAction Stop
13 | $oldDNSSuffix = (Get-ItemProperty $keyPath -Name $valueName -ErrorAction Stop).$valueName
14 |
15 | if($oldDNSSuffix -eq '') {
16 |
17 | #Update primary DNS Suffix for FQDN
18 | Write-Verbose -Message ('Adding {0} to {1}' -f $DNSSuffix, $keyPath)
19 | Set-ItemProperty $keyPath -Name 'Domain' -Value $DNSSuffix
20 | Set-ItemProperty $keyPath -Name $valueName -Value $DNSSuffix
21 |
22 | # Update DNS Suffix Search List - Win8/2012 and above - if needed
23 | # Set-DnsClientGlobalSetting -SuffixSearchList $oldDNSSuffix,$DNSSuffix
24 | }
25 | else {
26 | Write-Host ("The computer DNS suffix is already set to {0}.`nThe script did not change the current configuration." -f $oldDNSSuffix)
27 | }
28 |
29 | }
30 | catch {
31 | $null = New-ItemProperty -Path $keyPath -Name $valueName -Value $DNSSuffix -Force
32 | $null = New-ItemProperty -Path $keyPath -Name 'Domain' -Value $DNSSuffix -Force
33 | Write-Host ("The computer DNS suffix is now set to {0}.`nPlease restart the computer." -f $DNSSuffix)
34 | }
35 |
36 |
37 |
--------------------------------------------------------------------------------
/Exchange Server/Set-ExchangePreferredRegistrySettings/Disable-IPv6-Correctly.reg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Apoc70/PowerShell-Scripts/ef43113b389502d309e246891fa9c7a3ad5a147e/Exchange Server/Set-ExchangePreferredRegistrySettings/Disable-IPv6-Correctly.reg
--------------------------------------------------------------------------------
/Exchange Server/Set-ExchangePreferredRegistrySettings/RPC-TimeOut-PortScaling.reg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Apoc70/PowerShell-Scripts/ef43113b389502d309e246891fa9c7a3ad5a147e/Exchange Server/Set-ExchangePreferredRegistrySettings/RPC-TimeOut-PortScaling.reg
--------------------------------------------------------------------------------
/Exchange Server/Set-ExchangePreferredRegistrySettings/Set-ExchangePreferredRegistrySettings.ps1:
--------------------------------------------------------------------------------
1 | write-host-Message "Setting preferred registry settings for Exchange Server 2016/2019"
2 |
3 | # Set KeepAliveTime to 2 hours
4 | New-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters' -Name 'KeepAliveTime' -Value '001b7740' -PropertyType 'DWord' -Force | Out-Null
5 |
6 | # Set KeepAliveInterval to 1 second
7 | New-ItemProperty -Path 'HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\Rpc' -Name 'EnableTcpPortScaling' -Value '00000001' -PropertyType 'DWord' -Force | Out-Null
8 |
9 | # Set MinimumConnectionTimeout to 120 seconds
10 | New-ItemProperty -Path 'HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\Rpc' -Name 'MinimumConnectionTimeout' -Value '00000078' -PropertyType 'DWord' -Force | Out-Null
11 |
12 | # Set IVv6 to disabled
13 | New-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Services\Tcpip6\Parameters' -Name 'DisabledComponents' -Value '000000ff' -PropertyType 'DWord' -Force | Out-Null
14 |
15 | Write-Host 'Preferred registry settings have been applied. PLease restart the server to apply changes.'
--------------------------------------------------------------------------------
/Exchange Server/Set-ExchangePreferredRegistrySettings/Tcpip-KeepAliveTime.reg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Apoc70/PowerShell-Scripts/ef43113b389502d309e246891fa9c7a3ad5a147e/Exchange Server/Set-ExchangePreferredRegistrySettings/Tcpip-KeepAliveTime.reg
--------------------------------------------------------------------------------
/Exchange Server/Set-UserPicture/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) Thomas Stensitzki
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Exchange Server/Set-UserPicture/README.md:
--------------------------------------------------------------------------------
1 | # Set-UserPicture
2 |
3 | This script fetches images from a source folder, resizes images for use with Exchange, Active Directory and Intranet. Resized images are are written to Exchange and Active Directory.
4 |
5 | ## Description
6 |
7 | The script parses all images provided in a dedicated source folder. The images are resized and cropped for use with the following targets:
8 |
9 | * Exchange Server user photo
10 | * Active Directory thumbnailPhoto attribute
11 | * Local intranet use in your local infrastructure
12 |
13 | Source images must be named using the respective user logon name.
14 |
15 | Example: MYDOMAIN\JohnDoe --> JohnDoe.jpg
16 |
17 | Preferably, the images are stored in jpg format.
18 |
19 | Optionally, processed image files are moved from the respective folder (Exchange, AD, Intranet) to a processed folder.
20 |
21 | ## Requirements
22 |
23 | * GlobalFunctions PowerShell module, described here: [https://scripts.granikos.eu](https://scripts.granikos.eu)
24 | * ResizeImage.exe executable, described here: [https://granikos.eu/add-resized-user-photos-automatically/](https://granikos.eu/add-resized-user-photos-automatically/)
25 | * Exchange Server 2016+ Management Shell (EMS) for storing user photos in on-premises mailboxes
26 | * Exchange Online Management Shell for storing user photos in cloud mailboxes
27 | * Write access to thumbnailPhoto attribute in Active Directory
28 |
29 | ## Parameters
30 |
31 | ### PictureSource
32 |
33 | Absolute path to source images
34 | Filenames must match the logon name of the user
35 |
36 | ### TargetPathAD
37 |
38 | Absolute path to store images resized for Active Directory
39 |
40 | ### TargetPathExchange
41 |
42 | Absolute path to store images resized for Exchange
43 |
44 | ### TargetPathIntranet
45 |
46 | Absolute path to store images resized for Intranet
47 |
48 | ### ExchangeOnPrem
49 |
50 | Switch to create resized images for Exchange On-Premesis and store images in users mailbox
51 | Requires the image tool to be available in TargetPathExchange
52 |
53 | ### ExchangeOnline
54 |
55 | Switch to create resized images for Exchange Online and store images in users mailbox
56 | Requires the image tool to be available in TargetPathExchange
57 |
58 | ### ExchangeOnPremisesFqdn
59 |
60 | Name the on-premises Exchange Server Fqdn to connect to using remote PowerShell
61 |
62 | ### ActiveDirectory
63 |
64 | Switch to create resized images for Active Directory and store images in users thumbnailPhoto attribute
65 | Requires the image tool to be available in TargetPathAD
66 |
67 | ### Intranet
68 |
69 | Switch to create resized images for Intranet
70 | Requires the image tool to be available in TargetPathIntranet
71 |
72 | ### SaveUserStatus
73 |
74 | Switch to save a last modified status in a local Xml file. Currently in development.
75 |
76 | ### MoveAction
77 |
78 | Optional action to move processed images to a dedicated sub folder.
79 |
80 | Possible values:
81 |
82 | * MoveTargetToProcessed = Move Exchange, AD or Intranet pictures to a subfolder
83 | * MoveSourceToProcessed = Move image source to a subfolder
84 |
85 | ## Examples
86 |
87 | ``` PowerShell
88 | .\Set-UserPicture.ps1 -ExchangeOnPrem
89 | ```
90 |
91 | Resize photos stored in the default PictureSource folder for Exchange (648x648) and write images to user mailboxes
92 |
93 | ``` PowerShell
94 | .\Set-UserPicture.ps1 -ExchangeOnline -PictureSource '\\SRV01\HRShare\Photos' -TargetPathExchange '\\SRV02\ExScripts\Photos'
95 | ```
96 |
97 | Resize photos stored on a SRV01 share for Exchange Online and save resized photos on a SRV02 share
98 |
99 | ``` PowerShell
100 | .\Set-UserPicture.ps1 -ActiveDirectory
101 | ```
102 |
103 | Resize photos stored in the default PictureSource folder for Active Directory (96x96) and write images to user thumbnailPhoto attribute
104 |
105 | ``` PowerShell
106 | .\Set-UserPicture.ps1 -Intranet
107 | ```
108 |
109 | Resize photos stored in the default PictureSource folder for Intranet (150x150)
110 |
111 | ## Note
112 |
113 | THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE
114 | RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER.
115 |
116 | ## Credits
117 |
118 | Written by: Thomas Stensitzki
119 |
120 | ### Stay connected
121 |
122 | * Blog: [https://blog.granikos.eu](https://blog.granikos.eu)
123 | * Bluesky: [https://bsky.app/profile/stensitzki.eu](https://bsky.app/profile/stensitzki.eu)
124 | * LinkedIn: [https://www.linkedin.com/in/thomasstensitzki](https://www.linkedin.com/in/thomasstensitzki)
125 | * YouTube: [https://www.youtube.com/@ThomasStensitzki](https://www.youtube.com/@ThomasStensitzki)
126 | * LinkTree: [https://linktr.ee/stensitzki](https://linktr.ee/stensitzki)
127 |
128 | For more Microsoft 365, Cloud Security, and Exchange Server stuff checkout services provided by Granikos
129 |
130 | * Website: [https://granikos.eu](https://granikos.eu)
131 | * Bluesky: [https://bsky.app/profile/granikos.eu](https://bsky.app/profile/granikos.eu)
132 |
--------------------------------------------------------------------------------
/Exchange Server/Set-UserPicture/UserStatus.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/Exchange Server/Set-VirtualDirectoryUrl/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Thomas Stensitzki
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/Exchange Server/Set-VirtualDirectoryUrl/README.md:
--------------------------------------------------------------------------------
1 | # Set-VirtualDirectoryUrl
2 |
3 | ## SYNOPSIS
4 |
5 | Configure Exchange Server 2013 Virtual Directory Url Settings
6 |
7 | Thomas Stensitzki
8 |
9 | THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE
10 | RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER.
11 |
12 | Version 2.1, 2021-11-23
13 |
14 | Use GitHub for Please send ideas, comments and suggestions.
15 |
16 | ## LINK
17 |
18 | [https://scripts.granikos.eu](https://scripts.granikos.eu)
19 |
20 | ## DESCRIPTION
21 |
22 | Exchange Server virtual directories (vDirs) require a proper configuration of
23 | internal and external Urls. This is even more important in a co-existence
24 | scenario with legacy Exchange Server versions.
25 |
26 | Read more about Exchange Server 2013+ vDirs [here](https://techcommunity.microsoft.com/t5/exchange-team-blog/configuring-multiple-owa-ecp-virtual-directories-on-the-exchange/ba-p/611217?WT.mc_id=M365-MVP-5003086).
27 |
28 |
29 | ## NOTES
30 |
31 | Requirements
32 | - Windows Server 2016+
33 | - Exchange Server 2016+
34 |
35 | Revision History
36 | --------------------------------------------------------------------------------
37 | 1.0 Initial community release
38 | 2.0 Updated for Exchange Server 2016, 2019, vNEXT
39 | 2.1 PowerShell Hygiene
40 |
41 | ## PARAMETERS
42 |
43 | ### PARAMETER InternalUrl
44 |
45 | The internal url FQDN with leading protocol definition, ie. https://mobile.mcsmemail.de
46 |
47 | ### PARAMETER ExternalUrl
48 |
49 | The internal url FQDN with leading protocol definition, ie. https://mobile.mcsmemail.de
50 |
51 | ## EXAMPLE
52 |
53 | Configure internal and external url for different host headers
54 | .\Set-VirtualDirectoryUrl -InternalUrl https://internal.mcsmemail.de -ExternalUrl https://mobile.mcsmemail.de
55 |
56 | # Stay connected
57 |
58 | * Blog: [https://blog.granikos.eu](https://blog.granikos.eu)
59 | * Bluesky: [https://bsky.app/profile/stensitzki.eu](https://bsky.app/profile/stensitzki.eu)
60 | * LinkedIn: [https://www.linkedin.com/in/thomasstensitzki](https://www.linkedin.com/in/thomasstensitzki)
61 | * YouTube: [https://www.youtube.com/@ThomasStensitzki](https://www.youtube.com/@ThomasStensitzki)
62 | * LinkTree: [https://linktr.ee/stensitzki](https://linktr.ee/stensitzki)
63 |
64 | For more Microsoft 365, Cloud Security, and Exchange Server stuff checkout services provided by Granikos
65 |
66 | * Website: [https://granikos.eu](https://granikos.eu)
67 | * Bluesky: [https://bsky.app/profile/granikos.eu](https://bsky.app/profile/granikos.eu)
68 |
--------------------------------------------------------------------------------
/Exchange Server/Set-VirtualDirectoryUrl/Set-VirtualDirectoryUrl.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | .SYNOPSIS
3 | Configure Exchange Server 2016+ Virtual Directory Url Settings
4 |
5 | Thomas Stensitzki
6 |
7 | THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE
8 | RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER.
9 |
10 | Version 2.1, 2021-11-23
11 |
12 | Please send ideas, comments and suggestions to support@granikos.eu
13 |
14 | .LINK
15 | https://scripts.granikos.eu
16 |
17 | .DESCRIPTION
18 | Exchange Server virtual directories (vDirs) require a proper configuration of
19 | internal and external Urls. This is even more important in a co-existence
20 | scenario with legacy Exchange Server versions.
21 |
22 | .NOTES
23 | Requirements
24 | - Windows Server 2016+
25 | - Exchange Server 2016+
26 |
27 | Revision History
28 | --------------------------------------------------------------------------------
29 | 1.0 Initial community release
30 | 2.0 Updated for Exchange Server 2016, 2019, vNEXT
31 | 2.1 PowerShell Hygiene
32 |
33 | .PARAMETER InternalUrl
34 | The internal url FQDN with protocol definition, ie. https://mobile.mcsmemail.de
35 |
36 | .PARAMETER ExternalUrl
37 | The internal url FQDN with protocol definition, ie. https://mobile.mcsmemail.de
38 |
39 | .EXAMPLE
40 | Configure internal and external url for different host headers
41 | .\Set-VirtualDirectoryUrl.ps1 -InternalUrl https://internal.mcsmemail.de -ExternalUrl https://mobile.mcsmemail.de
42 |
43 | #>
44 |
45 | Param(
46 | [string]$InternalUrl='',
47 | [string]$ExternalUrl='',
48 | [string]$AutodiscoverUrl='',
49 | [string]$ServerName = ''
50 | )
51 |
52 | if($ServerName -ne '') {
53 | # Fetch Exchange Server
54 | $exchangeServers = Get-ExchangeServer -Identity $ServerName
55 | }
56 | else {
57 | # Fetch Exchange Server 2016+ Servers
58 | $exchangeServers = Get-ExchangeServer | Where-Object {($_.IsE15OrLater -eq $true) -and ($_.ServerRole -ilike '*Mailbox*')}
59 | }
60 |
61 | if($InternalUrl -ne '' -and $null -ne $exchangeServers) {
62 |
63 | # Trim trailing "/"
64 | if($InternalUrl.EndsWith('/')) {
65 | $InternalUrl = $InternalUrl.TrimEnd('/')
66 | }
67 |
68 | Write-Output 'The script configures the following servers:'
69 | $exchangeServers | Format-Table -Property Name, AdminDisplayVersion -AutoSize
70 |
71 | Write-Output 'Changing InternalUrl settings'
72 | Write-Output ('New INTERNAL Url: {0}' -f $InternalUrl)
73 |
74 | # Set Internal Urls
75 | $exchangeServers | ForEach-Object{ Set-ClientAccessService -Identity $_.Name -AutodiscoverServiceInternalUri ('{0}/autodiscover/autodiscover.xml' -f $AutodiscoverUrl) -Confirm:$false}
76 | $exchangeServers | Get-WebServicesVirtualDirectory | Set-WebServicesVirtualDirectory -InternalUrl ('{0}/ews/exchange.asmx' -f $InternalUrl) -Confirm:$false
77 | $exchangeServers | Get-OabVirtualDirectory | Set-OabVirtualDirectory -InternalUrl ('{0}/oab' -f $InternalUrl) -Confirm:$false
78 | $exchangeServers | Get-OwaVirtualDirectory | Set-OwaVirtualDirectory -InternalUrl ('{0}/owa' -f $InternalUrl) -Confirm:$false
79 | $exchangeServers | Get-EcpVirtualDirectory | Set-EcpVirtualDirectory -InternalUrl ('{0}/ecp' -f $InternalUrl) -Confirm:$false
80 | $exchangeServers | Get-MapiVirtualDirectory | Set-MapiVirtualDirectory -InternalUrl ('{0}/mapi' -f $InternalUrl) -Confirm:$false
81 | $exchangeServers | Get-ActiveSyncVirtualDirectory | Set-ActiveSyncVirtualDirectory -InternalUrl ('{0}/Microsoft-Server-ActiveSync' -f $InternalUrl) -Confirm:$false
82 |
83 | Write-Output 'InternalUrl changed'
84 | }
85 |
86 | if($ExternalUrl -ne '' -and $null -ne $exchangeServers) {
87 |
88 | # Trim trailing "/"
89 | if($ExternalUrl.EndsWith('/')) {
90 | $ExternalUrl = $ExternalUrl.TrimEnd('/')
91 | }
92 |
93 | Write-Output 'Changing ExternalUrl settings'
94 | Write-Output ('New EXTERNAL Url: {0}' -f $ExternalUrl)
95 |
96 | # Set External Urls
97 | $exchangeServers | Get-WebServicesVirtualDirectory | Set-WebServicesVirtualDirectory -ExternalUrl ('{0}/ews/exchange.asmx' -f $ExternalUrl) -Confirm:$false
98 | $exchangeServers | Get-OabVirtualDirectory | Set-OabVirtualDirectory -ExternalUrl ('{0}/oab' -f $ExternalUrl) -Confirm:$false
99 | $exchangeServers | Get-OwaVirtualDirectory | Set-OwaVirtualDirectory -ExternalUrl ('{0}/owa' -f $ExternalUrl) -Confirm:$false
100 | $exchangeServers | Get-EcpVirtualDirectory | Set-EcpVirtualDirectory -ExternalUrl ('{0}/ecp' -f $ExternalUrl) -Confirm:$false
101 | $exchangeServers | Get-MapiVirtualDirectory | Set-MapiVirtualDirectory -ExternalUrl ('{0}/ecp' -f $ExternalUrl) -Confirm:$false
102 | $exchangeServers | Get-ActiveSyncVirtualDirectory | Set-ActiveSyncVirtualDirectory -ExternalUrl ('{0}/Microsoft-Server-ActiveSync' -f $ExternalUrl) -Confirm:$false
103 | }
104 |
105 | if(($InternalUrl -ne '') -or ($ExternalUrl -ne '')) {
106 | # Query Settings
107 | Write-Output ''
108 | Write-Output 'Current Url settings for CAS AutodiscoverServiceInternalUri'
109 | $exchangeServers | Get-ClientAccessServer | Format-List -Property Identity, AutodiscoverServiceInternalUri
110 | Write-Output 'Current Url settings for Web Services Virtual Directory'
111 | $exchangeServers | Get-WebServicesVirtualDirectory | Format-List -Property Identity, InternalUrl, ExternalUrl
112 | Write-Output 'Current Url settings for OAB Virtual Directory'
113 | $exchangeServers | Get-OabVirtualDirectory | Format-List -Property Identity, InternalUrl, ExternalUrl
114 | Write-Output 'Current Url settings for OWA Virtual Directory'
115 | $exchangeServers | Get-OwaVirtualDirectory | Format-List -Property Identity, InternalUrl,ExternalUrl
116 | Write-Output 'Current Url settings for ECP Virtual Directory'
117 | $exchangeServers | Get-EcpVirtualDirectory | Format-List -Property Identity, InternalUrl,ExternalUrl
118 | Write-Output 'Current Url settings for MAPI Virtual Directory'
119 | $exchangeServers | Get-MapiVirtualDirectory | Format-List -Property Identity, InternalUrl,ExternalUrl
120 | Write-Output 'Current Url settings for ActiveSync Virtual Directory'
121 | $exchangeServers | Get-ActiveSyncVirtualDirectory | Format-List -Property Identity, internalurl, ExternalUrl
122 | }
123 | else {
124 | Write-Output 'Nothing changed!'
125 | }
126 |
--------------------------------------------------------------------------------
/Exchange Server/Start-MailboxImport/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 26 Thomas Stensitzki
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Exchange Server/Start-MailboxImport/README.md:
--------------------------------------------------------------------------------
1 | # Start-MailboxImport.ps1
2 |
3 | Import one or more PST files into an exisiting mailbox or a archive.
4 |
5 | ## Description
6 |
7 | This script imports one or more PST files into a user mailbox or a user archive as batch.
8 |
9 | PST file names can used as target folder names for import. PST files are renamed to support file name limitations by New-MailboxImportRequest cmdlet.
10 |
11 | All files of a given folder will be imported into the user's mailbox.
12 |
13 | ## Requirements
14 |
15 | - Windows Server 2016+
16 | - Exchange Server 2016+
17 | - GlobalFunctions PowerShell Module, [https://www.powershellgallery.com/packages/GlobalFunctions](https://www.powershellgallery.com/packages/GlobalFunctions)
18 |
19 | ## Parameters
20 |
21 | ### Identity
22 |
23 | Mailbox identity in which the PST files get imported
24 |
25 | ### Archive
26 |
27 | Import PST files into the online archive.
28 |
29 | ### FilePath
30 |
31 | Folder which contains the PST files. Has to be an UNC path.
32 |
33 | ### FilenameAsTargetFolder
34 |
35 | Import the PST files into dedicated target folders. The folder name will equal the file name.
36 |
37 | ### BadItemLimit
38 |
39 | Standard is set to 0. Don't max it out because the script doesn't add "AcceptLargeDatalost".
40 |
41 | ### ContinueOnError
42 |
43 | If set the script continue with the next PST file if a import request failed.
44 |
45 | ### SecondsToWait
46 |
47 | Timespan to wait between import request staus checks in seconds. Default:
48 |
49 | ### IncludeFolders
50 |
51 | If set the import would only import the given folder + subfolders. Note: If you want to import subfolders you have to use /* at the end of the folder. (Test/*).
52 |
53 | ### TargetFolder
54 |
55 | Import the files in to definied target folder. Can't be used together with FilenameAsTargetFolder
56 |
57 | ### Recurse
58 |
59 | If this parameter is set all PST files in subfolders will be also imported
60 |
61 | ### RenameFileAfterImport
62 |
63 | Rename successfully imported PST files to simplify a re-run of the script. A .PST file will be renamed to .imported
64 |
65 | ## Examples
66 |
67 | ``` PowerShell
68 | .\Start-MailboxImport.ps1 -Identity testuser -Filepath "\\testserver\share"
69 | ```
70 |
71 | Import all PST files into the mailbox "testuser"
72 |
73 | ``` PowerShell
74 | .\Start-MailboxImport.ps1 -Identity testuser -Filepath "\\testserver\share\*" -FilenameAsTargetFolder -SecondsToWait 90
75 | ```
76 |
77 | Import all PST files into the mailbox "testuser". Use PST file name as target folder name. Wait 90 seconds between each status check
78 |
79 | ## Note
80 |
81 | THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE
82 | RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER.
83 |
84 | ## Credits
85 |
86 | Written by: Thomas Stensitzki
87 |
88 | Related blog post: [https://granikos.eu/simple-import-of-multiple-pst-files-for-a-single-user/](https://granikos.eu/simple-import-of-multiple-pst-files-for-a-single-user/)
89 |
90 | ### Stay connected
91 |
92 | * Blog: [https://blog.granikos.eu](https://blog.granikos.eu)
93 | * Bluesky: [https://bsky.app/profile/stensitzki.eu](https://bsky.app/profile/stensitzki.eu)
94 | * LinkedIn: [https://www.linkedin.com/in/thomasstensitzki](https://www.linkedin.com/in/thomasstensitzki)
95 | * YouTube: [https://www.youtube.com/@ThomasStensitzki](https://www.youtube.com/@ThomasStensitzki)
96 | * LinkTree: [https://linktr.ee/stensitzki](https://linktr.ee/stensitzki)
97 |
98 | For more Microsoft 365, Cloud Security, and Exchange Server stuff checkout services provided by Granikos
99 |
100 | * Website: [https://granikos.eu](https://granikos.eu)
101 | * Bluesky: [https://bsky.app/profile/granikos.eu](https://bsky.app/profile/granikos.eu)
102 |
--------------------------------------------------------------------------------
/Exchange Server/Test-ExchangeServerHealth/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) Thomas Stensitzki
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Exchange Server/Test-ExchangeServerHealth/README.md:
--------------------------------------------------------------------------------
1 | # Test-ExchangeServerHealth.ps1
2 |
3 |
4 |
5 | ## Description
6 |
7 |
8 | ## Requirements
9 |
10 | - Windows Server 2016+
11 | - Exchange Server 2016+
12 |
13 | ## Parameters
14 |
15 | ## Examples
16 |
17 | ### Example 1
18 |
19 | Checks all servers in the organization and outputs the results to the shell window.
20 |
21 | ``` PowerShell
22 | .\Test-ExchangeServerHealth.ps1 -Server HO-EX2010-MB1
23 | ```
24 |
25 | ### Example 2
26 |
27 | Checks the server HO-EX2010-MB1 and outputs the results to the shell window.
28 |
29 | ``` PowerShell
30 | .\Test-ExchangeServerHealth.ps1 -Server HO-EX2010-MB1
31 | ```
32 |
33 | ### Example 3
34 |
35 | Checks all servers in the organization, outputs the results to the shell window, a HTML report, and emails the HTML report to the address configured in the script.
36 |
37 | ``` PowerShell
38 | .\Test-ExchangeServerHealth.ps1 -ReportMode -SendEmail
39 | ```
40 |
41 | ## Note
42 |
43 | THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE
44 | RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER.
45 |
46 | ## Credits
47 |
48 | Original script by Paul Cunningham: [https://github.com/cunninghamp/Test-ExchangeServerHealth.ps1/tree/master](https://github.com/cunninghamp/Test-ExchangeServerHealth.ps1/tree/master)
49 |
50 | Updated for Exchange Server 2019 by: Thomas Stensitzki
51 |
52 | Related blog post:
53 |
54 | ### Stay connected
55 |
56 | - My Blog: [https://blog.granikos.eu](https://blog.granikos.eu)
57 | - Bluesky: [https://bsky.app/profile/stensitzki.bsky.social](https://bsky.app/profile/stensitzki.bsky.social)
58 | - LinkedIn: [https://www.linkedin.com/in/thomasstensitzki](https://www.linkedin.com/in/thomasstensitzki)
59 | - YouTube: [https://www.youtube.com/@ThomasStensitzki](https://www.youtube.com/@ThomasStensitzki)
60 | - LinkTree: [https://linktr.ee/stensitzki](https://linktr.ee/stensitzki)
61 |
62 | For more Office 365, Cloud Security, and Exchange Server stuff checkout services provided by Granikos
63 |
64 | - Website: [https://granikos.eu](https://www.granikos)
65 | - Bluesky: [https://bsky.app/profile/granikos.bsky.social](https://bsky.app/profile/granikos.bsky.social)
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 Thomas Stensitzki
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Misc/Get-Diskspace/Get-Diskspace.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | .SYNOPSIS
3 | Fetches disk/volume information from a given computer
4 |
5 | Thomas Stensitzki
6 |
7 | THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE
8 | RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER.
9 |
10 | Version 1.4, 2021-11-12
11 |
12 | Please use GitHub repository for ideas, comments, and suggestions.
13 |
14 | .LINK
15 | https://scripts.granikos.eu
16 |
17 | .DESCRIPTION
18 | This script fetches disk/volume information from a given computer and displays
19 |
20 | * Volume name
21 | * Capacity
22 | * Free Space
23 | * Boot Volume Status
24 | * System Volume Status
25 | * File Systemtype
26 |
27 | With -SendMail switch no data is returned to the console.
28 |
29 | .NOTES
30 | Requirements
31 | - Windows Server 2012 R2+
32 | - Remote WMI access
33 | - Exchange Server Management Shell (for AllExchangeServer switch)
34 |
35 | Revision History
36 | --------------------------------------------------------------------------------
37 | 1.0 Initial community release
38 | 1.1 Email reports added
39 | 1.11 Send email issue fixed
40 | 1.12 PowerShell hygiene applied
41 | 1.1.3 Version number adjusted, minor PowerShell adjustments
42 | 1.3 Tested for Windows Server 2019, minor PowerShell adjustments
43 | 1.4 TLS 1.2 added
44 |
45 | .PARAMETER ComputerName
46 | Can of the computer to fetch disk information from
47 |
48 | .PARAMETER Unit
49 | Target unit for disk space value (default = GB)
50 |
51 | .PARAMETER AllExchangeServer
52 | Switch to fetch disk space data from all Exchange Servers
53 |
54 | .PARAMETER SendMail
55 | Switch to send an Html report
56 |
57 | .PARAMETER MailFrom
58 | Email address of report sender
59 |
60 | .PARAMETER MailTo
61 | Email address of report recipient
62 |
63 | .PARAMETER MailServer
64 | SMTP Server for email report
65 |
66 | .EXAMPLE
67 | Get disk information from computer MYSERVER
68 |
69 | Get-Diskpace.ps1 -ComputerName MYSERVER
70 |
71 | .EXAMPLE
72 | Get disk information from computer MYSERVER in MB
73 |
74 | Get-Diskpace.ps1 -ComputerName MYSERVER -Unit MB
75 |
76 | .EXAMPLE
77 | Get disk information from all Exchange servers and send html email
78 |
79 | Get-Diskpace.ps1 -AllExchangeServer -SendMail -MailFrom postmaster@sedna-inc.com -MailTo exchangeadmin@sedna-inc.com -MailServer mail.sedna-inc.com
80 |
81 | #>
82 |
83 | [CmdletBinding()]
84 | param(
85 | [string]$ComputerName = $env:COMPUTERNAME,
86 | [string]$Unit = 'GB',
87 | [switch]$AllExchangeServer,
88 | [switch]$SendMail,
89 | [string]$MailFrom = '',
90 | [string]$MailTo = '',
91 | [string]$MailServer = ''
92 | )
93 |
94 |
95 | $Unit = $Unit.ToUpper()
96 | $now = Get-Date -Format F
97 | $ReportTitle = ('Diskspace Report - {0}' -f ($now))
98 | $global:Html = ''
99 |
100 | # Set TLS protocol to TLS 1.2
101 | [System.Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
102 |
103 | switch($Unit){
104 | 'GB' {
105 | $ConvertTo = 1GB
106 | }
107 | 'MB' {
108 | $ConvertTo = 1MB
109 | }
110 | }
111 |
112 | function Get-DiskspaceFromComputer {
113 | [CmdletBinding()]
114 | param(
115 | [string] $ServerName
116 | )
117 |
118 | if(($Unit -eq 'GB') -or ($Unit -eq 'MB')) {
119 |
120 | $ServerName = $ServerName.ToUpper()
121 |
122 | Write-Output ('Fetching Volume Data from {0}' -f ($ServerName))
123 |
124 | $WmiResult = Get-WmiObject Win32_Volume -ComputerName $ServerName | Select-Object Name, @{Label="Capacity ($Unit)";Expression={[decimal]::round($_.Capacity/$ConvertTo)}}, @{Label="FreeSpace ($Unit)";Expression={[decimal]::round($_.FreeSpace/$ConvertTo)}}, BootVolume, SystemVolume, FileSystem | Sort-Object Name
125 | $global:Html += $WmiResult | ConvertTo-Html -Fragment -PreContent ('Server {0}
' -f ($ServerName))
126 | }
127 |
128 | $WmiResult
129 | }
130 |
131 | Function Test-SendMail {
132 | if( ($SendMail) -and ($MailFrom -ne '') -and ($MailTo -ne '') -and ($MailServer -ne '') ) {
133 | return $true
134 | }
135 | else {
136 | return $false
137 | }
138 | }
139 |
140 | #### MAIN
141 | If (($SendMail) -and (!(Test-SendMail))) {
142 | Throw 'If -SendMail specified, -MailFrom, -MailTo and -MailServer must be specified as well!'
143 | }
144 |
145 | # Some CSS to get a pretty report
146 | $head = @"
147 |
148 |
149 | $($ReportTitle)
150 |
187 | "@
188 |
189 | if($AllExchangeServer) {
190 | $servers = Get-ExchangeServer | Sort-Object Name
191 |
192 | foreach($server in $servers) {
193 |
194 | $output = Get-DiskspaceFromComputer -ServerName $server.Name
195 |
196 | if(!($SendMail)) { $output | Format-Table -AutoSize }
197 |
198 | }
199 | }
200 | else {
201 |
202 | $output = Get-DiskspaceFromComputer -ServerName $ComputerName
203 |
204 | if(!($SendMail)) { $output | Format-Table -AutoSize }
205 |
206 | }
207 |
208 | if($SendMail) {
209 |
210 | [string]$Body = ConvertTo-Html -Body $global:Html -Title 'Status' -Head $head
211 |
212 | Send-MailMessage -From $MailFrom -To $Mailto -SmtpServer $MailServer -Body $Body -BodyAsHtml -Subject $ReportTitle
213 |
214 | Write-Output ('Email sent to {0}' -f ($MailTo))
215 | }
--------------------------------------------------------------------------------
/Misc/Get-Diskspace/Get-Diskspace.ps1.txt:
--------------------------------------------------------------------------------
1 | <#
2 | .SYNOPSIS
3 |
4 | Fetches disk/volume information from a given computer
5 |
6 | Thomas Stensitzki
7 |
8 | THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE
9 | RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER.
10 |
11 | Version 1.3, 2019-10-16
12 |
13 | Please send ideas, comments and suggestions to support@granikos.eu
14 |
15 | .LINK
16 |
17 | https://scripts.granikos.eu
18 |
19 | .DESCRIPTION
20 |
21 | This script fetches disk/volume information from a given computer and displays
22 |
23 | * Volume name
24 | * Capacity
25 | * Free Space
26 | * Free Space in percent
27 | * Boot Volume Status
28 | * System Volume Status
29 | * File Systemtype
30 |
31 | With -SendMail switch no data is returned to the console.
32 |
33 | .NOTES
34 |
35 | Requirements
36 | - Windows Server 2012 R2+
37 | - Remote WMI
38 | - Exchange Server Management Shell 2013+
39 |
40 | Revision History
41 | --------------------------------------------------------------------------------
42 | 1.0 Initial community release
43 | 1.1 Email reports added
44 | 1.11 Send email issue fixed
45 | 1.2 Some PowerShell hygiene, Html CSS changes
46 | 1.3 Tested for Windows Server 2019
47 | 1.4 Free Space percentage added, HTML formatting changed
48 |
49 | .PARAMETER ComputerName
50 | Can of the computer to fetch disk information from
51 |
52 | .PARAMETER Unit
53 | Target unit for disk space value (default = GB)
54 |
55 | .PARAMETER AllExchangeServer
56 | Switch to fetch disk space data from all Exchange Servers
57 |
58 | .PARAMETER SendMail
59 | Switch to send an Html report
60 |
61 | .PARAMETER MailFrom
62 | Email address of report sender
63 |
64 | .PARAMETER MailTo
65 | Email address of report recipient
66 |
67 | .PARAMETER MailServer
68 | SMTP Server for email report
69 |
70 | .EXAMPLE
71 | Get disk information from computer MYSERVER
72 |
73 | .\Get-Diskpace.ps1 -ComputerName MYSERVER
74 |
75 | .EXAMPLE
76 | Get disk information from computer MYSERVER in MB
77 |
78 | .\Get-Diskpace.ps1 -ComputerName MYSERVER -Unit MB
79 |
80 | .EXAMPLE
81 | Get disk information from all Exchange servers and send html email
82 |
83 | .\Get-Diskpace.ps1 -AllExchangeServer -SendMail -MailFrom postmaster@sedna-inc.com -MailTo exchangeadmin@sedna-inc.com -MailServer mail.sedna-inc.com
84 |
85 | #>
86 |
87 | [CmdletBinding()]
88 | param(
89 | [string] $ComputerName = $env:COMPUTERNAME,
90 | [ValidateSet('MB','GB')]
91 | [string] $Unit = 'GB',
92 | [switch] $AllExchangeServer,
93 | [switch] $SendMail,
94 | [string] $MailFrom = '',
95 | [string] $MailTo = '',
96 | [string] $MailServer = ''
97 | )
98 |
99 | $scriptVersion = '1.4'
100 | $Unit = $Unit.ToUpper()
101 | $now = Get-Date -Format F
102 | $ReportTitle = ('Diskspace Report - {0}' -f ($now))
103 | $script:Html = ''
104 |
105 | switch($Unit){
106 | 'GB' {
107 | $ConvertTo = 1GB
108 | }
109 | 'MB' {
110 | $ConvertTo = 1MB
111 | }
112 | }
113 |
114 | function Get-DiskspaceFromComputer {
115 | [CmdletBinding()]
116 | param(
117 | [string] $ServerName = ''
118 | )
119 |
120 | if(($Unit -eq 'GB') -or ($Unit -eq 'MB')) {
121 |
122 | $ServerName = $ServerName.ToUpper()
123 |
124 | Write-Output ('Fetching Volume Data from {0}' -f ($ServerName))
125 |
126 | # This assumes that the account running this script has permissions to access the remote computer using WMI
127 | $wmiRaw = Get-WmiObject -Class Win32_Volume -ComputerName $ServerName | where{$_.name -notlike '\\?\*'}
128 |
129 | $wmi = $wmiRaw | Select-Object -Property Name, `
130 | @{Label="Capacity ($Unit)";Expression={[decimal]::round($_.Capacity/$ConvertTo)}}, `
131 | @{Label="FreeSpace ($Unit)";Expression={[decimal]::round($_.FreeSpace/$ConvertTo)}}, `
132 | @{Label="FreeSpace (%)";Expression={[int](($_.FreeSpace/$_.Capacity) * 100).ToString("#")} }, `
133 | BootVolume, SystemVolume, FileSystem `
134 | | Sort-Object -Property Name
135 |
136 | $sumCapacity = ($wmiRaw | Measure-Object Capacity -Sum).Sum / 1gb
137 |
138 | # Add WMI data to HTML
139 | $script:Html += $wmi | ConvertTo-Html -Fragment -PreContent ('Server {0}
' -f ($ServerName)) -Postcontent ('{0:0} GB
' -f $sumCapacity)
140 | }
141 |
142 | $wmi
143 | }
144 |
145 | Function Test-SendMail {
146 | if( ($SendMail) -and ($MailFrom -ne '') -and ($MailTo -ne '') -and ($MailServer -ne '') ) {
147 | return $true
148 | }
149 | else {
150 | return $false
151 | }
152 | }
153 |
154 | #### MAIN
155 | If (($SendMail) -and (!(Test-SendMail))) {
156 | Throw 'If -SendMail specified, -MailFrom, -MailTo and -MailServer must be specified as well!'
157 | }
158 |
159 | # Some CSS to get a pretty report
160 | $head = @"
161 |
162 | $($ReportTitle)
163 |
214 | "@
215 |
216 | $script:Html += 'Note: Mounted VSS snapshots are excluded from this report
'
217 |
218 | if($AllExchangeServer) {
219 |
220 | # Fetch all Exchange Servers except Edge-Transport-Systems
221 | $servers = Get-ExchangeServer | where {$_.ServerRole -ne "Edge"}| Sort-Object -Property Name
222 |
223 | foreach($server in $servers) {
224 |
225 | $output = Get-DiskspaceFromComputer -ServerName $server.Name
226 |
227 | if(!($SendMail)) { $output | Format-Table -AutoSize }
228 | }
229 | }
230 | else {
231 |
232 | $output = Get-DiskspaceFromComputer -ServerName $ComputerName
233 |
234 | if(!($SendMail)) { $output | Format-Table -AutoSize }
235 | }
236 |
237 | if($SendMail) {
238 | [string]$Body = ConvertTo-Html -Body $script:Html -Title 'Status' -Head $head -Postcontent ('Script version: {0}
' -f $scriptVersion)
239 |
240 | Send-MailMessage -From $MailFrom -To $Mailto -SmtpServer $MailServer -Body $Body -BodyAsHtml -Subject $ReportTitle
241 |
242 | Write-Output ('Email sent to {0}' -f ($MailTo))
243 | }
--------------------------------------------------------------------------------
/Misc/Get-Diskspace/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) Thomas Stensitzki
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Misc/Get-Diskspace/README.md:
--------------------------------------------------------------------------------
1 | # Get-Diskspace.ps1
2 |
3 | Fetches disk/volume information from a given computer
4 |
5 | ## Description
6 |
7 | This script fetches disk/volume information from a given computer and displays
8 |
9 | - Volume name
10 | - Capacity
11 | - Free Space
12 | - Boot Volume Status
13 | - System Volume Status
14 | - File System Type
15 |
16 | With -SendMail switch no data is returned to the console.
17 |
18 | ## Requirements
19 |
20 | - Windows Server 2012R2, 2016, 2019
21 | - Exchange Server 2013+ (for AllExchangeServer switch)
22 | - WMI access to remote computers
23 |
24 | ## Parameters
25 |
26 | ### ComputerName
27 |
28 | Can of the computer to fetch disk information from
29 |
30 | ### Unit
31 |
32 | Target unit for disk space value (default = GB)
33 |
34 | ### AllExchangeServer
35 |
36 | Switch to fetch disk space data from all Exchange Servers
37 |
38 | ### SendMail
39 |
40 | Switch to send an Html report
41 |
42 | ### MailFrom
43 |
44 | Email address of report sender
45 |
46 | ### MailTo
47 |
48 | Email address of report recipient
49 |
50 | ### MailServer
51 |
52 | SMTP Server for email report
53 |
54 | ## Examples
55 |
56 | ``` PowerShell
57 | .\Get-Diskpace.ps1 -ComputerName MYSERVER
58 | ```
59 |
60 | Get disk information from computer MYSERVER
61 |
62 | ``` PowerShell
63 | .\Get-Diskpace.ps1 -ComputerName MYSERVER -Unit MB
64 | ```
65 |
66 | Get disk information from computer MYSERVER in MB
67 |
68 | ``` PowerShell
69 | .\Get-Diskpace.ps1 -AllExchangeServer -SendMail -MailFrom postmaster@sedna-inc.com -MailTo exchangeadmin@sedna-inc.com -MailServer mail.sedna-inc.com
70 | ```
71 |
72 | Get disk information from all Exchange servers and send html email
73 |
74 | ## Note
75 |
76 | THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE
77 | RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER.
78 |
79 | ## Credits
80 |
81 | Written by: Thomas Stensitzki
82 |
83 | ### Stay connected
84 |
85 | - My Blog: [https://blog.granikos.eu](https://blog.granikos.eu)
86 | - Bluesky: [https://bsky.app/profile/stensitzki.bsky.social](https://bsky.app/profile/stensitzki.bsky.social)
87 | - LinkedIn: [https://www.linkedin.com/in/thomasstensitzki](https://www.linkedin.com/in/thomasstensitzki)
88 | - YouTube: [https://www.youtube.com/@ThomasStensitzki](https://www.youtube.com/@ThomasStensitzki)
89 | - LinkTree: [https://linktr.ee/stensitzki](https://linktr.ee/stensitzki)
90 |
91 | For more Office 365, Cloud Security, and Exchange Server stuff checkout services provided by Granikos
92 |
93 | - Website: [https://granikos.eu](https://www.granikos)
94 | - Bluesky: [https://bsky.app/profile/granikos.bsky.social](https://bsky.app/profile/granikos.bsky.social)
--------------------------------------------------------------------------------
/Misc/Get-Diskspace/Screenshot-Email.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Apoc70/PowerShell-Scripts/ef43113b389502d309e246891fa9c7a3ad5a147e/Misc/Get-Diskspace/Screenshot-Email.PNG
--------------------------------------------------------------------------------
/Misc/Get-Diskspace/Screenshot-Shell.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Apoc70/PowerShell-Scripts/ef43113b389502d309e246891fa9c7a3ad5a147e/Misc/Get-Diskspace/Screenshot-Shell.PNG
--------------------------------------------------------------------------------
/Misc/Get-SoftwareInventory/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016,2025 Thomas Stensitzki
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Misc/Get-SoftwareInventory/README.md:
--------------------------------------------------------------------------------
1 | # Get-SoftwareInventory.ps1
2 |
3 | Script to generate an html reports of installed software, installed updates and installed components on a remote computer
4 |
5 | ## Description
6 |
7 | This script utilizes the slightly modified function Get-RemoteProgram by Jaap Brasser to fetch information about installed software.
8 |
9 | ## Parameters
10 |
11 | ### RemoteComputer
12 |
13 | Name of the computer to fetch data from (default: local computer)
14 |
15 | ### SoftwareFilter
16 |
17 | Filter for installed software. Only software matching the filter will be included in the report.
18 |
19 | ### ProgramsOnly
20 |
21 | Switch to only fetch installed programs and exclude updates and components
22 |
23 | ### SendMail
24 |
25 | Switch to send an Html report
26 |
27 | ### MailFrom
28 |
29 | Email address of report sender
30 |
31 | ### MailTo
32 |
33 | Email address of report recipient
34 |
35 | ### MailServer
36 |
37 | SMTP Server for email report
38 |
39 | ## Examples
40 |
41 | ``` PowerShell
42 | .\Get-SoftwareInventory.ps1 -SendMail -MailFrom postmaster@mcsmemail.de -MailTo it-support@mcsmemail.de -MailServer mymailserver.mcsmemail.de -RemoteComputer SERVER01,SERVER02
43 | ```
44 |
45 | Get software information about SERVER01, SERVER02 and send email report
46 |
47 | ``` PowerShell
48 | .\Get-SoftwareInventory.ps1 -RemoteComputer SERVER01,SERVER02
49 | ```
50 |
51 | Get software information about SERVER01, SERVER02 and write report to disk only
52 |
53 | ## Note
54 |
55 | THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE
56 | RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER.
57 |
58 | ## Credits
59 |
60 | Written by: Thomas Stensitzki
61 |
62 | ### Stay connected
63 |
64 | - My Blog: [https://blog.granikos.eu](https://blog.granikos.eu)
65 | - Bluesky: [https://bsky.app/profile/stensitzki.bsky.social](https://bsky.app/profile/stensitzki.bsky.social)
66 | - LinkedIn: [https://www.linkedin.com/in/thomasstensitzki](https://www.linkedin.com/in/thomasstensitzki)
67 | - YouTube: [https://www.youtube.com/@ThomasStensitzki](https://www.youtube.com/@ThomasStensitzki)
68 | - LinkTree: [https://linktr.ee/stensitzki](https://linktr.ee/stensitzki)
69 |
70 | For more Office 365, Cloud Security, and Exchange Server stuff checkout services provided by Granikos
71 |
72 | - Website: [https://granikos.eu](https://www.granikos)
73 | - Bluesky: [https://bsky.app/profile/granikos.bsky.social](https://bsky.app/profile/granikos.bsky.social)
74 |
75 | ### Additional Credits
76 |
77 | - Jaap Brasser
78 | - Georges Zwingelstein
79 |
--------------------------------------------------------------------------------
/Misc/Get-SoftwareInventory/Run-GetSoftwareInventory.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | Predefined script to fetch software inventory and send email report
3 |
4 | Not a real script, but Copyright (c) 2025 Thomas Stensitzki
5 | #>
6 |
7 | # Define server list
8 | $server = @('SERVER01','server02')
9 |
10 | .\Get-SoftwareInventory.ps1 -SendMail -MailFrom postmaster@mcsmemail.de -MailTo it@mcsmemail.de -MailServer myserver@mcsmemail.de -RemoteComputer $server
--------------------------------------------------------------------------------
/Misc/Get-SoftwareInventory/Run-GetSoftwareInventoryExchange.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | Script to fetch software inventory across all Exchange servers and some additional servers amd send the result via email.
3 | Must be executed in Exchange Management Shell (EMS)
4 |
5 | Not fancy script, but Copyright (c) 2025 Thomas Stensitzki
6 | #>
7 |
8 | # Define initial server list
9 | $server = @('SERVER01','server02')
10 |
11 | # Fetch Exchange server list and add to server list
12 | # Requires Exchange Management Shell
13 | Get-ExchangeServer | Select-Object Name | ForEach-Object{$server += $_.Name} | Sort-Object
14 |
15 | # Fetch software inventory for all servers
16 | # Requires Get-SoftwareInventory.ps1 in the same folder as this script
17 | .\Get-SoftwareInventory.ps1 -SendMail -MailFrom postmaster@mcsmail.de -MailTo it@mcsmail.de -MailServer mobile.mcsmail.de -RemoteComputer $server
--------------------------------------------------------------------------------
/Misc/New-TestUser/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) Thomas Stensitzki
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Misc/New-TestUser/New-TestUser.ps1:
--------------------------------------------------------------------------------
1 | param (
2 | [int]$UserCount = 5,
3 | [string]$Company = 'Varunagroup',
4 | [string]$UserNameCsv = '',
5 | [switch]$RandomPassword,
6 | [string]$TestUserPrefix = 'TestUser',
7 | [string]$PreferredLanguage = 'de-DE',
8 | [string]$TargetOU = 'OU=IT,dc=varunagroup,dc=de',
9 | [string]$TestUserOU = 'Test User',
10 | [string]$UpnDomain = 'varunagroup.de'
11 | )
12 |
13 | $DefaultPassword = 'Pa55w.rd'
14 |
15 | # User name prefix
16 | # New user object will be named TestUser1, TestUser2, ...
17 |
18 | # User object properties
19 | $GivenName = 'Test'
20 | $Surname = 'User'
21 | $JobTitle = @('Junior Consultant','Senior Consultant','Technical Consultant','Business Consultant','Sales Professional','Team Lead')
22 |
23 | # Import Active Directory PowerShell Module
24 | Import-Module -Name ActiveDirectory
25 |
26 | # Build OU Path
27 | $TestUserOUPath = ('OU={0},{1}' -f $TestUserOU, $TargetOU)
28 |
29 | # Check if OU exists
30 | $OUExists = $false
31 |
32 | try {
33 | $OUExists = [adsi]::Exists("LDAP://$TestUserOUPath")
34 | }
35 | catch {
36 | $OUExists =$true
37 | }
38 |
39 | if(-not $OUExists) {
40 | # Create new organizational unit for test users
41 | New-ADOrganizationalUnit -Name $TestUserOU -Path $TargetOU -ProtectedFromAccidentalDeletion:$false -Confirm:$false
42 | }
43 | else {
44 | Write-Warning -Message ('OU {0} exists please delete the OU and user objects manually, before running this script.' -f $TestUserOUPath)
45 | Exit
46 | }
47 |
48 | Write-Output -InputObject ('Creating {0} user object in {1}' -f $UserCount, $TestUserOUPath)
49 |
50 | # Create new user objects
51 | 1..$UserCount | ForEach-Object {
52 |
53 | # Get a random number for selecting a job title
54 | $random = Get-Random -Minimum 0 -Maximum (($JobTitle | Measure-Object). Count - 1)
55 |
56 | # Set user password
57 | if($RandomPassword) {
58 | # Create a random password
59 | $UserPassword = ConvertTo-SecureString -String (-join ((33..93) + (97..125) | Get-Random -Count 25 | % {[char]$_})) -AsPlainText -Force
60 | }
61 | else {
62 | # Use a fixed password
63 | $UserPassword = ConvertTo-SecureString -String $DefaultPassword -AsPlainText -Force
64 | }
65 |
66 | # Create a new user object
67 | # Adjust user name template and other attributes as needed
68 | New-ADUser -Name ('{0}{1}' -f $TestUserPrefix, $_) `
69 | -DisplayName ('{0} {1}' -f $TestUserPrefix, $_) `
70 | -GivenName $GivenName `
71 | -Surname (('{0}{1}' -f $Surname, $_)) `
72 | -OtherAttributes @{title=$JobTitle[$random];company=$Company;preferredLanguage=$PreferredLanguage} `
73 | -Path $TestUserOUPath `
74 | -AccountPassword $UserPassword `
75 | -UserPrincipalName ('{0}.{1]@{2}' -f $GivenName, (('{0}{1}' -f $Surname, $_)), $UpnDomain) `
76 | -Enabled:$True `
77 | -Confirm:$false
78 | }
--------------------------------------------------------------------------------
/Misc/New-TestUser/README.md:
--------------------------------------------------------------------------------
1 | # New-TestUser.ps1
2 | Short script to create Active Directory test user objects.
3 |
4 | ## Description
5 |
6 | The script creates a new organizational unit (OU) and a configured number of user accounts.
7 |
8 | You must configure all variables in the script to suit your environment.
9 |
10 | ## Examples
11 |
12 | ``` PowerShell
13 | .\New-TestUser.ps1
14 | ```
15 |
16 | ## Note
17 |
18 | THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE
19 | RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER.
20 |
21 | ## Credits
22 |
23 | Written by: Thomas Stensitzki
24 |
25 | ### Stay connected
26 |
27 | - My Blog: [https://blog.granikos.eu](https://blog.granikos.eu)
28 | - Bluesky: [https://bsky.app/profile/stensitzki.bsky.social](https://bsky.app/profile/stensitzki.bsky.social)
29 | - LinkedIn: [https://www.linkedin.com/in/thomasstensitzki](https://www.linkedin.com/in/thomasstensitzki)
30 | - YouTube: [https://www.youtube.com/@ThomasStensitzki](https://www.youtube.com/@ThomasStensitzki)
31 | - LinkTree: [https://linktr.ee/stensitzki](https://linktr.ee/stensitzki)
32 |
33 | For more Office 365, Cloud Security, and Exchange Server stuff checkout services provided by Granikos
34 |
35 | - Website: [https://granikos.eu](https://www.granikos)
36 | - Bluesky: [https://bsky.app/profile/granikos.bsky.social](https://bsky.app/profile/granikos.bsky.social)
--------------------------------------------------------------------------------
/Misc/New-TestUser/TestUserNames.xlsx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Apoc70/PowerShell-Scripts/ef43113b389502d309e246891fa9c7a3ad5a147e/Misc/New-TestUser/TestUserNames.xlsx
--------------------------------------------------------------------------------
/Misc/Set-PageFileSize/Set-PageFileSize.ps1:
--------------------------------------------------------------------------------
1 | # Ermitteln der aktuellen Größe des Arbeitsspeichers
2 | $TotalMemory = (Get-WmiObject -Class Win32_ComputerSystem).TotalPhysicalMemory
3 | $TotalMemoryMB = [math]::Round($TotalMemory / 1MB)
4 |
5 | # Berechnen der Größe der Auslagerungsdatei (25% des Arbeitsspeichers)
6 | $PageFileSizeMB = [math]::Round($TotalMemoryMB * 0.25)
7 |
8 | # Ausgabe der ermittelten Werte
9 | Write-Output "Gesamter Arbeitsspeicher: $TotalMemoryMB MB"
10 | Write-Output "Größe der Auslagerungsdatei (25% des Arbeitsspeichers): $PageFileSizeMB MB"
11 |
12 | # Deaktivieren der automatischen Verwaltung der Auslagerungsdatei
13 | $computersys = Get-WmiObject Win32_ComputerSystem -EnableAllPrivileges
14 | $computersys.AutomaticManagedPagefile = $False
15 | $computersys.Put()
16 |
17 | # Festlegen der Größe der Auslagerungsdatei auf 25% des Arbeitsspeichers
18 | Set-CimInstance -Query "SELECT * FROM Win32_PageFileSetting" -Property @{
19 | InitialSize = $PageFileSizeMB
20 | MaximumSize = $PageFileSizeMB
21 | }
22 |
23 | # Bestätigung der neuen Einstellungen
24 | Write-Output "Die Auslagerungsdatei wurde erfolgreich auf $PageFileSizeMB MB konfiguriert."
25 | Write-Output "Bitte starte den Computer neu, um die Änderungen zu übernehmen."
26 |
--------------------------------------------------------------------------------
/Network/Test-DNSRecords/Domains.txt:
--------------------------------------------------------------------------------
1 | Domain,TenantDomain
2 | varunagroup.de,
3 | ixion-inc.de,
4 | granikos.eu,granikoseu.onmicrosoft.com
--------------------------------------------------------------------------------
/Network/Test-NetworkAdapters/Test-NetworkAdapters.ps1:
--------------------------------------------------------------------------------
1 | # Abrufen aller Netzwerkschnittstellen und ihrer IP-Adressen
2 | $networkAdapters = Get-NetIPAddress | Where-Object { $_.InterfaceAlias -notlike "*Loopback*" }
3 |
4 | foreach ($adapter in $networkAdapters) {
5 | $interfaceAlias = $adapter.InterfaceAlias
6 | $ipAddress = $adapter.IPAddress
7 | $addressFamily = $adapter.AddressFamily
8 | $dhcpEnabled = (Get-NetIPInterface -InterfaceAlias $interfaceAlias -AddressFamily $addressFamily).Dhcp
9 |
10 | # Ausgabe der Netzwerkkonfiguration je nach Adressfamilie
11 | if ($addressFamily -eq "IPv4") {
12 | Write-Output "Netzwerkadapter: $interfaceAlias (IPv4)"
13 | Write-Output "IP-Adresse : $ipAddress"
14 | Write-Output "DHCP aktiviert : $dhcpEnabled"
15 | Write-Output "-----------------------------"
16 |
17 | # Überprüfen, ob DHCP deaktiviert ist (statische IP-Adresse)
18 | if ($dhcpEnabled -eq "Disabled") {
19 | Write-Output "> Der Adapter $interfaceAlias (IPv4) ist für eine fest zugewiesene IP-Adresse konfiguriert."
20 | } else {
21 | Write-Output "> Der Adapter $interfaceAlias (IPv4) verwendet DHCP."
22 | }
23 | Write-Output "============================="
24 | } elseif ($addressFamily -eq "IPv6") {
25 | Write-Output "Netzwerkadapter: $interfaceAlias (IPv6)"
26 | Write-Output "IP-Adresse : $ipAddress"
27 | Write-Output "DHCP aktiviert : $dhcpEnabled"
28 | Write-Output "-----------------------------"
29 |
30 | # Überprüfen, ob DHCP deaktiviert ist (statische IP-Adresse)
31 | if ($dhcpEnabled -eq "Disabled") {
32 | Write-Output "> Der Adapter $interfaceAlias (IPv6) ist für eine fest zugewiesene IP-Adresse konfiguriert."
33 | } else {
34 | Write-Output "> Der Adapter $interfaceAlias (IPv6) verwendet DHCP."
35 | }
36 | Write-Output "============================="
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/README-IMAGES/Add-CustomCalendarEvents-OWA.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Apoc70/PowerShell-Scripts/ef43113b389502d309e246891fa9c7a3ad5a147e/README-IMAGES/Add-CustomCalendarEvents-OWA.png
--------------------------------------------------------------------------------
/README-IMAGES/Add-CustomCalendarEvents-PowerShell.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Apoc70/PowerShell-Scripts/ef43113b389502d309e246891fa9c7a3ad5a147e/README-IMAGES/Add-CustomCalendarEvents-PowerShell.png
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # PowerShell Scripts
2 |
3 | This GitHub Repository contains most of my public PowerShell scripts. In the past I used dedicated repositories per script. I will archive those repositories after moving the script to this repository. C#-related projects remain in separate repositories.
4 |
5 | ## Exchange Online
6 |
7 | Script for Exchange Online
8 |
9 | - [Move-MigrationUser.ps1](/Exchange%20Online/Move-MigrationUser)
10 |
11 | This script creates a new migration batch and moves migration users from one batch to the new batch
12 |
13 | ## Modern Exchange Server
14 |
15 | Scripts for Exchange 2013, 2016, and 2019
16 |
17 | - [Copy-ReceiveConnector.ps1](/Exchange%20Server/Copy-ReceiveConnector)
18 |
19 | Copy a selected receive connector and it's configuration and permissions to other Exchange Servers
20 |
21 | - [Export-MessageQueue.ps1](/Exchange%20Server/Export-MessageQueue)
22 |
23 | Export messages from a transport queue to file system for manual replay
24 |
25 | - [Get-ExchangeEnvironmentReport.ps1](/Exchange%20Server/Get-ExchangeEnvironmentReport)
26 |
27 | Creates an HTML report describing the On-Premises Exchange environment.
28 |
29 | - [Get-RemoteSmtpServers.ps1](/Exchange%20Server/Get-RemoteSmtpServers)
30 |
31 | Fetch all remote SMTP servers from Exchange receive connector logs
32 |
33 | - [Import-EdgeSubscription.ps1](/Exchange%20Server/Import-EdgeSubscription)
34 |
35 | Little helper script when working with Edge Transport Server subscriptions
36 |
37 | - [New-RoomMailbox.ps1](/Exchange%20Server/New-RoomMailbox)
38 |
39 | This scripts creates a new room mailbox and security groups for full access and and send-as delegation. As a third security group a dedicated group for allowed users to book the new room is created. The CalenderBooking security group is only created, but not assigned to the room mailbox. Security groups are created using a naming convention.
40 |
41 | - [New-TeamMailbox.ps1](/Exchange%20Server/New-TeamMailbox)
42 |
43 | Creates a new shared mailbox, security groups for full access and send-as permission and adds the security groups to the shared mailbox configuration.
44 |
45 | - [Purge-LogFiles.ps1](/Exchange%20Server/Purge-LogFiles)
46 |
47 | PowerShell script for modern Exchange Server environments to clean up Exchange Server and IIS log files
48 |
49 | - [Start-MailboxImport.ps1](/Exchange%20Server/Start-MailboxImport)
50 |
51 | Import one or more pst files into an exisiting mailbox or a archive
52 |
53 |
54 | ## Legacy Exchange Server
55 |
56 | Scripts for Exchange Server 2010 and older
57 |
58 | - TBD
59 |
60 | ## Misc
61 |
62 | Some usefull scripts not Exchange related
63 |
64 | - [Get-Diskspace.ps1](/Misc/Get-Diskspace)
65 |
66 | Fetches disk/volume information from a given computer
67 |
68 | ## Network
69 |
70 | Some useful network related scripts
71 |
72 | - [Test-DNSRecords.ps1](/Network/Test-DNSRecords)
73 |
74 | ### Stay connected
75 |
76 | - My Blog: [https://blog.granikos.eu](https://blog.granikos.eu)
77 | - Bluesky: [https://bsky.app/profile/stensitzki.eu](https://bsky.app/profile/stensitzki.eu)
78 | - LinkedIn: [https://www.linkedin.com/in/thomasstensitzki](https://www.linkedin.com/in/thomasstensitzki)
79 | - YouTube: [https://www.youtube.com/@ThomasStensitzki](https://www.youtube.com/@ThomasStensitzki)
80 | - LinkTree: [https://linktr.ee/stensitzki](https://linktr.ee/stensitzki)
81 |
82 | For more Office 365, Cloud Security, and Exchange Server stuff checkout services provided by Granikos
83 |
84 | - Website: [https://granikos.eu/](https://granikos.eu/)
85 | - Bluesky: [https://bsky.app/profile/granikos.eu](https://bsky.app/profile/granikos.eu)
--------------------------------------------------------------------------------
/Script Template/PowerShell-Script-Template.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | .SYNOPSIS
3 |
4 | Short description
5 |
6 | Remove any comment section not used, e.g., LINK, INPUTS, or OUTPUTS
7 | Additonal information on comment based help: https://learn.microsoft.com/powershell/module/microsoft.powershell.core/about/about_comment_based_help
8 |
9 | .DESCRIPTION
10 |
11 | Long description
12 |
13 | THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE RISK
14 | OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER.
15 |
16 | .NOTES
17 |
18 | Requirements
19 |
20 | - Windows Server 2019+
21 |
22 | Revision History
23 | --------------------------------------------------------------------------------
24 | 1.0 Initial release
25 | 1.1 xxxx
26 |
27 | .LINK
28 |
29 | https://scripts.granikos.eu
30 |
31 | .PARAMETER ExportFileWithoutDefaultValue
32 |
33 | Madatory export file name
34 |
35 | .PARAMETER ExportFileWithDefaultValue
36 |
37 | Export file name example using a default value of 'ExportFile.csv''
38 |
39 | .EXAMPLE
40 |
41 | Get-SomeCmdlet.ps1 -SomeParameter1 'ExportToUTF8.csv'
42 |
43 | Executes the script and exports the gathered information to a CSV file named ExportToUTF8.csv
44 |
45 | #>
46 |
47 | # Parameter section with examples
48 | # Additional information parameters: https://learn.microsoft.com/powershell/module/microsoft.powershell.core/about/about_functions_advanced_parameters
49 | [CmdletBinding()]
50 | param(
51 | [Parameter(
52 | Mandatory=$true,
53 | HelpMessage = "Full path of the output file to be generated. If only filename is specified, then the output file will be generated in the current directory.")]
54 | [ValidateNotNull()]
55 | [string]$ExportFileWithoutDefaultValue,
56 | [string]$ExportFileWithDefaultValue = 'ExportFile.csv'
57 | )
58 |
59 | #region Initialize Script
60 |
61 | # Measure script running time
62 | $StopWatch = [System.Diagnostics.Stopwatch]::StartNew()
63 |
64 | $script:ScriptPath = Split-Path $script:MyInvocation.MyCommand.Path
65 | $script:ScriptName = $MyInvocation.MyCommand.Name
66 |
67 | # Load required module for logging
68 | if($null -ne (Get-Module -Name GlobalFunctions -ListAvailable).Version) {
69 | Import-Module -Name GlobalFunctions
70 | }
71 | else {
72 | Write-Warning -Message 'Unable to load GlobalFunctions PowerShell module.'
73 | Write-Warning -Message 'Open an administrative PowerShell session and run Import-Module GlobalFunctions'
74 | Write-Warning -Message 'Please check http://bit.ly/GlobalFunctions for further instructions'
75 | exit
76 | }
77 |
78 | # Create a logging oobject
79 | $logger = New-Logger -ScriptRoot $script:ScriptPath -ScriptName $script:ScriptName -LogFileRetention 14
80 | $logger.Purge()
81 | $logger.Write('Script started')
82 |
83 | #endregion
84 |
85 | #region Functions
86 |
87 | <#
88 | Load script settings from dedicated settings.xml
89 | The following comment block contains an XML example
90 |
91 |
92 |
93 |
94 | 4711
95 |
96 |
97 |
98 | #>
99 | function LoadScriptSettings {
100 | if (Test-Path -Path ('{0}\Settings.xml' -f $script:ScriptPath)) {
101 | # Load Script settings
102 | [xml]$Config = Get-Content -Path ('{0}\Settings.xml' -f $script:ScriptPath)
103 |
104 | Write-Verbose -Message 'Loading script settings'
105 |
106 | # Group settings
107 | $someValue = $Config.Settings.Group.SomeValue
108 |
109 | Write-Verbose -Message 'Script settings loaded'
110 | }
111 | else {
112 | Write-Error -Message 'Script settings file settings.xml missing. Please check documentation.'
113 | exit 99
114 | }
115 | }
116 |
117 | <#
118 |
119 | Function to prompt user for yes/no
120 |
121 | Depending on the choice order, the functions returns
122 | 0 = YES
123 | 1 = NO
124 |
125 | Example: Interactive Y/N query before running additional code
126 |
127 | if((Request-Choice -Caption ('Do you want to apply the settings to {0}?' -f $SomeVariable)) -eq 0) {
128 | # Yes Option
129 | }
130 | else {
131 | # No Option
132 | }
133 |
134 | #>
135 | function Request-Choice {
136 | [CmdletBinding()]
137 | param(
138 | [Parameter(
139 | Mandatory=$true,
140 | HelpMessage = "Provide a caption for the Y/N question.")]
141 | [string]$Caption
142 | )
143 | $choices = [System.Management.Automation.Host.ChoiceDescription[]]@('&Yes','&No')
144 | [int]$defaultChoice = 1
145 |
146 | $choiceReturn = $Host.UI.PromptForChoice($Caption, '', $choices, $defaultChoice)
147 |
148 | return $choiceReturn
149 | }
150 |
151 | #endregion
152 |
153 | #region MAIN
154 |
155 | # 1. Load script settings
156 | LoadScriptSettings
157 |
158 | <#
159 | The main code
160 | #>
161 |
162 | #endregion
163 |
164 | #region End Script
165 |
166 | # Stop watch
167 | $StopWatch.Stop()
168 |
169 | # Write script runtime
170 | Write-Verbose -Message ('It took {0:00}:{1:00}:{2:00} to run the script.' -f $StopWatch.Elapsed.Hours, $StopWatch.Elapsed.Minutes, $StopWatch.Elapsed.Seconds)
171 | $logger.Write( ('It took {0:00}:{1:00}:{2:00} to run the script.' -f $StopWatch.Elapsed.Hours, $StopWatch.Elapsed.Minutes, $StopWatch.Elapsed.Seconds) )
172 | $logger.Write('Script finished')
173 |
174 | #endregion
--------------------------------------------------------------------------------