Added Files
This commit is contained in:
@@ -0,0 +1,44 @@
|
|||||||
|
# Script to add user to an AzureAD role
|
||||||
|
|
||||||
|
<#
|
||||||
|
You can find a list of available roles in the following Microsoft article
|
||||||
|
https://docs.microsoft.com/en-us/azure/active-directory/active-directory-assign-admin-roles-azure-portal
|
||||||
|
Double check using Get-AzureADDirectoryRole as they don't always have the same name in PowerShell as the GUI
|
||||||
|
|
||||||
|
Built from the code example on the following Mircosoft page
|
||||||
|
https://docs.microsoft.com/en-us/powershell/module/azuread/add-azureaddirectoryrolemember?view=azureadps-2.0
|
||||||
|
#>
|
||||||
|
|
||||||
|
|
||||||
|
# User UPN to assign role to
|
||||||
|
$roleUser = ''
|
||||||
|
|
||||||
|
# Role Name to Assign
|
||||||
|
$roleName = ''
|
||||||
|
|
||||||
|
|
||||||
|
# Import AzureAD module and Connect
|
||||||
|
Import-Module AzureAD
|
||||||
|
Connect-AzureAD
|
||||||
|
|
||||||
|
# Fetch user to assign to role
|
||||||
|
$roleMember = Get-AzureADUser -ObjectId $roleUser
|
||||||
|
|
||||||
|
# Fetch role instance
|
||||||
|
$role = Get-AzureADDirectoryRole | Where-Object {$_.displayName -eq $roleName}
|
||||||
|
|
||||||
|
# If role instance does not exist, instantiate it based on the role template
|
||||||
|
if (!($role)) {
|
||||||
|
# Instantiate an instance of the role template
|
||||||
|
$roleTemplate = Get-AzureADDirectoryRoleTemplate | Where-Object {$_.displayName -eq $roleName}
|
||||||
|
Enable-AzureADDirectoryRole -RoleTemplateId $roleTemplate.ObjectId
|
||||||
|
|
||||||
|
# Fetch role instance again
|
||||||
|
$role = Get-AzureADDirectoryRole | Where-Object {$_.displayName -eq $roleName}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Add user to role
|
||||||
|
Add-AzureADDirectoryRoleMember -ObjectId $role.ObjectId -RefObjectId $roleMember.ObjectId
|
||||||
|
|
||||||
|
# Fetch role membership for role to confirm
|
||||||
|
Get-AzureADDirectoryRoleMember -ObjectId $role.ObjectId | Get-AzureADUser
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
# Bulk add a new licence to users on the basis of what licence they currently have.
|
||||||
|
#
|
||||||
|
# An updated version of my previous MSOL script, now using AzureAD.
|
||||||
|
# This is useful for something like adding Office 365 ATP to everyone who currently has E3, for example.
|
||||||
|
#
|
||||||
|
# List of available SKUs can be obtained with (Get-AzureADSubscribedSku).SkuPartNumber
|
||||||
|
#
|
||||||
|
|
||||||
|
# What licence do the users currently have?
|
||||||
|
$existingLicence = 'ENTERPRISEPACK'
|
||||||
|
|
||||||
|
# What licence are we adding?
|
||||||
|
$licenceToAdd = 'ATP_ENTERPRISE'
|
||||||
|
|
||||||
|
# Import AzureAD module and connect
|
||||||
|
Import-Module AzureAD
|
||||||
|
Connect-AzureAD
|
||||||
|
|
||||||
|
# Get all available licence skus
|
||||||
|
$allSkus = Get-AzureADSubscribedSku
|
||||||
|
|
||||||
|
# Get sku ID for existing licence
|
||||||
|
$existingSkuID = ($allSkus | Where-Object {$_.SkuPartNumber -eq $existingLicence}).SkuId
|
||||||
|
|
||||||
|
# Get sku ID for new licence
|
||||||
|
$newSkuID = ($allSkus | Where-Object {$_.SkuPartNumber -eq $licenceToAdd}).SkuId
|
||||||
|
|
||||||
|
# Find everyone who has the existing licence but not the licence we're adding
|
||||||
|
$users = Get-AzureADUser -All $true | Where-Object {$_.AssignedLicenses.SkuId -match $existingSkuID -and !($_.AssignedLicenses.SkuId -match $newSkuID)}
|
||||||
|
|
||||||
|
# Create a new licence object for the licence we're adding
|
||||||
|
$newLicense = New-Object -TypeName Microsoft.Open.AzureAD.Model.AssignedLicense
|
||||||
|
$newLicense.SkuId = $newSkuID
|
||||||
|
|
||||||
|
# Create a new assigned licenses object and add the licence we're adding to add licenses
|
||||||
|
$newLicenses = New-Object -TypeName Microsoft.Open.AzureAD.Model.AssignedLicenses
|
||||||
|
$newLicenses.AddLicenses = $newLicense
|
||||||
|
|
||||||
|
# Add the new licence to each user in the list
|
||||||
|
foreach ($user in $users) {
|
||||||
|
Set-AzureADUserLicense -ObjectId $user.UserPrincipalName -AssignedLicenses $newLicenses
|
||||||
|
}
|
||||||
|
|
||||||
|
# Disconnect from AzureAD
|
||||||
|
Disconnect-AzureAD
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
# Bulk add additional licences to a list of users.
|
||||||
|
#
|
||||||
|
# An updated version of my previous MSOL script, now using AzureAD.
|
||||||
|
#
|
||||||
|
# List of available SKUs can be obtained with (Get-AzureADSubscribedSku).SkuPartNumber
|
||||||
|
#
|
||||||
|
|
||||||
|
# Where is the list of user UPN's?
|
||||||
|
$userListPath = 'C:\Temp\UserList.txt'
|
||||||
|
|
||||||
|
# What licences are we adding?
|
||||||
|
$licencesToAdd = @('ENTERPRISEPACK','ATP_ENTERPRISE','EMSPREMIUM')
|
||||||
|
|
||||||
|
# Import AzureAD module and connect
|
||||||
|
Import-Module AzureAD
|
||||||
|
Connect-AzureAD
|
||||||
|
|
||||||
|
# Import list of users from file
|
||||||
|
$userList = Get-Content -Path $userListPath | Sort-Object
|
||||||
|
|
||||||
|
# Get all available licence skus
|
||||||
|
$newSkuIDs = (Get-AzureADSubscribedSku | Where-Object {$_.SkuPartNumber -in $licencesToAdd}).SkuId
|
||||||
|
|
||||||
|
# Create a licenses object
|
||||||
|
$licenses = New-Object -TypeName Microsoft.Open.AzureAD.Model.AssignedLicenses
|
||||||
|
|
||||||
|
# Add the licences to add to the licences object
|
||||||
|
foreach ($newSkuID in $newSkuIDs) {
|
||||||
|
$newLicense = New-Object -TypeName Microsoft.Open.AzureAD.Model.AssignedLicense
|
||||||
|
$newLicense.SkuId = $newSkuID
|
||||||
|
$licenses.AddLicenses += $newLicense
|
||||||
|
}
|
||||||
|
|
||||||
|
# Add the licenses
|
||||||
|
foreach ($username in $userList) {
|
||||||
|
try {
|
||||||
|
$user = Get-AzureADUser -ObjectId $username -ErrorAction Stop
|
||||||
|
if ($user.AccountEnabled -eq $true) {
|
||||||
|
Set-AzureADUserLicense -ObjectId $user.UserPrincipalName -AssignedLicenses $licenses -ErrorAction Stop
|
||||||
|
Write-Output -InputObject ('Licence(s) added for user account ' + $user.UserPrincipalName + '.')
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Write-Output -InputObject ('User account ' + $user.UserPrincipalName + ' is disabled.')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Output -InputObject ('There was a problem updating licences for user account ' + $user.UserPrincipalName + '.')
|
||||||
|
Write-Output -InputObject $Error[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Disconnect from AzureAD
|
||||||
|
Disconnect-AzureAD
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
# Script to add multiple users to multiple AzureAD roles
|
||||||
|
|
||||||
|
<#
|
||||||
|
You can find a list of available roles in the following Microsoft article
|
||||||
|
https://docs.microsoft.com/en-us/azure/active-directory/active-directory-assign-admin-roles-azure-portal
|
||||||
|
Double check using Get-AzureADDirectoryRole as they don't always have the same name in PowerShell as the GUI
|
||||||
|
|
||||||
|
Built from the code example on the following Mircosoft page
|
||||||
|
https://docs.microsoft.com/en-us/powershell/module/azuread/add-azureaddirectoryrolemember?view=azureadps-2.0
|
||||||
|
#>
|
||||||
|
|
||||||
|
|
||||||
|
# User UPNs to assign roles to
|
||||||
|
$roleUsers = @('')
|
||||||
|
|
||||||
|
# Role Names to Assign
|
||||||
|
$roleNames = @('')
|
||||||
|
|
||||||
|
|
||||||
|
# Import AzureAD module and Connect
|
||||||
|
Import-Module AzureAD
|
||||||
|
Connect-AzureAD
|
||||||
|
|
||||||
|
# Run through the list of users
|
||||||
|
foreach ($roleUser in $roleUsers) {
|
||||||
|
# Fetch user to assign to role
|
||||||
|
$roleMember = Get-AzureADUser -ObjectId $roleUser
|
||||||
|
|
||||||
|
# Run through the list of roles
|
||||||
|
foreach ($roleName in $roleNames) {
|
||||||
|
# Fetch User Account Administrator role instance
|
||||||
|
$role = Get-AzureADDirectoryRole | Where-Object { $_.displayName -eq $roleName }
|
||||||
|
|
||||||
|
# If role instance does not exist, instantiate it based on the role template
|
||||||
|
if (!($role)) {
|
||||||
|
# Instantiate an instance of the role template
|
||||||
|
$roleTemplate = Get-AzureADDirectoryRoleTemplate | Where-Object { $_.displayName -eq $roleName }
|
||||||
|
Enable-AzureADDirectoryRole -RoleTemplateId $roleTemplate.ObjectId
|
||||||
|
|
||||||
|
# Fetch User Account Administrator role instance again
|
||||||
|
$role = Get-AzureADDirectoryRole | Where-Object { $_.displayName -eq $roleName }
|
||||||
|
}
|
||||||
|
|
||||||
|
# Get existing users with the role assigned
|
||||||
|
$existingRoleUsers = (Get-AzureADDirectoryRoleMember -ObjectId $role.ObjectId).UserPrincipalName
|
||||||
|
|
||||||
|
# Assign role to new user if not already in the list
|
||||||
|
if ($roleMember.UserPrincipalName -notin $existingRoleUsers) {
|
||||||
|
Add-AzureADDirectoryRoleMember -ObjectId $role.ObjectId -RefObjectId $roleMember.ObjectId -ErrorAction Stop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Uncomment to fetch role membership for each role to confirm
|
||||||
|
#foreach ($roleName in $roleNames) {
|
||||||
|
# Write-Output -InputObject ('Members for role: ' + $roleName)
|
||||||
|
# Get-AzureADDirectoryRoleMember -ObjectId $role.ObjectId | Get-AzureADUser | Where-Object {$_.UserPrincipalName -in $roleUsers} | Format-List -Property DisplayName,UserPrincipalName
|
||||||
|
#}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
# Script to bulk disable guest accounts in Azure AD, either all or for a specific domain
|
||||||
|
#
|
||||||
|
|
||||||
|
# Are we disabling for a specific guest domain? Leave blank for all guests.
|
||||||
|
$guestDomain = ''
|
||||||
|
|
||||||
|
# Install the AzureAD module, uncomment if required
|
||||||
|
#Install-Module -Name AzureAD
|
||||||
|
|
||||||
|
# Import the AzureAD module and connect to Azure AD
|
||||||
|
Import-Module -Name AzureAD
|
||||||
|
Connect-AzureAD
|
||||||
|
|
||||||
|
# Get a list of all guests from Azure AD either matching the domain or all guests
|
||||||
|
if ($guestDomain -ne '') {
|
||||||
|
$guestAccounts = Get-AzureADUser -All $true | Where-Object { $_.AccountEnabled -eq $true -and $_.UserType -eq 'Guest' -and $_.Mail -like ('*' + $guestDomain) }
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$guestAccounts = Get-AzureADUser -All $true | Where-Object { $_.AccountEnabled -eq $true -and $_.UserType -eq 'Guest' }
|
||||||
|
}
|
||||||
|
|
||||||
|
# Disable the guest accounts
|
||||||
|
try {
|
||||||
|
$guestAccounts | Set-AzureADUser -AccountEnabled $false -ErrorAction Stop
|
||||||
|
Write-Output -InputObject ('The specified guest accounts have been disabled.')
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Output -InputObject ('Failed - The specified guest accounts have not been disabled.')
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
# Script to bulk enable guest accounts in Azure AD, either all or for a specific domain
|
||||||
|
#
|
||||||
|
|
||||||
|
# Are we disabling for a specific guest domain? Leave blank for all guests.
|
||||||
|
$guestDomain = ''
|
||||||
|
|
||||||
|
# Install the AzureAD module, uncomment if required
|
||||||
|
#Install-Module -Name AzureAD
|
||||||
|
|
||||||
|
# Import the AzureAD module and connect to Azure AD
|
||||||
|
Import-Module -Name AzureAD
|
||||||
|
Connect-AzureAD
|
||||||
|
|
||||||
|
# Get a list of all guests from Azure AD either matching the domain or all guests
|
||||||
|
if ($guestDomain -ne '') {
|
||||||
|
$guestAccounts = Get-AzureADUser -All $true | Where-Object { $_.AccountEnabled -eq $false -and $_.UserType -eq 'Guest' -and $_.Mail -like ('*' + $guestDomain) }
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$guestAccounts = Get-AzureADUser -All $true | Where-Object { $_.AccountEnabled -eq $false -and $_.UserType -eq 'Guest' }
|
||||||
|
}
|
||||||
|
|
||||||
|
# Enable the guest accounts
|
||||||
|
$guestAccounts | Set-AzureADUser -AccountEnabled $true
|
||||||
|
Write-Output -InputObject ('The specified guest accounts have been disabled.')
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
# Find all guests with unaccepted invites over a specific time period and remove them
|
||||||
|
#
|
||||||
|
|
||||||
|
# How many days are we allowing for invites to be accepted? (This can be 0 for all)
|
||||||
|
$cutOffDays = 30
|
||||||
|
|
||||||
|
# Install the AzureAD module, uncomment if required
|
||||||
|
#Install-Module -Name AzureAD
|
||||||
|
|
||||||
|
# Import the AzureAD module and connect to Azure AD
|
||||||
|
Import-Module -Name AzureAD
|
||||||
|
Connect-AzureAD
|
||||||
|
|
||||||
|
# Get the cut off date
|
||||||
|
$cutOffDate = (Get-Date).AddDays(-$cutOffDays)
|
||||||
|
|
||||||
|
# Get a list of all guests with unaccepted invites older than the specificed timeframe
|
||||||
|
$unacceptedGuests = Get-AzureADUser -All $true | Where-Object { $_.UserType -eq 'Guest' -and $_.UserState -eq 'PendingAcceptance' -and $_.RefreshTokensValidFromDateTime -lt $cutOffDate }
|
||||||
|
|
||||||
|
# Remove the guest accounts
|
||||||
|
$unacceptedGuests | Remove-AzureADUser
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
# Bulk remove licences to a list of users.
|
||||||
|
#
|
||||||
|
# List of available SKUs can be obtained with (Get-AzureADSubscribedSku).SkuPartNumber
|
||||||
|
#
|
||||||
|
|
||||||
|
# Where is the list of user UPN's?
|
||||||
|
$userListPath = 'C:\Temp\UserList.txt'
|
||||||
|
|
||||||
|
# What licences are we removing?
|
||||||
|
$licencesToRemove = @('ENTERPRISEPACK','ATP_ENTERPRISE','EMSPREMIUM')
|
||||||
|
|
||||||
|
# Import AzureAD module and connect
|
||||||
|
Import-Module AzureAD
|
||||||
|
Connect-AzureAD
|
||||||
|
|
||||||
|
# Import list of users from file
|
||||||
|
$userList = Get-Content -Path $userListPath | Sort-Object
|
||||||
|
|
||||||
|
# Create a licenses object
|
||||||
|
$licenses = New-Object -TypeName Microsoft.Open.AzureAD.Model.AssignedLicenses
|
||||||
|
|
||||||
|
# Get SkuIDs for licence(s) to be removed
|
||||||
|
$licenses.RemoveLicenses = (Get-AzureADSubscribedSku | Where-Object {$_.SkuPartNumber -in $licencesToRemove}).SkuId
|
||||||
|
|
||||||
|
# Remove the licenses
|
||||||
|
foreach ($username in $userList) {
|
||||||
|
try {
|
||||||
|
$user = Get-AzureADUser -ObjectId $username -ErrorAction Stop
|
||||||
|
if ($user.AccountEnabled -eq $true) {
|
||||||
|
Set-AzureADUserLicense -ObjectId $user.UserPrincipalName -AssignedLicenses $licenses
|
||||||
|
Write-Output -InputObject ('Licence(s) removed from user account ' + $user.UserPrincipalName + '.')
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Write-Output -InputObject ('User account ' + $user.UserPrincipalName + ' is disabled.')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Output -InputObject ('There was a problem updating licences for user account ' + $user.UserPrincipalName + '.')
|
||||||
|
Write-Output -InputObject $Error[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Disconnect from AzureAD
|
||||||
|
Disconnect-AzureAD
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
# Bulk replace licences to a list of users.
|
||||||
|
#
|
||||||
|
# List of available SKUs can be obtained with (Get-AzureADSubscribedSku).SkuPartNumber
|
||||||
|
#
|
||||||
|
|
||||||
|
# Where is the list of user UPN's?
|
||||||
|
$userListPath = 'C:\Temp\UserList.txt'
|
||||||
|
|
||||||
|
# What licences are we adding?
|
||||||
|
$licencesToAdd = @('EMSPREMIUM')
|
||||||
|
|
||||||
|
# What licences are we removing?
|
||||||
|
$licencesToRemove = @('EMS','AAD_PREMIUM_P2')
|
||||||
|
|
||||||
|
# Import AzureAD module and connect
|
||||||
|
Import-Module AzureAD
|
||||||
|
Connect-AzureAD
|
||||||
|
|
||||||
|
# Import list of users from file
|
||||||
|
$userList = Get-Content -Path $userListPath | Sort-Object
|
||||||
|
|
||||||
|
$userList = @('OAndrewsAdmin@sis.tv')
|
||||||
|
|
||||||
|
# Get all available licence skus
|
||||||
|
$newSkuIDs = (Get-AzureADSubscribedSku | Where-Object {$_.SkuPartNumber -in $licencesToAdd}).SkuId
|
||||||
|
|
||||||
|
# Create a licenses object
|
||||||
|
$licenses = New-Object -TypeName Microsoft.Open.AzureAD.Model.AssignedLicenses
|
||||||
|
|
||||||
|
# Add the licences to add to the licences object
|
||||||
|
foreach ($newSkuID in $newSkuIDs) {
|
||||||
|
$newLicense = New-Object -TypeName Microsoft.Open.AzureAD.Model.AssignedLicense
|
||||||
|
$newLicense.SkuId = $newSkuID
|
||||||
|
$licenses.AddLicenses += $newLicense
|
||||||
|
}
|
||||||
|
|
||||||
|
# Add the licences we want to remove to the $licenses object
|
||||||
|
$licenses.RemoveLicenses = (Get-AzureADSubscribedSku | Where-Object {$_.SkuPartNumber -in $licencesToRemove}).SkuID
|
||||||
|
|
||||||
|
# Replace the licenses
|
||||||
|
foreach ($username in $userList) {
|
||||||
|
try {
|
||||||
|
$user = Get-AzureADUser -ObjectId $username -ErrorAction Stop
|
||||||
|
if ($user.AccountEnabled -eq $true) {
|
||||||
|
Set-AzureADUserLicense -ObjectId $user.UserPrincipalName -AssignedLicenses $licenses
|
||||||
|
Write-Output -InputObject ('Licences updated for user account ' + $user.UserPrincipalName + '.')
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Write-Output -InputObject ('User account ' + $user.UserPrincipalName + ' is disabled.')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Output -InputObject ('There was a problem updating licences for user account ' + $user.UserPrincipalName + '.')
|
||||||
|
Write-Output -InputObject $Error[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Disconnect from AzureAD
|
||||||
|
Disconnect-AzureAD
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
# Update Online User UPN - when AD sync doesn't do it.
|
||||||
|
|
||||||
|
# Import the Azure AD module and connect
|
||||||
|
Import-Module AzureAD
|
||||||
|
Connect-AzureAD
|
||||||
|
|
||||||
|
# Whose UPN are we changing?
|
||||||
|
$oldUPN = Read-Host -Prompt 'Enter user''s old UPN in the format username@domain'
|
||||||
|
|
||||||
|
# What are we changing it to?
|
||||||
|
$newUPN = Read-Host -Prompt 'Enter user''s new UPN in the format username@domain'
|
||||||
|
|
||||||
|
# Update the UPN in AzureAD
|
||||||
|
Set-AzureADUser -ObjectId $oldUPN -UserPrincipalName $newUPN
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
# Import the Azure AD module and connect
|
||||||
|
Import-Module AzureAD
|
||||||
|
Connect-AzureAD
|
||||||
|
|
||||||
|
# Whose ID are we getting?
|
||||||
|
$userUPN = Read-Host -Prompt 'Enter user''s UPN in the format username@domain'
|
||||||
|
|
||||||
|
# Convert 365 ImmutableID to AD GUID
|
||||||
|
$immutableID = (Get-AzureADUser -ObjectId $userUPN).ImmutableID
|
||||||
|
[GUID][System.Convert]::FromBase64String($immutableID)
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
# Retrieve licences for all users and export to CSV.
|
||||||
|
#
|
||||||
|
|
||||||
|
# Import AzureAD module and connect
|
||||||
|
Import-Module AzureAD
|
||||||
|
Connect-AzureAD
|
||||||
|
|
||||||
|
# Get all available licence details
|
||||||
|
$allSkus = Get-AzureADSubscribedSku
|
||||||
|
|
||||||
|
# Import list of users from file
|
||||||
|
$users = Get-AzureADUser -All $true
|
||||||
|
|
||||||
|
# Get Licences for each user and find out what they are from the list
|
||||||
|
$allUserLicences = @()
|
||||||
|
foreach ($user in $users) {
|
||||||
|
$assignedLicences = @()
|
||||||
|
foreach ($license in $user.AssignedLicenses.SkuID) {
|
||||||
|
$assignedLicences += ($allSkus | Where-Object { $_.SkuID -eq $license }).SkuPartNumber
|
||||||
|
}
|
||||||
|
$userLicences = [PSCustomObject]@{
|
||||||
|
'UserName' = $user.UserPrincipalName
|
||||||
|
'AssignedLicences' = (($assignedLicences | Sort-Object) -join ';')
|
||||||
|
}
|
||||||
|
$allUserLicences += $userLicences
|
||||||
|
}
|
||||||
|
|
||||||
|
# Export list to CSV file
|
||||||
|
$allUserLicences | Export-Csv -Path 'C:\Temp\AllUserLicences.csv' -NoTypeInformation
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
# Retrieve licences for a list of users and export them to a CSV file.
|
||||||
|
#
|
||||||
|
|
||||||
|
# Where is the list of user UPN's?
|
||||||
|
$userListPath = 'C:\Temp\UserList.txt'
|
||||||
|
|
||||||
|
# Import AzureAD module and connect
|
||||||
|
Import-Module AzureAD
|
||||||
|
Connect-AzureAD
|
||||||
|
|
||||||
|
# Get all available licence details
|
||||||
|
$allSkus = Get-AzureADSubscribedSku
|
||||||
|
|
||||||
|
# Import list of users from file
|
||||||
|
$users = Get-Content -Path $userListPath | Sort-Object
|
||||||
|
|
||||||
|
# Get Licences for user and find out what they are from the list
|
||||||
|
$allUserLicences = @()
|
||||||
|
foreach ($user in $users) {
|
||||||
|
$assignedLicences = @()
|
||||||
|
try {
|
||||||
|
$userDetails = Get-AzureADUser -ObjectId $user -ErrorAction Stop
|
||||||
|
foreach ($license in $userDetails.AssignedLicenses.SkuID) {
|
||||||
|
$assignedLicences += ($allSkus | Where-Object { $_.SkuID -eq $license }).SkuPartNumber
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
$assignedLicences = 'User not found.'
|
||||||
|
}
|
||||||
|
$userLicences = [PSCustomObject]@{
|
||||||
|
'UserName' = $user
|
||||||
|
'AssignedLicences' = (($assignedLicences | Sort-Object) -join ';')
|
||||||
|
}
|
||||||
|
$allUserLicences += $userLicences
|
||||||
|
}
|
||||||
|
|
||||||
|
# Export list to CSV file
|
||||||
|
$allUserLicences | Export-Csv -Path 'C:\Temp\UserLicences.csv' -NoTypeInformation
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
# Some example scripts for finding users with specific plans assigned
|
||||||
|
#
|
||||||
|
# A full list of subscription and plan ID's is available from:
|
||||||
|
# https://docs.microsoft.com/en-us/azure/active-directory/users-groups-roles/licensing-service-plan-reference
|
||||||
|
#
|
||||||
|
# Here I'm using the following plan IDs.
|
||||||
|
#
|
||||||
|
# OFFICESUBSCRIPTION (43de0ff5-c92c-492b-9116-175376d08c38)
|
||||||
|
# PROJECT_CLIENT_SUBSCRIPTION (fafd7243-e5c1-4a3a-9e40-495efcb1d3c3)
|
||||||
|
# VISIO_CLIENT_SUBSCRIPTION (663a804f-1c30-4ff0-9915-9db84f0d1cea)
|
||||||
|
#
|
||||||
|
|
||||||
|
# Plan ID's to check for
|
||||||
|
$planID1 = '43de0ff5-c92c-492b-9116-175376d08c38'
|
||||||
|
$planID2 = 'fafd7243-e5c1-4a3a-9e40-495efcb1d3c3'
|
||||||
|
$planID3 = '663a804f-1c30-4ff0-9915-9db84f0d1cea'
|
||||||
|
|
||||||
|
# Import the AzureAD module and connect
|
||||||
|
Import-Module AzureAD
|
||||||
|
Connect-AzureAD
|
||||||
|
|
||||||
|
# Get all users from AzureAD
|
||||||
|
$allUsers = Get-AzureADUser -All $true
|
||||||
|
|
||||||
|
# Create variables to put the users in to
|
||||||
|
$planID1Users = @()
|
||||||
|
$planID2Users = @()
|
||||||
|
$planID3Users = @()
|
||||||
|
|
||||||
|
# Check through the users and check what plans are assigned and enabled
|
||||||
|
foreach ($user in $allUsers) {
|
||||||
|
foreach ($assignedPlan in $user.AssignedPlans) {
|
||||||
|
# Find all users of plan 1 and add to the related variable
|
||||||
|
if ($assignedPlan.ServicePlanId -eq $planID1 -and $assignedPlan.CapabilityStatus -eq 'Enabled') {
|
||||||
|
$planID1Users += $user
|
||||||
|
}
|
||||||
|
# Find all users of plan 2 and add to the related variable
|
||||||
|
if ($assignedPlan.ServicePlanId -eq $planID2 -and $assignedPlan.CapabilityStatus -eq 'Enabled') {
|
||||||
|
$planID2Users += $user
|
||||||
|
}
|
||||||
|
# Find all users of plan 3 and add to the related variable
|
||||||
|
if ($assignedPlan.ServicePlanId -eq $planID3 -and $assignedPlan.CapabilityStatus -eq 'Enabled') {
|
||||||
|
$planID3Users += $user
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create variables to put the users in to
|
||||||
|
$plan1OnlyUsers = @()
|
||||||
|
$planID1and2Users = @()
|
||||||
|
$planID1and3Users = @()
|
||||||
|
$planID12and3Users = @()
|
||||||
|
|
||||||
|
# Using plan 1 as the master list, check which other licences are assigned
|
||||||
|
foreach ($planID1User in $planID1Users) {
|
||||||
|
# Find users who are only in the plan 1 list
|
||||||
|
if ($planID1User -notin $planID2Users -and $planID1User -notin $planID3Users) {
|
||||||
|
$plan1OnlyUsers += $planID1User
|
||||||
|
}
|
||||||
|
# Find users who are the plan 1 and plan 2 list
|
||||||
|
if ($planID1User -in $planID2Users) {
|
||||||
|
$planID1and2Users += $planID1User
|
||||||
|
}
|
||||||
|
# Find users who are in the plan 1 and plan 3 list
|
||||||
|
if ($planID1User -in $planID3Users) {
|
||||||
|
$planID1and3Users += $planID1User
|
||||||
|
}
|
||||||
|
# Find users who are in all three lists
|
||||||
|
if ($planID1User -in $planID2Users -and $planID1User -in $planID3Users) {
|
||||||
|
$planID12and3Users += $planID1User
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# How many people in each list?
|
||||||
|
$plan1OnlyUsers.Count
|
||||||
|
$planID1and2Users.Count
|
||||||
|
$planID1and3Users.Count
|
||||||
|
$planID12and3Users.Count
|
||||||
|
|
||||||
|
# Who are those people?
|
||||||
|
$plan1OnlyUsers
|
||||||
|
$planID1and2Users
|
||||||
|
$planID1and3Users
|
||||||
|
$planID12and3Users
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
# Script to invite a list of guest users to AzureAD and add them to a group
|
||||||
|
#
|
||||||
|
# Useful for bulk adding guests to a SharePoint or Teams group
|
||||||
|
#
|
||||||
|
# Uses a CSV file containing columns for FirstName,LastName,DisplayName,ExternalEmailAddress
|
||||||
|
#
|
||||||
|
|
||||||
|
# Where is our list of people?
|
||||||
|
$contactsFile = 'C:\Temp\ExternalContacts.csv'
|
||||||
|
|
||||||
|
# What group are we added them to?
|
||||||
|
$groupName = 'Test Group'
|
||||||
|
|
||||||
|
# Import the AzureAD module and connect
|
||||||
|
Import-Module AzureAD
|
||||||
|
Connect-AzureAD
|
||||||
|
|
||||||
|
# Import the list of people
|
||||||
|
$contactsList = Import-Csv -Path $contactsFile
|
||||||
|
|
||||||
|
# Find the group ID for our group
|
||||||
|
$groupID = (Get-AzureADGroup -All $true | Where-Object {$_.DisplayName -eq $groupName}).ObjectId
|
||||||
|
|
||||||
|
# Run through the list sending the guest invite and adding them to the group, unless their email address is already used somewhere
|
||||||
|
foreach ($externalUser in $contactsList) {
|
||||||
|
$getExisting = Get-AzureADUser -SearchString $externalUser.ExternalEmailAddress
|
||||||
|
|
||||||
|
if ($getExisting.Count -eq 0) {
|
||||||
|
$newUser = New-AzureADMSInvitation -InvitedUserDisplayName $externalUser.DisplayName -InvitedUserEmailAddress $externalUser.ExternalEmailAddress -SendInvitationMessage $true -InviteRedirectUrl "https://myapps.microsoft.com"
|
||||||
|
Add-AzureADGroupMember -ObjectId $groupID -RefObjectId $newUser.InvitedUser.Id
|
||||||
|
Write-Output -InputObject ('Guest ' + $externalUser.ExternalEmailAddress + ' created and added to group.')
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Write-Output -InputObject ('Object already exists with email address ' + $externalUser.ExternalEmailAddress + '.')
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
# Remove all licences from a list of users
|
||||||
|
#
|
||||||
|
# Removing licences with this module doesn't work in the way that Microsoft's documentation states.
|
||||||
|
# I'm assuming this is a bug, so the code below works at time of creation but may stop working in the future.
|
||||||
|
#
|
||||||
|
|
||||||
|
# Where is the list of user UPN's?
|
||||||
|
$userListPath = 'C:\Temp\UserList.txt'
|
||||||
|
|
||||||
|
# Import AzureAD module and connect
|
||||||
|
Import-Module AzureAD
|
||||||
|
Connect-AzureAD
|
||||||
|
|
||||||
|
# Import list of users from file
|
||||||
|
$userList = Get-Content -Path $userListPath | Sort-Object
|
||||||
|
|
||||||
|
# Remove all the licences
|
||||||
|
foreach ($username in $userList) {
|
||||||
|
try {
|
||||||
|
$user = Get-AzureADUser -ObjectId $username -ErrorAction Stop
|
||||||
|
if ($user.AccountEnabled -eq $true) {
|
||||||
|
if(($user.AssignedLicenses.SkuId).Count -ge 1) {
|
||||||
|
$licenses = New-Object -TypeName Microsoft.Open.AzureAD.Model.AssignedLicenses
|
||||||
|
$licenses.RemoveLicenses = (Get-AzureADSubscribedSku | Where-Object {$_.SkuId -in $user.AssignedLicenses.SkuId}).SkuId
|
||||||
|
Set-AzureADUserLicense -ObjectId $user.UserPrincipalName -AssignedLicenses $licenses
|
||||||
|
Write-Output -InputObject ('All licences removed from user account ' + $username + '.')
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Write-Output -InputObject ('User account ' + $username + ' has no licences assigned.')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Write-Output -InputObject ('User account ' + $username + ' is disabled.')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Output -InputObject ('User account ' + $username + ' does not exist.')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Disconnect from AzureAD
|
||||||
|
Disconnect-AzureAD
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
# Script to get details for a list of mailboxes and save that out to a CSV file
|
||||||
|
#
|
||||||
|
|
||||||
|
# File containing the list of mailboxes
|
||||||
|
$inputFile = 'C:\Temp\MailboxList.txt'
|
||||||
|
|
||||||
|
# File to save the results to
|
||||||
|
$outputFile = 'C:\Temp\SharedMailboxes.csv'
|
||||||
|
|
||||||
|
# Establish a session to Exchange Online
|
||||||
|
$credentials = Get-Credential -Message 'Enter your Exchange Online administrator credentials'
|
||||||
|
$connectionParams = @{
|
||||||
|
'ConfigurationName' = 'Microsoft.Exchange';
|
||||||
|
'ConnectionUri' = 'https://outlook.office365.com/powershell-liveid/';
|
||||||
|
'Credential' = $credentials;
|
||||||
|
'Authentication' = 'Basic';
|
||||||
|
'AllowRedirection' = $true
|
||||||
|
}
|
||||||
|
$exchangeSession = New-PSSession @connectionParams
|
||||||
|
Import-PSSession -Session $exchangeSession
|
||||||
|
|
||||||
|
# Check output folder exists and create it if it doesn't
|
||||||
|
$outputPath = (Split-Path -Path $outputFile)
|
||||||
|
if (!(Test-Path -Path $outputPath)) {New-Item -Path $outputFolder -ItemType Directory}
|
||||||
|
|
||||||
|
# If output file already exists, delete it.
|
||||||
|
if (Test-Path -Path $outputFile) {Remove-Item -Path $outputFile}
|
||||||
|
|
||||||
|
# Get the list of mailboxes from the file
|
||||||
|
$mailboxes = Get-Content -Path $inputFile
|
||||||
|
|
||||||
|
# Initialise the results table
|
||||||
|
$mailboxesTable = @()
|
||||||
|
|
||||||
|
# Iterate through the mailboxes from the file and show a progress bar as we go.
|
||||||
|
foreach ($mailbox in $mailboxes) {
|
||||||
|
Write-Progress -Activity 'Checking..' -status $mailbox -percentComplete ($mailboxes.IndexOf($mailbox) / $mailboxes.Count * 100)
|
||||||
|
|
||||||
|
# Get the statistics for the mailbox.
|
||||||
|
$mailboxStats = ''
|
||||||
|
$mailboxStats = Get-MailboxStatistics -Identity $mailbox -ErrorAction SilentlyContinue -
|
||||||
|
|
||||||
|
# If mailboxStats is blank then mailbox doesn't exist, so that entry can be skipped. For all others get the mailbox permissions and build the output table.
|
||||||
|
if ($mailboxStats -ne $null) {
|
||||||
|
# Get permit permissions for users where the username has an @ in it, this filters out all the system permissions.
|
||||||
|
$usersWithAccess = (Get-MailboxPermission -Identity $mailbox | Where-Object -Property {($_.User -like '*@*') -and ($_.Deny -ne 'False')}).User -join '; '
|
||||||
|
|
||||||
|
$tableRow = New-Object System.Object
|
||||||
|
$tableRow | Add-Member -MemberType NoteProperty -Name 'MailboxUPN' -Value $mailbox
|
||||||
|
$tableRow | Add-Member -MemberType NoteProperty -Name 'MailboxType' -Value $mailboxStats.MailboxTypeDetail
|
||||||
|
$tableRow | Add-Member -MemberType NoteProperty -Name 'ItemCount' -Value $mailboxStats.ItemCount
|
||||||
|
$tableRow | Add-Member -MemberType NoteProperty -Name 'TotalItemSize' -Value $mailboxStats.TotalItemSize
|
||||||
|
$tableRow | Add-Member -MemberType NoteProperty -Name 'LastLogonTime' -Value $mailboxStats.LastLogonTime
|
||||||
|
$tableRow | Add-Member -MemberType NoteProperty -Name 'UsersWithAccess' -Value $usersWithAccess
|
||||||
|
$mailboxesTable += $tableRow
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Output the final table of results to a file.
|
||||||
|
$mailboxesTable | Export-Csv -Path $outputFile -NoTypeInformation
|
||||||
|
|
||||||
|
# End the Exchange Online session
|
||||||
|
Remove-PSSession -Session $exchangeSession
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
# Find mailboxes where UPN domain doesn't match email domain
|
||||||
|
#
|
||||||
|
|
||||||
|
# Establish a session to Exchange Online
|
||||||
|
$credentials = Get-Credential -Message 'Enter your Exchange Online administrator credentials'
|
||||||
|
$connectionParams = @{
|
||||||
|
'ConfigurationName' = 'Microsoft.Exchange';
|
||||||
|
'ConnectionUri' = 'https://outlook.office365.com/powershell-liveid/';
|
||||||
|
'Credential' = $credentials;
|
||||||
|
'Authentication' = 'Basic';
|
||||||
|
'AllowRedirection' = $true
|
||||||
|
}
|
||||||
|
$exchangeSession = New-PSSession @connectionParams
|
||||||
|
Import-PSSession -Session $exchangeSession
|
||||||
|
|
||||||
|
# Get list of mailboxes with a different mail domain to UPN domain
|
||||||
|
Get-Mailbox -ResultSize Unlimited | Where-Object {$_.UserPrincipalName.Split('@')[1] -ne $_.PrimarySmtpAddress.Split('@')[1]} | Format-Table Name,UserPrincipalName,PrimarySmtpAddress,RecipientTypeDetails
|
||||||
|
|
||||||
|
# Disconnect from Exchange Online
|
||||||
|
Remove-PSSession $exchangeSession
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
# Script to get all client forwarding rules, smtp forwarding rules and delegates on mailboxes
|
||||||
|
#
|
||||||
|
# This builds on top of a script from https://github.com/OfficeDev/O365-InvestigationTooling
|
||||||
|
#
|
||||||
|
|
||||||
|
# Establish a session to Exchange Online
|
||||||
|
$credentials = Get-Credential -Message 'Enter your Exchange Online administrator credentials'
|
||||||
|
$connectionParams = @{
|
||||||
|
'ConfigurationName' = 'Microsoft.Exchange';
|
||||||
|
'ConnectionUri' = 'https://outlook.office365.com/powershell-liveid/';
|
||||||
|
'Credential' = $credentials;
|
||||||
|
'Authentication' = 'Basic';
|
||||||
|
'AllowRedirection' = $true
|
||||||
|
}
|
||||||
|
$exchangeSession = New-PSSession @connectionParams
|
||||||
|
Import-PSSession -Session $exchangeSession
|
||||||
|
|
||||||
|
$allUsers = @()
|
||||||
|
$allUsers = Get-Mailbox -ResultSize Unlimited | Select-Object DisplayName,UserPrincipalName,ForwardingAddress,ForwardingSMTPAddress,DeliverToMailboxandForward
|
||||||
|
|
||||||
|
$userInboxRules = @()
|
||||||
|
$userDelegates = @()
|
||||||
|
|
||||||
|
foreach ($user in $allUsers) {
|
||||||
|
Write-Progress -Activity "Checking inbox rules for..." -status $user.UserPrincipalName -percentComplete ($allUsers.IndexOf($user) / $allUsers.Count * 100)
|
||||||
|
$userInboxRules += Get-InboxRule -Mailbox $user.UserPrincipalName | `
|
||||||
|
Select-Object MailboxOwnerId,Name,Description,Enabled,Priority,ForwardTo,ForwardAsAttachmentTo,RedirectTo,DeleteMessage | `
|
||||||
|
Where-Object {($_.ForwardTo -ne $null) -or ($_.ForwardAsAttachmentTo -ne $null) -or ($_.RedirectsTo -ne $null)}
|
||||||
|
$userDelegates += Get-MailboxPermission -Identity $user.UserPrincipalName | Where-Object {($_.IsInherited -ne "True") -and ($_.User -notlike "*SELF*")}
|
||||||
|
}
|
||||||
|
|
||||||
|
$smtpForwarding = $allUsers | Select-Object DisplayName,ForwardingAddress,ForwardingSMTPAddress,DeliverToMailboxandForward | Where-Object {$_.ForwardingSMTPAddress -ne $null}
|
||||||
|
|
||||||
|
$userInboxRules | Export-Csv MailForwardingRulesToExternalDomains.csv -NoTypeInformation
|
||||||
|
$smtpForwarding | Export-Csv Mailboxsmtpforwarding.csv -NoTypeInformation
|
||||||
|
$userDelegates | Export-Csv MailboxDelegatePermissions.csv -NoTypeInformation
|
||||||
|
|
||||||
|
Remove-PSSession -Session $exchangeSession
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
# Script to grant access rights on mailboxes
|
||||||
|
#
|
||||||
|
|
||||||
|
# Mailboxes to grant rights on
|
||||||
|
$grantRightsOnMailboxes = @('','')
|
||||||
|
|
||||||
|
# Users to grant rights to
|
||||||
|
$grantRightsToUsers = @('','')
|
||||||
|
|
||||||
|
# Rights to grant
|
||||||
|
$accessRights = 'Editor'
|
||||||
|
|
||||||
|
# Establish a session to Exchange Online
|
||||||
|
$credentials = Get-Credential -Message 'Enter your Exchange Online administrator credentials'
|
||||||
|
$connectionParams = @{
|
||||||
|
'ConfigurationName' = 'Microsoft.Exchange';
|
||||||
|
'ConnectionUri' = 'https://outlook.office365.com/powershell-liveid/';
|
||||||
|
'Credential' = $credentials;
|
||||||
|
'Authentication' = 'Basic';
|
||||||
|
'AllowRedirection' = $true
|
||||||
|
}
|
||||||
|
$exchangeSession = New-PSSession @connectionParams
|
||||||
|
Import-PSSession -Session $exchangeSession
|
||||||
|
|
||||||
|
# Apply the permissions
|
||||||
|
foreach ($grantRightsToUser in $grantRightsToUsers ) {
|
||||||
|
foreach ($grantRightsOnMailbox in $grantRightsOnMailboxes) {
|
||||||
|
$calendarIdentity = $grantRightsOnMailbox + ':\Calendar'
|
||||||
|
$existingPermissions = Get-MailboxFolderPermission -Identity $calendarIdentity -User $grantRightsToUser -ErrorAction SilentlyContinue
|
||||||
|
if (!($existingPermissions)) {
|
||||||
|
Add-MailboxFolderPermission -Identity $calendarIdentity -User $grantRightsToUser -AccessRights $accessRights
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Set-MailboxFolderPermission -Identity $calendarIdentity -User $grantRightsToUser -AccessRights $accessRights
|
||||||
|
}
|
||||||
|
Set-Mailbox -Identity $upn –GrantSendOnBehalfTo @{add=$editor}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# End the PowerShell session
|
||||||
|
Remove-PSSession -Session $exchangeSession
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
# Enable mailbox auditing and disable PowerShell remoting on individual mailboxes
|
||||||
|
#
|
||||||
|
# Working on this as a one stop for hardening Exchange Online accounts
|
||||||
|
#
|
||||||
|
|
||||||
|
# UPN of New User
|
||||||
|
$newUPN = ''
|
||||||
|
|
||||||
|
# Is the user an administrator?
|
||||||
|
$isAnAdmin = $false
|
||||||
|
|
||||||
|
# Establish a session to Exchange Online
|
||||||
|
$credentials = Get-Credential -Message 'Enter your Exchange Online administrator credentials'
|
||||||
|
$connectionParams = @{
|
||||||
|
'ConfigurationName' = 'Microsoft.Exchange';
|
||||||
|
'ConnectionUri' = 'https://outlook.office365.com/powershell-liveid/';
|
||||||
|
'Credential' = $credentials;
|
||||||
|
'Authentication' = 'Basic';
|
||||||
|
'AllowRedirection' = $true
|
||||||
|
}
|
||||||
|
$exchangeSession = New-PSSession @connectionParams
|
||||||
|
Import-PSSession -Session $exchangeSession
|
||||||
|
|
||||||
|
# Set Auditing parameters
|
||||||
|
$params = @{
|
||||||
|
'AuditEnabled' = $true
|
||||||
|
'AuditLogAgeLimit' = '180'
|
||||||
|
'AuditAdmin' = @('Update','MoveToDeletedItems','SoftDelete','HardDelete','SendAs','SendOnBehalf','Create','UpdateFolderPermission')
|
||||||
|
'AuditDelegate' = @('Update','SoftDelete','HardDelete','SendAs','Create','UpdateFolderPermissions','MoveToDeletedItems','SendOnBehalf')
|
||||||
|
'AuditOwner' = @('UpdateFolderPermission','MailboxLogin','Create','SoftDelete','HardDelete','Update','MoveToDeletedItems')
|
||||||
|
}
|
||||||
|
|
||||||
|
# Enable Auditing
|
||||||
|
Get-Mailbox -Identity $newUPN | Set-Mailbox @params
|
||||||
|
|
||||||
|
# Disable PowerShell Remoting for non-Admin staff
|
||||||
|
if ($isAnAdmin) {
|
||||||
|
Set-User -Identity $newUPN -RemotePowerShellEnabled $true
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Set-User -Identity $newUPN -RemotePowerShellEnabled $false
|
||||||
|
}
|
||||||
|
|
||||||
|
# Disconnect from Exchange Online
|
||||||
|
Remove-PSSession $exchangeSession
|
||||||
@@ -0,0 +1,111 @@
|
|||||||
|
# Script to bulk remove email proxy addresses from Exchange users in a synced environment using an on-prem Exchange management server
|
||||||
|
#
|
||||||
|
|
||||||
|
# What is the FQDN of the on-prem Exchange server?
|
||||||
|
$exchangeServerFQDN = ''
|
||||||
|
|
||||||
|
# Which domains are we removing?
|
||||||
|
$domainsToRemove = @('','')
|
||||||
|
|
||||||
|
# Create Exchange connection Uri from FQDN
|
||||||
|
$exchangeConnectionUri = 'http://' + $exchangeServerFQDN +'/PowerShell/'
|
||||||
|
|
||||||
|
# Establish a session to Exchange
|
||||||
|
$userCredential = Get-Credential
|
||||||
|
$session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri $exchangeConnectionUri -Authentication Kerberos -Credential $userCredential
|
||||||
|
Import-PSSession $session -DisableNameChecking
|
||||||
|
|
||||||
|
### Check if address policies are using this domain ###
|
||||||
|
|
||||||
|
# Initialise a hastable to store our results in
|
||||||
|
$addressPolicies = @()
|
||||||
|
|
||||||
|
# Get all address policies from Exchange
|
||||||
|
$allAddressPolicies = Get-EmailAddressPolicy
|
||||||
|
|
||||||
|
# Run through the domains checking if the domain to be removed is in the address policies
|
||||||
|
foreach ($domainToRemove in $domainsToRemove) {
|
||||||
|
$emailAddressTemplate = 'SMTP:@' + $domainToRemove
|
||||||
|
$addressPolicies += $allAddressPolicies | Where-Object {$_.EnabledEmailAddressTemplates -contains $emailAddressTemplate}
|
||||||
|
}
|
||||||
|
|
||||||
|
# If domain found in address policies then list them and ask if we want to contiue removing the proxy domain
|
||||||
|
if ($addressPolicies.Count -gt 0) {
|
||||||
|
Write-Output -InputObject ('The following address policies are using the domain to be removed:')
|
||||||
|
$addressPolicies | Format-Table
|
||||||
|
$continue = ''
|
||||||
|
while ($continue -notmatch '[YyNn]') {
|
||||||
|
$continue = Read-Host -Prompt 'Do you want to continue running the script? (Y/N)'
|
||||||
|
}
|
||||||
|
if ($continue -match '[Nn]') {
|
||||||
|
Write-Output -InputObject ('Terminating script.')
|
||||||
|
break
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Write-Output -InputObject ('Continuing with script.')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
###
|
||||||
|
|
||||||
|
### Remove the domain from mailboxes ###
|
||||||
|
|
||||||
|
# Get All Mailboxes
|
||||||
|
$allMailboxes = Get-RemoteMailbox -ResultSize Unlimited | Sort-Object -Property alias
|
||||||
|
|
||||||
|
# Remove alias from each mailbox
|
||||||
|
foreach ($mailbox in $allMailboxes) {
|
||||||
|
$redundantAddresses = @()
|
||||||
|
foreach ($domainToRemove in $domainsToRemove) {
|
||||||
|
$redundantAddresses += (($mailbox.EmailAddresses -split ',' | Where-Object {$_ -match $domainToRemove}) -replace 'smtp:','')
|
||||||
|
}
|
||||||
|
if ($redundantAddresses.Count -gt 0) {
|
||||||
|
Write-Output -InputObject ('Removing addresses ' + $redundantAddresses + ' from maibox ' + $mailbox.Name)
|
||||||
|
Set-RemoteMailbox -Identity $mailbox.Identity -EmailAddresses @{remove=$redundantAddresses} -Confirm:$false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
###
|
||||||
|
|
||||||
|
### Remove the domain from contacts ###
|
||||||
|
|
||||||
|
# Get all the contacts
|
||||||
|
$allContacts = Get-MailContact -ResultSize Unlimited | Sort-Object -Property alias
|
||||||
|
|
||||||
|
# Remove alias from each contact
|
||||||
|
foreach ($contact in $allContacts) {
|
||||||
|
$redundantAddresses = @()
|
||||||
|
foreach ($domainToRemove in $domainsToRemove) {
|
||||||
|
$redundantAddresses += (($contact.EmailAddresses -split ',' | Where-Object {$_ -like ('*' + $domainToRemove + '*')}) -replace 'smtp:','')
|
||||||
|
}
|
||||||
|
if ($redundantAddresses.Count -gt 0) {
|
||||||
|
Write-Output -InputObject ('Removing addresses ' + $redundantAddresses + ' from contact ' + $contact.Name)
|
||||||
|
Set-MailContact -Identity $contact.Identity -EmailAddresses @{remove=$redundantAddresses} -Confirm:$false -ForceUpgrade:$true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
###
|
||||||
|
|
||||||
|
### Remove the domain from groups ###
|
||||||
|
|
||||||
|
# Get all the groups (this includes email enabled security groups)
|
||||||
|
$allGroups = Get-DistributionGroup -ResultSize Unlimited | Sort-Object -Property alias
|
||||||
|
|
||||||
|
# Remove alias from each group
|
||||||
|
foreach ($group in $allGroups) {
|
||||||
|
$redundantAddresses = @()
|
||||||
|
foreach ($domainToRemove in $domainsToRemove) {
|
||||||
|
$redundantAddresses += (($group.EmailAddresses -split ',' | Where-Object {$_ -like ('*' + $domainToRemove + '*')}) -replace 'smtp:','')
|
||||||
|
}
|
||||||
|
if ($redundantAddresses.Count -gt 0) {
|
||||||
|
Write-Output -InputObject ('Removing addresses ' + $redundantAddresses + ' from group ' + $group.Name)
|
||||||
|
Set-DistributionGroup -Identity $group.Identity -EmailAddresses @{remove=$redundantAddresses} -Confirm:$false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
###
|
||||||
|
|
||||||
|
### Public folders don't feature here because in this scenario they're cloud only ###
|
||||||
|
|
||||||
|
# End the Exchange Session
|
||||||
|
Remove-PSSession -Session $session
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
# Find mailboxes for synced accounts, where the mailbox exists in Exchange Online but doesn't exist on-prem as a remote mailbox.
|
||||||
|
#
|
||||||
|
# Like the other stuff in this folder, it's a bit of a niche scenario.
|
||||||
|
# We're looking for mailboxes which were created in on-prem AD and then licenced in the cloud without a remote mailbox being created on the on-prem Exchange server, so that we can remote mail enable them correctly.
|
||||||
|
#
|
||||||
|
# This is a bit complicated as it requires connected to two Exchange environments at once, so uses command prefixes for one of them.
|
||||||
|
#
|
||||||
|
|
||||||
|
# What is the FQDN of the on-prem Exchange server?
|
||||||
|
$exchangeServerFQDN = ''
|
||||||
|
|
||||||
|
# What is your remote routing address? E.g.: @domain.mail.onmicrosoft.com
|
||||||
|
$remoteRoutingSuffix = '@domain.mail.onmicrosoft.com'
|
||||||
|
|
||||||
|
# Find and load the new ExO "module"
|
||||||
|
$exoModulePath = (Get-ChildItem -Path $env:userprofile -Filter CreateExoPSSession.ps1 -Recurse -Force -ErrorAction SilentlyContinue).DirectoryName[-1]
|
||||||
|
. "$exoModulePath\CreateExoPSSession.ps1"
|
||||||
|
|
||||||
|
# Establish a session to Exchange Online
|
||||||
|
Connect-EXOPSSession
|
||||||
|
|
||||||
|
# Create Exchange on-prem connection Uri from FQDN
|
||||||
|
$exchangeConnectionUri = 'http://' + $exchangeServerFQDN +'/PowerShell/'
|
||||||
|
|
||||||
|
# Establish a session to Exchange on-prem and add the OnPrem prefix to all commands
|
||||||
|
$userCredential = Get-Credential
|
||||||
|
$session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri $exchangeConnectionUri -Authentication Kerberos -Credential $userCredential
|
||||||
|
Import-PSSession $session -DisableNameChecking -Prefix OnPrem
|
||||||
|
|
||||||
|
# Get list of mailboxes from Exchange online for synced users
|
||||||
|
$allOnlineMailboxes = Get-Mailbox -ResultSize Unlimited | Where-Object {$_.IsDirSynced -eq $true -and $_.Name -notlike 'DiscoverySearchMailbox*'}
|
||||||
|
|
||||||
|
# Get list of remote mailboxes from on-prem server
|
||||||
|
$allOnPremMailboxes = Get-OnPremRemoteMailbox -ResultSize Unlimited
|
||||||
|
|
||||||
|
# Compare the two lists and add those missing from the on-prem list to the $syncedMailboxMismatch hashtable
|
||||||
|
$syncedMailboxMismatch = $allOnlineMailboxes | Where-Object {$allOnPremMailboxes.PrimarySmtpAddress -notcontains $_.PrimarySmtpAddress}
|
||||||
|
|
||||||
|
# Find mailboxes where alias and username match
|
||||||
|
$matchedUPNAliases = $syncedMailboxMismatch | Where-Object {$_.Alias -eq $_.UserPrincipalName.Split('@')[0]}
|
||||||
|
|
||||||
|
# Find mailboxes where alias and username don't match
|
||||||
|
$mismatchedUPNAliases = $syncedMailboxMismatch | Where-Object {$_.Alias -ne $_.UserPrincipalName.Split('@')[0]}
|
||||||
|
|
||||||
|
# Fix the mailboxes where username and alias matched.
|
||||||
|
foreach ($mailboxToRemoteEnable in $matchedUPNAliases) {
|
||||||
|
$mailboxUsername = $mailboxToRemoteEnable.UserPrincipalName.Split('@')[0]
|
||||||
|
$mailboxToFix = $mailboxToRemoteEnable.UserPrincipalName
|
||||||
|
$remoteRoutingAddress = $mailboxUsername + $remoteRoutingSuffix
|
||||||
|
Enable-OnPremRemoteMailbox -Identity $mailboxToFix -RemoteRoutingAddress $remoteRoutingAddress
|
||||||
|
}
|
||||||
|
|
||||||
|
# Write out the mailboxes that have been updated
|
||||||
|
Write-Output -InputObject ('The following mailboxes have been remote mail enabled.')
|
||||||
|
$matchedUPNAliases | Select-Object Name,Alias,UserPrincipalName,PrimarySmtpAddress
|
||||||
|
|
||||||
|
# Write out the mailboxes that have been left due to a mismatch between username and alias
|
||||||
|
Write-Output -InputObject ('The following mailboxes have not been changed because they have a mismatch between email alias and username.')
|
||||||
|
$mismatchedUPNAliases | Select-Object Name,Alias,UserPrincipalName,PrimarySmtpAddress
|
||||||
|
|
||||||
|
# End the Exchange Session
|
||||||
|
Remove-PSSession -Session $session
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
# Disable a list of mail users
|
||||||
|
#
|
||||||
|
|
||||||
|
# What is the FQDN of the on-prem Exchange server?
|
||||||
|
$exchangeServerFQDN = ''
|
||||||
|
|
||||||
|
# What accounts are we disabling?
|
||||||
|
$usersToDisable = @('','')
|
||||||
|
|
||||||
|
# Create Exchange connection Uri from FQDN
|
||||||
|
$exchangeConnectionUri = 'http://' + $exchangeServerFQDN +'/PowerShell/'
|
||||||
|
|
||||||
|
# Establish a session to Exchange
|
||||||
|
$userCredential = Get-Credential
|
||||||
|
$session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri $exchangeConnectionUri -Authentication Kerberos -Credential $userCredential
|
||||||
|
Import-PSSession $session -DisableNameChecking -AllowClobber
|
||||||
|
|
||||||
|
# Get list of mailboxes
|
||||||
|
foreach ($userToDisable in $usersToDisable) {
|
||||||
|
Disable-RemoteMailbox -Identity $userToDisable -Confirm:$false
|
||||||
|
}
|
||||||
|
|
||||||
|
# End the Exchange Session
|
||||||
|
Remove-PSSession -Session $session
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
# Find mailboxes using a specific email domain and export list to CSV file
|
||||||
|
#
|
||||||
|
|
||||||
|
# What is the FQDN of the on-prem Exchange server?
|
||||||
|
$exchangeServerFQDN = ''
|
||||||
|
|
||||||
|
# Primary SMTP domain to search for
|
||||||
|
$primarySMTP = ''
|
||||||
|
|
||||||
|
# Where are we saving the output file?
|
||||||
|
$outputFile = 'C:\Temp\Mailboxes.csv'
|
||||||
|
|
||||||
|
# Create Exchange connection Uri from FQDN
|
||||||
|
$exchangeConnectionUri = 'http://' + $exchangeServerFQDN +'/PowerShell/'
|
||||||
|
|
||||||
|
# Establish a session to Exchange
|
||||||
|
$userCredential = Get-Credential
|
||||||
|
$session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri $exchangeConnectionUri -Authentication Kerberos -Credential $userCredential
|
||||||
|
Import-PSSession $session -DisableNameChecking
|
||||||
|
|
||||||
|
# Get list of mailboxes
|
||||||
|
$allMailboxes = Get-RemoteMailbox -ResultSize Unlimited | Where-Object {($_.PrimarySmtpAddress.Split('@')[1] -eq $primarySMTP)}
|
||||||
|
|
||||||
|
# Export results to CSV file
|
||||||
|
$allMailboxes | Select-Object Name,Alias,UserPrincipalName,PrimarySmtpAddress,EmailAddresses | Export-Csv -Path $outputFile -NoTypeInformation
|
||||||
|
|
||||||
|
# End the Exchange Session
|
||||||
|
Remove-PSSession -Session $session
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
# Exchange Online with AzureAD Connect
|
||||||
|
|
||||||
|
## What is this?
|
||||||
|
|
||||||
|
The scripts in this folder are for managing mailboxes in a synced environment, where you have an Exchange server on-prem purely for management purposes. This means all mailboxes are seen as "remote" mailboxes, so all the scripts use commands to that effect. These can easily be updated to a normal environment like on-prem only or cloud only just by removing the work "remote" from the commands. For example, Get-RemoteMailbox to just Get-Mailbox.
|
||||||
|
|
||||||
|
## Pre-requisites
|
||||||
|
|
||||||
|
These scripts require you to have on-prem Active Directory with an on-prem Exchange server for management, and using AzureAD Connect to sync to AzureAD.
|
||||||
|
|
||||||
|
It's pretty niche.
|
||||||
|
|
||||||
|
## Disclaimer
|
||||||
|
|
||||||
|
All scripts are provided as is without warranty of any kind, use them at your own risk.
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
# Bulk change guest accounts from a certain domain so that they show in the Global Address List
|
||||||
|
#
|
||||||
|
# Uses the new PowerShell "module" that support MFA.
|
||||||
|
#
|
||||||
|
|
||||||
|
# What domain are the guests email addresses from?
|
||||||
|
$guestDomain = ''
|
||||||
|
|
||||||
|
# Find and load the new ExO "module"
|
||||||
|
$exoModulePath = (Get-ChildItem -Path $env:userprofile -Filter CreateExoPSSession.ps1 -Recurse -Force -ErrorAction SilentlyContinue).DirectoryName[-1]
|
||||||
|
. "$exoModulePath\CreateExoPSSession.ps1"
|
||||||
|
|
||||||
|
# Establish a session to Exchange Online
|
||||||
|
Connect-EXOPSSession
|
||||||
|
|
||||||
|
# Find all the relevant users and enable them to show in the address list
|
||||||
|
Get-MailUser -ResultSize Unlimited | Where-Object {$_.RecipientTypeDetails -eq 'GuestMailUser' -and $_.EmailAddresses -match $guestDomain} | Set-MailUser -HiddenFromAddressListsEnabled $false
|
||||||
|
|
||||||
|
# End the Exchange Session
|
||||||
|
Get-PSSession | Where-Object {$_.ComputerName -eq 'outlook.office365.com'} | Remove-PSSession
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
# Create a group and add it to all room mailboxes as an editor
|
||||||
|
#
|
||||||
|
# Uses the new PowerShell "module" that support MFA.
|
||||||
|
#
|
||||||
|
|
||||||
|
# What do you want the editors group to be called?
|
||||||
|
$editorsGroup = 'Calendar Editors'
|
||||||
|
|
||||||
|
# Who do you want to be in the group? This can be 1 or more people.
|
||||||
|
$editorsMembers = @('','')
|
||||||
|
|
||||||
|
# Find and load the new ExO "module"
|
||||||
|
$exoModulePath = (Get-ChildItem -Path $env:userprofile -Filter CreateExoPSSession.ps1 -Recurse -Force -ErrorAction SilentlyContinue).DirectoryName[-1]
|
||||||
|
. "$exoModulePath\CreateExoPSSession.ps1"
|
||||||
|
|
||||||
|
# Establish a session to Exchange Online
|
||||||
|
Connect-EXOPSSession
|
||||||
|
|
||||||
|
# Get all room mailboxes in the organisation
|
||||||
|
$roomMailboxes = (Get-Mailbox -RecipientTypeDetails RoomMailbox).Alias
|
||||||
|
|
||||||
|
# If the editors group doesn't exist, create it.
|
||||||
|
if (!(Get-DistributionGroup -Identity $editorsGroup -ErrorAction SilentlyContinue)) {
|
||||||
|
New-DistributionGroup -Name $editorsGroup -Type Security
|
||||||
|
}
|
||||||
|
|
||||||
|
# Add members to the group
|
||||||
|
$existingMembers = Get-DistributionGroupMember -Identity $editorsGroup
|
||||||
|
foreach ($editorsMember in $editorsMembers) {
|
||||||
|
if ($editorsMember -notin $existingMembers.Name) {
|
||||||
|
Add-DistributionGroupMember -Identity $editorsGroup -Member $editorsMember
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Add the permissions to the mailboxes
|
||||||
|
foreach ($roomMailbox in $roomMailboxes) {
|
||||||
|
$calendarFolder = $mailboxAlias + ':\calendar'
|
||||||
|
Add-MailboxFolderPermission -Identity $calendarFolder -User $editorsGroup -AccessRights Editor
|
||||||
|
}
|
||||||
|
|
||||||
|
# End the Exchange Session
|
||||||
|
Get-PSSession | Where-Object {$_.ComputerName -eq 'outlook.office365.com'} | Remove-PSSession
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
# Create a rule to block email forwarding to specific domains.
|
||||||
|
#
|
||||||
|
# Uses the new PowerShell "module" that support MFA.
|
||||||
|
#
|
||||||
|
|
||||||
|
# What domains are we blocking?
|
||||||
|
$domainListFile = 'C:\Temp\DomainList.txt'
|
||||||
|
|
||||||
|
# What do we want to call the rule?
|
||||||
|
$ruleName = 'Block Auto Forwarding to Specific Domains 4'
|
||||||
|
|
||||||
|
# What reason do we want end users to see for the rejection?
|
||||||
|
$rejectionReason = 'Auto forwarding messages to this email provider is not permitted.'
|
||||||
|
|
||||||
|
# Find and load the new ExO "module"
|
||||||
|
$exoModulePath = (Get-ChildItem -Path $env:userprofile -Filter CreateExoPSSession.ps1 -Recurse -Force -ErrorAction SilentlyContinue).DirectoryName[-1]
|
||||||
|
. "$exoModulePath\CreateExoPSSession.ps1"
|
||||||
|
|
||||||
|
# Establish a session to Exchange Online
|
||||||
|
Connect-EXOPSSession
|
||||||
|
|
||||||
|
# Get the domain list from the file
|
||||||
|
$domainList = Get-Content -Path $domainListFile
|
||||||
|
|
||||||
|
# Create a new rule
|
||||||
|
$parameters = @{
|
||||||
|
'Name' = $ruleName;
|
||||||
|
'FromScope' = 'InOrganization';
|
||||||
|
'SenderAddressLocation' = 'Header'
|
||||||
|
'RecipientDomainIs' = $domainList;
|
||||||
|
'MessageTypeMatches' = 'AutoForward';
|
||||||
|
'RejectMessageEnhancedStatusCode' = '5.7.1'
|
||||||
|
'RejectMessageReasonText' = $rejectionReason;
|
||||||
|
'Priority' = 0;
|
||||||
|
'Mode' = 'Enforce'
|
||||||
|
'Enabled' = $true
|
||||||
|
}
|
||||||
|
New-TransportRule @parameters
|
||||||
|
|
||||||
|
# End the Exchange Session
|
||||||
|
Get-PSSession | Where-Object {$_.ComputerName -eq 'outlook.office365.com'} | Remove-PSSession
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
# Script to bulk remove email proxy addresses from Exchange Online users
|
||||||
|
#
|
||||||
|
|
||||||
|
# Which domains are we removing?
|
||||||
|
$domainsToRemove = @('','')
|
||||||
|
|
||||||
|
# Find and load the new ExO "module"
|
||||||
|
$exoModulePath = (Get-ChildItem -Path $env:userprofile -Filter CreateExoPSSession.ps1 -Recurse -Force -ErrorAction SilentlyContinue).DirectoryName[-1]
|
||||||
|
. "$exoModulePath\CreateExoPSSession.ps1"
|
||||||
|
|
||||||
|
# Establish a session to Exchange Online
|
||||||
|
Connect-EXOPSSession
|
||||||
|
|
||||||
|
### Remove the domain from mailboxes ###
|
||||||
|
|
||||||
|
# Get All Mailboxes
|
||||||
|
$allMailboxes = Get-Mailbox -ResultSize Unlimited | Sort-Object -Property alias
|
||||||
|
|
||||||
|
# Remove alias from each mailbox
|
||||||
|
foreach ($mailbox in $allMailboxes) {
|
||||||
|
$redundantAddresses = @()
|
||||||
|
foreach ($domainToRemove in $domainsToRemove) {
|
||||||
|
$redundantAddresses += (($mailbox.EmailAddresses -split ',' | Where-Object {$_ -like ('*' + $domainToRemove + '*')}) -replace 'smtp:','')
|
||||||
|
}
|
||||||
|
if ($redundantAddresses.Count -gt 0) {
|
||||||
|
Write-Output -InputObject ('Removing addresses ' + $redundantAddresses + ' from maibox ' + $mailbox.Name)
|
||||||
|
Set-Mailbox -Identity $mailbox.Identity -EmailAddresses @{remove=$redundantAddresses} -Confirm:$false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
###
|
||||||
|
|
||||||
|
### Remove the domain from contacts ###
|
||||||
|
|
||||||
|
# Get all the contacts
|
||||||
|
$allContacts = Get-MailContact -ResultSize Unlimited | Sort-Object -Property alias
|
||||||
|
|
||||||
|
# Remove alias from each contact
|
||||||
|
foreach ($contact in $allContacts) {
|
||||||
|
$redundantAddresses = @()
|
||||||
|
foreach ($domainToRemove in $domainsToRemove) {
|
||||||
|
$redundantAddresses += (($contact.EmailAddresses -split ',' | Where-Object {$_ -like ('*' + $domainToRemove + '*')}) -replace 'smtp:','')
|
||||||
|
}
|
||||||
|
if ($redundantAddresses.Count -gt 0) {
|
||||||
|
Write-Output -InputObject ('Removing addresses ' + $redundantAddresses + ' from contact ' + $contact.Name)
|
||||||
|
Set-MailContact -Identity $contact.Identity -EmailAddresses @{remove=$redundantAddresses} -Confirm:$false -ForceUpgrade:$true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
###
|
||||||
|
|
||||||
|
### Remove the domain from groups ###
|
||||||
|
|
||||||
|
# Get all the groups (this includes email enabled security groups)
|
||||||
|
$allGroups = Get-DistributionGroup -ResultSize Unlimited | Sort-Object -Property alias
|
||||||
|
|
||||||
|
# Remove alias from each group
|
||||||
|
foreach ($group in $allGroups) {
|
||||||
|
$redundantAddresses = @()
|
||||||
|
foreach ($domainToRemove in $domainsToRemove) {
|
||||||
|
$redundantAddresses += (($group.EmailAddresses -split ',' | Where-Object {$_ -like ('*' + $domainToRemove + '*')}) -replace 'smtp:','')
|
||||||
|
}
|
||||||
|
if ($redundantAddresses.Count -gt 0) {
|
||||||
|
Write-Output -InputObject ('Removing addresses ' + $redundantAddresses + ' from group ' + $group.Name)
|
||||||
|
Set-DistributionGroup -Identity $group.Identity -EmailAddresses @{remove=$redundantAddresses} -Confirm:$false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
###
|
||||||
|
|
||||||
|
### Remove the domain from public folders ###
|
||||||
|
|
||||||
|
# Get all the groups (this includes email enabled security groups)
|
||||||
|
$allPublicFolders = Get-MailPublicFolder -ResultSize Unlimited | Sort-Object -Property alias
|
||||||
|
|
||||||
|
# Remove alias from each public folder
|
||||||
|
foreach ($publicFolder in $allPublicFolders) {
|
||||||
|
$redundantAddresses = @()
|
||||||
|
foreach ($domainToRemove in $domainsToRemove) {
|
||||||
|
$redundantAddresses += (($publicFolder.EmailAddresses -split ',' | Where-Object {$_ -like ('*' + $domainToRemove + '*')}) -replace 'smtp:','')
|
||||||
|
}
|
||||||
|
if ($redundantAddresses.Count -gt 0) {
|
||||||
|
Write-Output -InputObject ('Removing addresses ' + $redundantAddresses + ' from public folder ' + $publicFolder.Name)
|
||||||
|
Set-MailPublicFolder -Identity $publicFolder.Identity -EmailAddresses @{remove=$redundantAddresses} -Confirm:$false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
###
|
||||||
|
|
||||||
|
# End the Exchange Session
|
||||||
|
Get-PSSession | Where-Object {$_.ComputerName -eq 'outlook.office365.com'} | Remove-PSSession
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
# Enable Modern Authentication in Exchange Online
|
||||||
|
#
|
||||||
|
# Uses the new PowerShell "module" that support MFA.
|
||||||
|
#
|
||||||
|
|
||||||
|
# Find and load the new ExO "module"
|
||||||
|
$exoModulePath = (Get-ChildItem -Path $env:userprofile -Filter CreateExoPSSession.ps1 -Recurse -Force -ErrorAction SilentlyContinue).DirectoryName[-1]
|
||||||
|
. "$exoModulePath\CreateExoPSSession.ps1"
|
||||||
|
|
||||||
|
# Establish a session to Exchange Online
|
||||||
|
Connect-EXOPSSession
|
||||||
|
|
||||||
|
# Enable modern authentication
|
||||||
|
Set-OrganizationConfig -OAuth2ClientProfileEnabled $true
|
||||||
|
|
||||||
|
# Verify the setting has changed
|
||||||
|
Get-OrganizationConfig | Format-Table -AutoSize Name,OAuth2ClientProfileEnabled
|
||||||
|
|
||||||
|
# End the Exchange Session
|
||||||
|
Get-PSSession | Where-Object {$_.ComputerName -eq 'outlook.office365.com'} | Remove-PSSession
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
# Enable mailbox auditing on mailboxes where auditing is not already enabled
|
||||||
|
#
|
||||||
|
# This is a rewrite of a script from https://github.com/OfficeDev/O365-InvestigationTooling
|
||||||
|
#
|
||||||
|
|
||||||
|
# Find and load the new ExO "module"
|
||||||
|
$exoModulePath = (Get-ChildItem -Path $env:userprofile -Filter CreateExoPSSession.ps1 -Recurse -Force -ErrorAction SilentlyContinue).DirectoryName[-1]
|
||||||
|
. "$exoModulePath\CreateExoPSSession.ps1"
|
||||||
|
|
||||||
|
# Establish a session to Exchange Online
|
||||||
|
Connect-EXOPSSession
|
||||||
|
|
||||||
|
# Set Auditing parameters
|
||||||
|
$params = @{
|
||||||
|
'AuditEnabled' = $true
|
||||||
|
'AuditLogAgeLimit' = '180'
|
||||||
|
'AuditAdmin' = @('Update','MoveToDeletedItems','SoftDelete','HardDelete','SendAs','SendOnBehalf','Create','UpdateFolderPermission')
|
||||||
|
'AuditDelegate' = @('Update','SoftDelete','HardDelete','SendAs','Create','UpdateFolderPermissions','MoveToDeletedItems','SendOnBehalf')
|
||||||
|
'AuditOwner' = @('UpdateFolderPermission','MailboxLogin','Create','SoftDelete','HardDelete','Update','MoveToDeletedItems')
|
||||||
|
}
|
||||||
|
|
||||||
|
# Enable Auditing
|
||||||
|
Get-Mailbox -ResultSize Unlimited | Where-Object {$_.RecipientTypeDetails -match '(User|Shared|Room|Discovery)Mailbox' -and $_.AuditEnabled -eq $false} | Set-Mailbox @params
|
||||||
|
|
||||||
|
# Check Auditing
|
||||||
|
Get-Mailbox -ResultSize Unlimited | Where-Object {$_.RecipientTypeDetails -match '(User|Shared|Room|Discovery)Mailbox'} | Format-Table -AutoSize UserPrincipalName,RecipientTypeDetails,AuditEnabled,AuditLogAgeLimit
|
||||||
|
|
||||||
|
# End the Exchange Session
|
||||||
|
Get-PSSession | Where-Object {$_.ComputerName -eq 'outlook.office365.com'} | Remove-PSSession
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
# Find mailboxes where UPN domain doesn't match email domain
|
||||||
|
#
|
||||||
|
|
||||||
|
# Find and load the new ExO "module"
|
||||||
|
$exoModulePath = (Get-ChildItem -Path $env:userprofile -Filter CreateExoPSSession.ps1 -Recurse -Force -ErrorAction SilentlyContinue).DirectoryName[-1]
|
||||||
|
. "$exoModulePath\CreateExoPSSession.ps1"
|
||||||
|
|
||||||
|
# Establish a session to Exchange Online
|
||||||
|
Connect-EXOPSSession
|
||||||
|
|
||||||
|
# Get list of mailboxes with a different mail domain to UPN domain
|
||||||
|
Get-Mailbox -ResultSize Unlimited | Where-Object {$_.UserPrincipalName.Split('@')[1] -ne $_.PrimarySmtpAddress.Split('@')[1]} | Format-Table Name,UserPrincipalName,PrimarySmtpAddress,RecipientTypeDetails
|
||||||
|
|
||||||
|
# End the Exchange Session
|
||||||
|
Get-PSSession | Where-Object {$_.ComputerName -eq 'outlook.office365.com'} | Remove-PSSession
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
# Find shared mailboxes on the basis of their email domain and lists of who has access to them
|
||||||
|
#
|
||||||
|
# Uses the new PowerShell "module" that support MFA.
|
||||||
|
#
|
||||||
|
|
||||||
|
# Where to save the CSV files to
|
||||||
|
$outputFile = 'C:\Temp\GroupDetails.csv'
|
||||||
|
|
||||||
|
# Wildcard for groups to find
|
||||||
|
$searchWildcard = '*@example.domain'
|
||||||
|
|
||||||
|
# Find and load the new ExO "module"
|
||||||
|
$exoModulePath = (Get-ChildItem -Path $env:userprofile -Filter CreateExoPSSession.ps1 -Recurse -Force -ErrorAction SilentlyContinue).DirectoryName[-1]
|
||||||
|
. "$exoModulePath\CreateExoPSSession.ps1"
|
||||||
|
|
||||||
|
# Establish a session to Exchange Online
|
||||||
|
Connect-EXOPSSession
|
||||||
|
|
||||||
|
# Get all the groups we're looking for
|
||||||
|
$allGroups = Get-DistributionGroup | Where-Object {$_.PrimarySmtpAddress -like $searchWildcard}
|
||||||
|
|
||||||
|
# Initialise the hash table to store results in
|
||||||
|
$groupDetails = @()
|
||||||
|
|
||||||
|
# Run through the groups getting the members and adding
|
||||||
|
foreach ($group in $allGroups) {
|
||||||
|
$groupMembers = (Get-DistributionGroupMember -Identity $group.Name).Alias -join '; '
|
||||||
|
$groupDetails += [PSCustomObject]@{
|
||||||
|
'GroupName' = $group.Name;
|
||||||
|
'GroupEmail' = $group.PrimarySmtpAddress;
|
||||||
|
'GroupType' = $group.GroupType;
|
||||||
|
'GroupMembers' = $groupMembers
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Export results to CSV file
|
||||||
|
$groupDetails | Export-Csv -Path $outputFile -NoTypeInformation
|
||||||
|
|
||||||
|
# End the Exchange Session
|
||||||
|
Get-PSSession | Where-Object {$_.ComputerName -eq 'outlook.office365.com'} | Remove-PSSession
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
# Get the permissions and sendas rights for a list of mailboxes and output them to CSV
|
||||||
|
#
|
||||||
|
# Uses the new PowerShell "module" that support MFA.
|
||||||
|
#
|
||||||
|
|
||||||
|
# Where to save the CSV files to
|
||||||
|
$outputPath = 'C:\Temp\MailboxPermissions\'
|
||||||
|
|
||||||
|
# What mailboxes are we checking?
|
||||||
|
$mailboxes = Get-Content -Path 'C:\Temp\Mailboxes.txt'
|
||||||
|
|
||||||
|
# Find and load the new ExO "module"
|
||||||
|
$exoModulePath = (Get-ChildItem -Path $env:userprofile -Filter CreateExoPSSession.ps1 -Recurse -Force -ErrorAction SilentlyContinue).DirectoryName[-1]
|
||||||
|
. "$exoModulePath\CreateExoPSSession.ps1"
|
||||||
|
|
||||||
|
# Establish a session to Exchange Online
|
||||||
|
Connect-EXOPSSession
|
||||||
|
|
||||||
|
# Get all non-inherited mailbox permissions excluding self and output to a CSV file named for each mailbox
|
||||||
|
foreach ($mailbox in $mailboxes) {
|
||||||
|
Write-Output -InputObject ('Getting permissions for mailbox ' + $mailbox)
|
||||||
|
$mailboxPermissions = Get-MailboxPermission -Identity $mailbox | Where-Object {$_.IsInherited -eq $false -and $_.User -ne 'NT AUTHORITY\SELF'} | Select-Object Identity,User,AccessRights,Deny
|
||||||
|
if ($mailboxPermissions.Count -gt 0) {
|
||||||
|
$outputFile = $outputPath + 'MailboxPermissions_' + ($mailbox -replace '@','_') + '.csv'
|
||||||
|
$mailboxPermissions | Export-CSV -Path $outputFile -NoTypeInformation
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Get all non-inherited recipient permissions (sendas) excluding self and output to a CSV file named for each mailbox
|
||||||
|
foreach ($mailbox in $mailboxes) {
|
||||||
|
Write-Output -InputObject ('Getting permissions for recipient ' + $mailbox)
|
||||||
|
$recipientPermissions = Get-RecipientPermission -Identity $mailbox | Where-Object {$_.IsInherited -eq $false -and $_.Trustee -ne 'NT AUTHORITY\SELF'} | Select-Object Identity,Trustee,AccessRights,AccessControlType
|
||||||
|
if ($recipientPermissions.Count -gt 0) {
|
||||||
|
$outputFile = $outputPath + 'RecipientPermissions_' + ($mailbox -replace '@','_') + '.csv'
|
||||||
|
$recipientPermissions | Export-CSV -Path $outputFile -NoTypeInformation
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# End the Exchange Session
|
||||||
|
Get-PSSession | Where-Object {$_.ComputerName -eq 'outlook.office365.com'} | Remove-PSSession
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
# Find shared mailboxes on the basis of their email domain and lists of who has access to them
|
||||||
|
#
|
||||||
|
# Uses the new PowerShell "module" that support MFA.
|
||||||
|
#
|
||||||
|
|
||||||
|
# Where to save the CSV files to
|
||||||
|
$outputPath = 'C:\Temp\'
|
||||||
|
|
||||||
|
# Wildcard for mailboxes to find
|
||||||
|
$searchWildcard = '*'
|
||||||
|
|
||||||
|
# Find and load the new ExO "module"
|
||||||
|
$exoModulePath = (Get-ChildItem -Path $env:userprofile -Filter CreateExoPSSession.ps1 -Recurse -Force -ErrorAction SilentlyContinue).DirectoryName[-1]
|
||||||
|
. "$exoModulePath\CreateExoPSSession.ps1"
|
||||||
|
|
||||||
|
# Establish a session to Exchange Online
|
||||||
|
Connect-EXOPSSession
|
||||||
|
|
||||||
|
# Get all the mailboxes we're looking for
|
||||||
|
$sharedMailboxes = Get-Mailbox -RecipientTypeDetails SharedMailbox -ResultSize Unlimited | Where-Object {$_.PrimarySmtpAddress -like $searchWildcard} | Select-Object Name,Alias,PrimarySmtpAddress,ProhibitSendQuota
|
||||||
|
|
||||||
|
# Export list of mailboxes to CSV File
|
||||||
|
$sharedMailboxes | Export-CSV -Path ($outputPath + 'Shared Mailboxes.csv') -NoTypeInformation
|
||||||
|
|
||||||
|
# Get all non-inherited permissions excluding self and output to a CSV file named for each group
|
||||||
|
foreach ($sharedMailbox in $sharedMailboxes) {
|
||||||
|
$mailboxPermissions = Get-MailboxPermission -Identity $sharedMailbox.alias | Where-Object {$_.IsInherited -eq $false -and $_.User -ne 'NT AUTHORITY\SELF'} | Select-Object Identity,User,AccessRights,Deny
|
||||||
|
$mailboxPermissions | Export-CSV -Path ($outputPath + $sharedMailbox.Name + '.csv') -NoTypeInformation
|
||||||
|
}
|
||||||
|
|
||||||
|
# End the Exchange Session
|
||||||
|
Get-PSSession | Where-Object {$_.ComputerName -eq 'outlook.office365.com'} | Remove-PSSession
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
# Exchange Online with MFA Support
|
||||||
|
|
||||||
|
## What is this?
|
||||||
|
|
||||||
|
Connecting to Exchange Online through a remote PowerShell session doesn't work when using multi factor authentication. You can (apparrently) get round that by creating an application password, but I've never got it to work and in my mind creating a password to bypass MFA somewhat defeates the point of enabling MFA in the first place.
|
||||||
|
|
||||||
|
There now is a new "module" available which does support MFA, so this folder is where I'll be putting new scripts that support MFA or old scripts as I update them.
|
||||||
|
|
||||||
|
## Pre-requisites
|
||||||
|
|
||||||
|
To connect using MFA you have to locally install a new module from Microsoft. Which for whatever reason isn't available from PSGallery, nor can it be simply downloaded. Instead it has to be installed from within the Exchange Online admin centre using one of those annoying ClickOnce installers that only work in MS's own web browsers.
|
||||||
|
|
||||||
|
Microsoft have a document explaining the unnecessarily convulted install process here:
|
||||||
|
|
||||||
|
[Connect to Exchange Online PowerShell using multi-factor authentication](https://docs.microsoft.com/en-us/powershell/exchange/exchange-online/connect-to-exchange-online-powershell/mfa-connect-to-exchange-online-powershell?view=exchange-ps)
|
||||||
|
|
||||||
|
In addition, because this will be using a remote session it will require the script execution policy within PowerShell to be changed to RemoteSigned. This is done either globally with:
|
||||||
|
|
||||||
|
`Set-ExecutionPolicy -ExecutionPolicy RemoteSigned`
|
||||||
|
|
||||||
|
Or for the current user with:
|
||||||
|
|
||||||
|
`Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser`
|
||||||
|
|
||||||
|
## Disclaimer
|
||||||
|
|
||||||
|
All scripts are provided as is without warranty of any kind, use them at your own risk.
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
# Reset default calendar permissions to availability for a list of users.
|
||||||
|
#
|
||||||
|
# Uses the new PowerShell "module" that support MFA.
|
||||||
|
#
|
||||||
|
|
||||||
|
# File with list of users
|
||||||
|
$userlistPath = 'C:\Temp\Userlist.txt'
|
||||||
|
|
||||||
|
# Find and load the new ExO "module"
|
||||||
|
$exoModulePath = (Get-ChildItem -Path $env:userprofile -Filter CreateExoPSSession.ps1 -Recurse -Force -ErrorAction SilentlyContinue).DirectoryName[-1]
|
||||||
|
. "$exoModulePath\CreateExoPSSession.ps1"
|
||||||
|
|
||||||
|
# Establish a session to Exchange Online
|
||||||
|
Connect-EXOPSSession
|
||||||
|
|
||||||
|
# Get list of users
|
||||||
|
$userlist = Get-Content -Path $userlistPath
|
||||||
|
|
||||||
|
# Get mailboxes for users in list
|
||||||
|
$mailboxes = Get-Mailbox -ResultSize Unlimited | Where-Object {$_.Name -in $userlist} | Sort-Object -Property Name
|
||||||
|
|
||||||
|
# Check the mailboxes and reset any which have default permissions of None to AvailabilityOnly
|
||||||
|
foreach ($mailbox in $mailboxes) {
|
||||||
|
$calendarPath = $mailbox.UserPrincipalName + ':\Calendar'
|
||||||
|
$defaultPermissions = Get-MailboxFolderPermission -Identity $calendarPath -User 'Default'
|
||||||
|
if ($defaultPermissions.AccessRights -eq 'None') {
|
||||||
|
Set-MailboxFolderPermission -Identity $calendarPath -User 'Default' -AccessRights AvailabilityOnly
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# End the Exchange Session
|
||||||
|
Get-PSSession | Where-Object {$_.ComputerName -eq 'outlook.office365.com'} | Remove-PSSession
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
# A simple script to enable forwarding for a list of users from a CSV file
|
||||||
|
#
|
||||||
|
# The script is expecting the CSV file to have two columns called SourceAddress and DestinationAddress
|
||||||
|
#
|
||||||
|
|
||||||
|
# Get the list of users from a CSV file
|
||||||
|
$userList = Import-Csv -Path 'C:\Temp\UserList.csv'
|
||||||
|
|
||||||
|
# Find and load the new ExO "module"
|
||||||
|
$exoModulePath = (Get-ChildItem -Path $env:userprofile -Filter CreateExoPSSession.ps1 -Recurse -Force -ErrorAction SilentlyContinue).DirectoryName[-1]
|
||||||
|
. "$exoModulePath\CreateExoPSSession.ps1"
|
||||||
|
|
||||||
|
# Establish a session to Exchange Online
|
||||||
|
Connect-EXOPSSession
|
||||||
|
|
||||||
|
# Get all non-inherited permissions excluding self and output to a CSV file named for each group
|
||||||
|
foreach ($user in $userList) {
|
||||||
|
Write-Output -InputObject ('Forwarding ' + $user.SourceAddress + ' to ' + $user.DestinationAddress)
|
||||||
|
Set-Mailbox -Identity $user.SourceAddress -ForwardingSmtpAddress $user.DestinationAddress
|
||||||
|
}
|
||||||
|
|
||||||
|
# Pull back a list to check everything has worked correctly
|
||||||
|
foreach ($user in $userList) {
|
||||||
|
Get-Mailbox -Identity $user.SourceAddress | Select-Object UserPrincipalName,ForwardingSmtpAddress,DeliverToMailboxAndForward
|
||||||
|
}
|
||||||
|
|
||||||
|
# End the Exchange Session
|
||||||
|
Get-PSSession | Where-Object {$_.ComputerName -eq 'outlook.office365.com'} | Remove-PSSession
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
# Create and set up a room mailbox.
|
||||||
|
#
|
||||||
|
# Uses the new PowerShell "module" that support MFA.
|
||||||
|
#
|
||||||
|
|
||||||
|
# New room name and alias
|
||||||
|
$displayName = ''
|
||||||
|
$mailboxAlias = ''
|
||||||
|
|
||||||
|
# How many people can the room take?
|
||||||
|
$roomCapacity = ''
|
||||||
|
|
||||||
|
# Do you want meeting requests to be auto accepted?
|
||||||
|
$requestAutoAccept = $true
|
||||||
|
|
||||||
|
# Do we want to add the room to a room list?
|
||||||
|
$addToRoomList = $true
|
||||||
|
|
||||||
|
# What is the room list called? (Will be created if it doesn't exist)
|
||||||
|
$roomList = ''
|
||||||
|
|
||||||
|
# Find and load the new ExO "module"
|
||||||
|
$exoModulePath = (Get-ChildItem -Path $env:userprofile -Filter CreateExoPSSession.ps1 -Recurse -Force -ErrorAction SilentlyContinue).DirectoryName[-1]
|
||||||
|
. "$exoModulePath\CreateExoPSSession.ps1"
|
||||||
|
|
||||||
|
# Establish a session to Exchange Online
|
||||||
|
Connect-EXOPSSession
|
||||||
|
|
||||||
|
# Create the new room mailbox
|
||||||
|
New-Mailbox -Room -Alias $mailboxAlias -Name $displayName -DisplayName $displayName -ResourceCapacity $roomCapacity
|
||||||
|
|
||||||
|
# Wait for the meeting room mailbox to process
|
||||||
|
Start-Sleep -Seconds 30
|
||||||
|
|
||||||
|
# Set the mailbox calendar to auto accept meeting requests (assuming policies are met)
|
||||||
|
if ($requestAutoAccept) {
|
||||||
|
Set-CalendarProcessing $mailboxAlias -AutomateProcessing AutoAccept
|
||||||
|
}
|
||||||
|
|
||||||
|
# If we're adding the room to a room list, then do that.
|
||||||
|
if ($addToRoomList) {
|
||||||
|
# If the room list doesn't exist, create it.
|
||||||
|
if (!(Get-DistributionGroup -Identity $roomList -ErrorAction SilentlyContinue)) {
|
||||||
|
New-DistributionGroup -Name $roomList -RoomList
|
||||||
|
}
|
||||||
|
|
||||||
|
# Add the room to the list
|
||||||
|
Add-DistributionGroupMember -Identity $roomList -Member $mailboxAlias
|
||||||
|
}
|
||||||
|
|
||||||
|
# End the Exchange Session
|
||||||
|
Get-PSSession | Where-Object {$_.ComputerName -eq 'outlook.office365.com'} | Remove-PSSession
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
# ScheduledBitLockerKeyBackup.ps1
|
||||||
|
|
||||||
|
<#
|
||||||
|
Script intended for deployment through Intune:
|
||||||
|
Creates a locally saved PS script to get the bitlocker key and save it to Azure.
|
||||||
|
Then creates a scheduled task to run that script at every logon.
|
||||||
|
#>
|
||||||
|
|
||||||
|
# Name and description for the schedulled task to be created
|
||||||
|
$taskName = 'Bitlocker Key Backup to AzureAD'
|
||||||
|
$taskDescription = 'Retrieve Bitlocker key for system drive and store it in AzureAD'
|
||||||
|
|
||||||
|
# Day, time and randomised delay for the task to be created
|
||||||
|
$taskDay = 'Monday'
|
||||||
|
$taskTime = '12:00:00'
|
||||||
|
$taskDelay = '00:20:00'
|
||||||
|
|
||||||
|
# Path to the folder for the files to be created
|
||||||
|
$scriptFolder = 'C:\ProgramData\Intune\Scripts\'
|
||||||
|
|
||||||
|
# Names of the files to be created
|
||||||
|
$scriptFilename = 'BitlockerKeyBackup.ps1'
|
||||||
|
$scriptLogFilename = 'BitlockerKeyBackup-LastRun.log'
|
||||||
|
$deployLogFilename = 'BitlockerKeyBackup-Deployed.log'
|
||||||
|
|
||||||
|
# Establish full paths to the files
|
||||||
|
$scriptPath = $scriptFolder + $scriptFilename
|
||||||
|
$logPath = $scriptFolder + $scriptLogFilename
|
||||||
|
$deployLogPath = $scriptFolder + $deployLogFilename
|
||||||
|
|
||||||
|
# If folder doesn't exist create it, else clean up files from last run
|
||||||
|
if (!(Test-Path -Path $scriptFolder)) {
|
||||||
|
New-Item -Path $scriptFolder -ItemType Directory
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
foreach ($filePath in @($scriptPath, $logPath, $deployLogPath)) {
|
||||||
|
if (Test-Path -Path $filePath) {
|
||||||
|
Remove-Item -Path $filePath
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Contents of the PS script file to be created on the target machine
|
||||||
|
$scriptContents = @(
|
||||||
|
'$logPath = ''' + $logPath + ''''
|
||||||
|
'try {'
|
||||||
|
' $recoveryPassword = ((Get-BitlockerVolume -MountPoint $env:SystemDrive -ErrorAction Stop).KeyProtector | Where-Object {$_.KeyProtectorType -eq "RecoveryPassword"})'
|
||||||
|
' $result = BackupToAAD-BitLockerKeyProtector $env:systemdrive -KeyProtectorId $recoveryPassword.KeyProtectorId -ErrorAction Stop'
|
||||||
|
' Out-File -InputObject $result -FilePath $logPath'
|
||||||
|
'}'
|
||||||
|
'catch {'
|
||||||
|
' Out-File -InputObject $Error[0].Exception.Message -FilePath $logPath'
|
||||||
|
'}'
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create the script file
|
||||||
|
Out-File -InputObject $scriptContents -FilePath $scriptPath
|
||||||
|
|
||||||
|
# Set up the various parts of the scheduled task
|
||||||
|
$taskArgument = '-ExecutionPolicy Bypass -Command ". ' + $scriptPath + '"'
|
||||||
|
$taskAction = New-ScheduledTaskAction -Execute 'powershell.exe' -Argument $taskArgument -WorkingDirectory $scriptFolder
|
||||||
|
$taskTrigger = New-ScheduledTaskTrigger -Weekly -DaysOfWeek $taskDay -At $taskTime -RandomDelay $taskDelay
|
||||||
|
$taskPrincipal = New-ScheduledTaskPrincipal -UserId 'SYSTEM' -LogonType 'ServiceAccount' -RunLevel 'Highest'
|
||||||
|
$taskSettings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -Compatibility 'Win8' -Hidden -StartWhenAvailable
|
||||||
|
|
||||||
|
# If the scheduled task already exists then remove it
|
||||||
|
if (Get-ScheduledTask | Where-Object { $_.TaskName -eq $taskName }) {
|
||||||
|
Unregister-ScheduledTask -TaskName $taskName -Confirm:$false
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create the scheduled task and run it immediately
|
||||||
|
$taskCreated = Register-ScheduledTask -Action $taskAction -Trigger $taskTrigger -TaskName $taskName -Description $taskDescription -Principal $taskPrincipal -Settings $taskSettings
|
||||||
|
Start-ScheduledTask -TaskName $taskName
|
||||||
|
|
||||||
|
# Create a log file for the deployment of the scheduled task
|
||||||
|
Out-File -InputObject $taskCreated -FilePath $deployLogPath
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
# Convert AD GUID to 365 ImmutableID
|
||||||
|
|
||||||
|
$adGuid = Read-Host -Prompt 'Enter Active Directory GUID to convert to ImmutableID'
|
||||||
|
[System.Convert]::ToBase64String($adGuid.tobytearray())
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
# Bulk add a new licence to users on the basis of what licence they currently have
|
||||||
|
#
|
||||||
|
# This is useful for something like adding Office 365 ATP to everyone who currently has E3, for example.
|
||||||
|
#
|
||||||
|
|
||||||
|
# What licence do the users currently have?
|
||||||
|
$existingLicence = ''
|
||||||
|
|
||||||
|
# What licence are we adding?
|
||||||
|
$licenceToAdd = ''
|
||||||
|
|
||||||
|
# Import MSOnline module and connect
|
||||||
|
Import-Module MSOnline
|
||||||
|
Connect-MsolService
|
||||||
|
|
||||||
|
# Find everyone who has the existing
|
||||||
|
$users = Get-MsolUser -All | Where-Object {($_.licenses).AccountSkuId -match $existingLicence -and !(($_.licenses).AccountSkuId -match $licenceToAdd)}
|
||||||
|
|
||||||
|
# Add the new licence
|
||||||
|
foreach ($user in $users) {
|
||||||
|
Set-MSOLUserLicense -UserPrincipalName $user.UserPrincipalName –AddLicenses $licenceToAdd
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
# Bulk add additional licences to a list of users where they already have a licence assigned.
|
||||||
|
#
|
||||||
|
# Only works where the user already has a licence assigned as assigning a licence to an unlicenced user is a different process.
|
||||||
|
#
|
||||||
|
|
||||||
|
# Where is the list of user UPN's?
|
||||||
|
$userListPath = 'C:\Temp\UserList.txt'
|
||||||
|
|
||||||
|
# What licences are we adding?
|
||||||
|
# List of available SKUs can be obtained with (Get-MsolAccountSku).AccountSkuId
|
||||||
|
$licencesToAdd = @('','')
|
||||||
|
|
||||||
|
# Import MSOnline module and connect
|
||||||
|
Import-Module MSOnline
|
||||||
|
Connect-MsolService
|
||||||
|
|
||||||
|
# Import list of users from file
|
||||||
|
$users = Get-Content -Path $userListPath | Sort-Object
|
||||||
|
|
||||||
|
# Add the new licence
|
||||||
|
foreach ($user in $users) {
|
||||||
|
$userDetails = Get-MSOLUser -UserPrincipalName $user
|
||||||
|
foreach ($licenceToAdd in $licencesToAdd) {
|
||||||
|
if ($userDetails.IsLicensed -eq $true) {
|
||||||
|
if (!(($userDetails.licenses).AccountSkuId -match $licenceToAdd)) {
|
||||||
|
Write-Output -InputObject ('Adding Licence ' + $licenceToAdd + ' to ' + $user + '.')
|
||||||
|
Set-MSOLUserLicense -UserPrincipalName $user –AddLicenses $licenceToAdd
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Write-Output -InputObject ('User ' + $user + ' already has ' + $licenceToAdd + ' licence assigned.')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Write-Output -InputObject ('User ' + $user + ' has no existing licence assigned.')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
# Bulk enable or disable MFA for all users in a tenant
|
||||||
|
#
|
||||||
|
|
||||||
|
# Enable or disable MFA?
|
||||||
|
$enableMFA = $true
|
||||||
|
|
||||||
|
# Import MSOnline module and connect
|
||||||
|
Import-Module MSOnline
|
||||||
|
Connect-MsolService
|
||||||
|
|
||||||
|
# Enable or disable MFA
|
||||||
|
if ($enableMFA) {
|
||||||
|
# Create an object containing the authentication requirements
|
||||||
|
$authReq = New-Object -TypeName Microsoft.Online.Administration.StrongAuthenticationRequirement
|
||||||
|
|
||||||
|
# Include all relying parties
|
||||||
|
$authReq.RelyingParty = '*'
|
||||||
|
|
||||||
|
# Set MFA state to enabled
|
||||||
|
# Enabled allows connected apps to keep working until the user completes MFA set up.
|
||||||
|
# This can also be set to enforced which would disconnect everything immediately.
|
||||||
|
$authReq.State = 'Enabled'
|
||||||
|
|
||||||
|
# Set the cut off date before which registered devices should require re-connecting with MFA.
|
||||||
|
# Using the current date is recommended so that all previously connected devices have to be reconnected.
|
||||||
|
$authReq.RememberDevicesNotIssuedBefore = (Get-Date)
|
||||||
|
|
||||||
|
# Find all licenced users who do not currently have MFA enabled or enforced
|
||||||
|
$usersToChange = Get-MsolUser | Where-Object {$_.StrongAuthenticationRequirements.State -notmatch 'Enabled|Enforced' -and $_.isLicensed -eq $true}
|
||||||
|
|
||||||
|
# Enable MFA for those users
|
||||||
|
$usersToChange | Set-MsolUser -StrongAuthenticationRequirements $authReq
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
# Find all licenced users who currently have MFA enabled or enforced
|
||||||
|
$usersToChange = Get-MsolUser | Where-Object {$_.StrongAuthenticationRequirements.State -match 'Enabled|Enforced' -and $_.isLicensed -eq $true}
|
||||||
|
|
||||||
|
# Disable MFA for those users
|
||||||
|
$usersToChange | Set-MsolUser -StrongAuthenticationRequirements @()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
# Bulk update Online User UPNs
|
||||||
|
#
|
||||||
|
|
||||||
|
# What domain are we replacing?
|
||||||
|
$oldDomain = ''
|
||||||
|
|
||||||
|
# What are we replacing it with?
|
||||||
|
$newDomain = ''
|
||||||
|
|
||||||
|
# Import the MSOnline module and connect
|
||||||
|
Import-Module MSOnline
|
||||||
|
Connect-MsolService
|
||||||
|
|
||||||
|
# Get all users using that domain as their UPN
|
||||||
|
$users = Get-MsolUser -All | Where-Object {$_.UserPrincipalName -match $oldDomain}
|
||||||
|
|
||||||
|
# Run through the users replacing their UPN and keeping us updated on what's going on
|
||||||
|
foreach ($user in $users) {
|
||||||
|
$newUPN = $user.UserPrincipalName.Split('@')[0] + '@' + $newDomain
|
||||||
|
Write-Output -InputObject ('Setting UPN for user ' + $user.UserPrincipalName + ' to ' + $newUPN)
|
||||||
|
Set-MsolUserPrincipalName -UserPrincipalName $user.UserPrincipalName -NewUserPrincipalName $newUPN
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
# Disable MFA for all licenced users who currently have it enabled
|
||||||
|
#
|
||||||
|
|
||||||
|
# Import MSOnline module and connect
|
||||||
|
Import-Module MSOnline
|
||||||
|
Connect-MsolService
|
||||||
|
|
||||||
|
# Find all licenced users who currently have MFA enabled or enforced
|
||||||
|
$usersToChange = Get-MsolUser | Where-Object {$_.StrongAuthenticationRequirements.State -match 'Enabled|Enforced' -and $_.isLicensed -eq $true}
|
||||||
|
|
||||||
|
# Disable MFA for those users
|
||||||
|
$usersToChange | Set-MsolUser -StrongAuthenticationRequirements @()
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
# Enable MFA for all licenced users who don't currently have it enabled
|
||||||
|
#
|
||||||
|
|
||||||
|
# Import MSOnline module and connect
|
||||||
|
Import-Module MSOnline
|
||||||
|
Connect-MsolService
|
||||||
|
|
||||||
|
# Create an object containing the authentication requirements
|
||||||
|
$authReq = New-Object -TypeName Microsoft.Online.Administration.StrongAuthenticationRequirement
|
||||||
|
|
||||||
|
# Include all relying parties
|
||||||
|
$authReq.RelyingParty = '*'
|
||||||
|
|
||||||
|
# Set MFA state to enabled
|
||||||
|
# Enabled allows connected apps to keep working until the user completes MFA set up.
|
||||||
|
# This can also be set to enforced which would disconnect everything immediately.
|
||||||
|
$authReq.State = 'Enabled'
|
||||||
|
|
||||||
|
# Set the cut off date before which registered devices should require re-connecting with MFA
|
||||||
|
$authReq.RememberDevicesNotIssuedBefore = (Get-Date)
|
||||||
|
|
||||||
|
# Find all licenced users who do not currently have MFA enabled or enforced
|
||||||
|
$usersToChange = Get-MsolUser | Where-Object {$_.StrongAuthenticationRequirements.State -notmatch 'Enabled|Enforced' -and $_.isLicensed -eq $true}
|
||||||
|
|
||||||
|
# Enable MFA for those users
|
||||||
|
$usersToChange | Set-MsolUser -StrongAuthenticationRequirements $authReq
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
# Bulk remove a licence from users
|
||||||
|
#
|
||||||
|
|
||||||
|
# What licence are we removing?
|
||||||
|
$licenceToRemove = ''
|
||||||
|
|
||||||
|
# Import MSOnline module and connect
|
||||||
|
Import-Module MSOnline
|
||||||
|
Connect-MsolService
|
||||||
|
|
||||||
|
# Find everyone who has the licence to be removed
|
||||||
|
$users = Get-MsolUser -All | Where-Object {($_.licenses).AccountSkuId -match $licenceToRemove}
|
||||||
|
|
||||||
|
# Remove the licence
|
||||||
|
foreach ($user in $users) {
|
||||||
|
Set-MSOLUserLicense –user $user.UserPrincipalName –RemoveLicenses $licenceToRemove
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
# Bulk replace a licence for all users who have the one being replaced.
|
||||||
|
#
|
||||||
|
|
||||||
|
# What licence are we removing?
|
||||||
|
$licenceToRemove = ''
|
||||||
|
|
||||||
|
# What licence are we adding?
|
||||||
|
$licenceToAdd = ''
|
||||||
|
|
||||||
|
# Import MSOnline module and connect
|
||||||
|
Import-Module MSOnline
|
||||||
|
Connect-MsolService
|
||||||
|
|
||||||
|
# Find everyone who has the licence being replaced
|
||||||
|
$users = Get-MsolUser -All | Where-Object {($_.licenses).AccountSkuId -match $licenceToRemove}
|
||||||
|
|
||||||
|
# Remove the old licence and add the new one
|
||||||
|
foreach ($user in $users) {
|
||||||
|
Set-MSOLUserLicense –user $user.UserPrincipalName –RemoveLicenses $licenceToRemove
|
||||||
|
Set-MSOLUserLicense –user $user.UserPrincipalName -AddLicenses $licenceToAdd
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
# Update Online User UPN - when AD sync doesn't do it.
|
||||||
|
|
||||||
|
# Import the MSOL module and connect
|
||||||
|
Import-Module MSOnline
|
||||||
|
Connect-MsolService
|
||||||
|
|
||||||
|
# Whose UPN are we changing?
|
||||||
|
$oldUPN = Read-Host -Prompt 'Enter user''s old UPN in the format username@domain'
|
||||||
|
|
||||||
|
# What are we changing it to?
|
||||||
|
$newUPN = Read-Host -Prompt 'Enter user''s new UPN in the format username@domain'
|
||||||
|
|
||||||
|
# Change the UPN
|
||||||
|
if (Get-ADUser -Identity $newUPN.Split('@')[0]) {
|
||||||
|
Set-MsolUserPrincipalName -UserPrincipalName $oldUPN -NewUserPrincipalName $newUPN
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
# Delete a user and then delete the deleted user
|
||||||
|
|
||||||
|
Import-Module MSOnline
|
||||||
|
Connect-MsolService
|
||||||
|
|
||||||
|
$upnToDelete = Read-Host -Prompt 'Enter UPN of user to delete in the format username@domain'
|
||||||
|
|
||||||
|
Remove-MsolUser -UserPrincipalName $upnToDelete
|
||||||
|
Remove-MsolUser -UserPrincipalName $upnToDelete -RemoveFromRecycleBin
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
# Remove a duplicate 365 account created by a faulty sync and reconnect
|
||||||
|
# the orphaned 365 user to the AD account.
|
||||||
|
#
|
||||||
|
|
||||||
|
# Get correct and incorrect account details
|
||||||
|
$adUsername = Read-Host -Prompt 'Enter username for AD account'
|
||||||
|
$msolIncorrectUPN = Read-Host -Prompt 'Enter UPN for the duplicate object in MSOL'
|
||||||
|
|
||||||
|
# Connect to MS Online
|
||||||
|
Import-Module MSOnline
|
||||||
|
Connect-MsolService
|
||||||
|
|
||||||
|
# Get AD user account
|
||||||
|
$adObject = Get-ADUser -Identity $adUsername
|
||||||
|
|
||||||
|
# Get correct UPN from AD account
|
||||||
|
$msolCorrectUPN = $adObject.UserPrincipalName
|
||||||
|
|
||||||
|
#
|
||||||
|
try {
|
||||||
|
Get-MsolUser -UserPrincipalName $msolCorrectUPN -ErrorAction Stop
|
||||||
|
|
||||||
|
Remove-MSOLuser -UserPrincipalName $msolIncorrectUPN
|
||||||
|
Remove-MSOLuser -UserPrincipalName $msolIncorrectUPN -RemoveFromRecycleBin
|
||||||
|
|
||||||
|
$adGuid = $adObject.ObjectGuid
|
||||||
|
$immutableID = [System.Convert]::ToBase64String($adGuid.ToByteArray())
|
||||||
|
|
||||||
|
Set-MSOLuser -UserPrincipalName $msolCorrectUPN -ImmutableID $immutableID
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Host 'No account found in Azure AD matching the UPN for that AD account.'
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
# Convert 365 ImmutableID to AD GUID
|
||||||
|
|
||||||
|
$immutableID = Read-Host -Prompt 'Enter ImmutableID to convert to Active Directory GUID'
|
||||||
|
[GUID][System.Convert]::FromBase64String($immutableID)
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# Retrive a list of all OneDrive for Business sites within the organisation
|
||||||
|
#
|
||||||
|
# Requires the Sharepoint Online PowerShell module to be installed
|
||||||
|
#
|
||||||
|
|
||||||
|
# The name of your Office 365 organization
|
||||||
|
# This can be found in your Sharepoint URL before the '-my', eg: https://thecompany-my.sharepoint.com/
|
||||||
|
$spoTenantName=''
|
||||||
|
|
||||||
|
# Connect to Sharepoint Online
|
||||||
|
Import-Module Microsoft.Online.SharePoint.PowerShell -DisableNameChecking
|
||||||
|
Connect-SPOService -Url ('https://' + $spoTenantName + '-admin.sharepoint.com')
|
||||||
|
|
||||||
|
# Create the common base URL for OneDrive for Business
|
||||||
|
$spoBaseWildcard = 'https://' + $spoTenantName + '-my.sharepoint.com/personal/*'
|
||||||
|
|
||||||
|
# Get a list of all 'personal' sites (e.g.: OneDrive for Business sites) within the tenant
|
||||||
|
Get-SPOSite -Limit all -IncludePersonalSite $true | Where-Object {$_.Url -like $spoBaseWildcard} | Select-Object Owner,URL,StorageQuota | Format-Table -AutoSize
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
# Increase the size of specific users' OneDrive for Business accounts
|
||||||
|
#
|
||||||
|
# Requires the Sharepoint Online PowerShell module to be installed
|
||||||
|
#
|
||||||
|
|
||||||
|
# The name of your Office 365 organization
|
||||||
|
# This can be found in your Sharepoint URL before the '-my', eg: https://thecompany-my.sharepoint.com/
|
||||||
|
$spoTenantName=''
|
||||||
|
|
||||||
|
# The size you want to increase to in TB.
|
||||||
|
$newSize = 1
|
||||||
|
|
||||||
|
# The list of UPNs for the accounts you wich to add the secondary admin to (list can contain a single item if required)
|
||||||
|
$userList = @('','')
|
||||||
|
|
||||||
|
# Connect to Sharepoint Online
|
||||||
|
Import-Module Microsoft.Online.SharePoint.PowerShell -DisableNameChecking
|
||||||
|
Connect-SPOService -Url ('https://' + $spoTenantName + '-admin.sharepoint.com')
|
||||||
|
|
||||||
|
# Create the base URL for OneDrive for Business
|
||||||
|
$spoBaseURL = 'https://' + $spoTenantName + '-my.sharepoint.com/personal/'
|
||||||
|
|
||||||
|
# Convert new size to bytes
|
||||||
|
$newBytes = $newSize * 1048576
|
||||||
|
|
||||||
|
# Increase size for each user in list
|
||||||
|
foreach ($userUPN in $userList) {
|
||||||
|
$spoURL = $spoBaseURL + ($userUPN.ToLower() -replace "[@.]", "_")
|
||||||
|
try {
|
||||||
|
$currentBytes = (Get-SPOSite -Identity $spoURL -ErrorAction:Stop).StorageQuota
|
||||||
|
if ($currentBytes -gt $newBytes) {
|
||||||
|
try {
|
||||||
|
Set-SPOSite -Identity $spoURL -StorageQuota $newBytes -ErrorAction:Stop
|
||||||
|
Write-Output -InputObject ('Updated OneDrive account limit for ' + $userUPN + ' to ' + $newSize + 'TB.')
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Output -InputObject ('Failed to update OneDrive account limit for ' + $userUPN + '.')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Write-Output -InputObject ('OneDrive account limit for ' + $userUPN + ' already equal to or higher than ' + $newSize + 'TB.')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Output -InputObject ('No OneDrive account found for ' + $userUPN + '.')
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
# Increase the size of all users' OneDrive for Business accounts
|
||||||
|
#
|
||||||
|
# Requires the Sharepoint Online PowerShell module to be installed
|
||||||
|
#
|
||||||
|
|
||||||
|
# The name of your Office 365 organization
|
||||||
|
# This can be found in your Sharepoint URL before the '-my', eg: https://thecompany-my.sharepoint.com/
|
||||||
|
$spoTenantName=''
|
||||||
|
|
||||||
|
# The size you want to increase to in TB.
|
||||||
|
$newSize = 1
|
||||||
|
|
||||||
|
# Connect to Sharepoint Online
|
||||||
|
Import-Module Microsoft.Online.SharePoint.PowerShell -DisableNameChecking
|
||||||
|
Connect-SPOService -Url ('https://' + $spoTenantName + '-admin.sharepoint.com')
|
||||||
|
|
||||||
|
# Create the common base URL for OneDrive for Business
|
||||||
|
$spoBaseWildcard = 'https://' + $spoTenantName + '-my.sharepoint.com/personal/*'
|
||||||
|
|
||||||
|
# Convert new size to bytes
|
||||||
|
$newBytes = $newSize * 1048576
|
||||||
|
|
||||||
|
# Get a list of all personal sites within the tenant.
|
||||||
|
$personalSites = Get-SPOSite -Limit all -IncludePersonalSite $true | Where-Object {$_.Url -like $spoBaseWildcard}
|
||||||
|
|
||||||
|
# Increase size for each user in turn.
|
||||||
|
foreach ($personalSite in $personalSites) {
|
||||||
|
$spoURL = $personalSite.Url
|
||||||
|
$userUPN = $personalSite.Owner
|
||||||
|
$currentBytes = $personalSite.StorageQuota
|
||||||
|
|
||||||
|
if ($currentBytes -lt $newBytes) {
|
||||||
|
try {
|
||||||
|
Set-SPOSite -Identity $spoURL -StorageQuota $newBytes -ErrorAction:Stop
|
||||||
|
Write-Output -InputObject ('Updated OneDrive account limit for ' + $userUPN + ' to ' + $newSize + 'TB.')
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Output -InputObject ('Failed to update OneDrive account limit for ' + $userUPN + '.')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Write-Output -InputObject ('OneDrive account limit for ' + $userUPN + ' already equal to or higher than ' + $newSize + 'TB.')
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
# Add a collection administrator to your users' OneDrive for Business accounts
|
||||||
|
#
|
||||||
|
# Requires the Sharepoint Online PowerShell module to be installed
|
||||||
|
#
|
||||||
|
|
||||||
|
# The name of your Office 365 organization
|
||||||
|
# This can be found in your Sharepoint URL before the '-my', eg: https://thecompany-my.sharepoint.com/
|
||||||
|
$spoTenantName=''
|
||||||
|
|
||||||
|
# The UPN of the account you wish to add as a secondary admin
|
||||||
|
$secondaryAdminUPN = ''
|
||||||
|
|
||||||
|
# The list of UPNs for the accounts you wich to add the secondary admin to
|
||||||
|
$userList = @('','')
|
||||||
|
|
||||||
|
# Connect to Sharepoint Online
|
||||||
|
Import-Module Microsoft.Online.SharePoint.PowerShell -DisableNameChecking
|
||||||
|
Connect-SPOService -Url ('https://' + $spoTenantName + '-admin.sharepoint.com')
|
||||||
|
|
||||||
|
# Create the base URL for OneDrive for Business
|
||||||
|
$spoBaseURL = 'https://' + $spoTenantName + '-my.sharepoint.com/personal/'
|
||||||
|
|
||||||
|
# Add secondary admin to each user in the list
|
||||||
|
foreach ($userUPN in $userList) {
|
||||||
|
$spoURL = $spoBaseURL + ($userUPN.ToLower() -replace "[@.]", "_")
|
||||||
|
Set-SPOUser -Site $spoURL -LoginName $secondaryAdminUPN -IsSiteCollectionAdmin $true -ErrorAction:Continue
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
# Remove a collection administrator from your users' OneDrive for Business accounts
|
||||||
|
#
|
||||||
|
# Requires the Sharepoint Online PowerShell module to be installed
|
||||||
|
#
|
||||||
|
|
||||||
|
# The name of your Office 365 organization
|
||||||
|
# This can be found in your Sharepoint URL before the '-my', eg: https://thecompany-my.sharepoint.com/
|
||||||
|
$spoTenantName=''
|
||||||
|
|
||||||
|
# The UPN of the account you wish to add as a secondary admin
|
||||||
|
$secondaryAdminUPN = ''
|
||||||
|
|
||||||
|
# The list of UPNs for the accounts you wich to add the secondary admin to
|
||||||
|
$userList = @('','')
|
||||||
|
|
||||||
|
# Connect to Sharepoint Online
|
||||||
|
Import-Module Microsoft.Online.SharePoint.PowerShell -DisableNameChecking
|
||||||
|
Connect-SPOService -Url ('https://' + $spoTenantName + '-admin.sharepoint.com')
|
||||||
|
|
||||||
|
# Create the base URL for OneDrive for Business
|
||||||
|
$spoBaseURL = 'https://' + $spoTenantName + '-my.sharepoint.com/personal/'
|
||||||
|
|
||||||
|
# Add secondary admin to each user in the list
|
||||||
|
foreach ($userUPN in $userList) {
|
||||||
|
$spoURL = $spoBaseURL + ($userUPN.ToLower() -replace "[@.]", "_")
|
||||||
|
Set-SPOUser -Site $spoURL -LoginName $secondaryAdminUPN -IsSiteCollectionAdmin $false -ErrorAction:Continue
|
||||||
|
Remove-SPOUser -Site $spoURL -LoginName $secondaryAdminUPN
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
# Retrive a list of all OneDrive for Business sites, check their size and reset it if it is less than the default
|
||||||
|
#
|
||||||
|
# Requires the Sharepoint Online PowerShell module to be installed
|
||||||
|
#
|
||||||
|
|
||||||
|
# The name of your Office 365 organization
|
||||||
|
# This can be found in your Sharepoint URL before the '-my', eg: https://thecompany-my.sharepoint.com/
|
||||||
|
$spoTenantName=''
|
||||||
|
|
||||||
|
# Connect to Sharepoint Online
|
||||||
|
Import-Module Microsoft.Online.SharePoint.PowerShell -DisableNameChecking
|
||||||
|
Connect-SPOService -Url ('https://' + $spoTenantName + '-admin.sharepoint.com')
|
||||||
|
|
||||||
|
# Create the common base URL for OneDrive for Business
|
||||||
|
$spoBaseWildcard = 'https://' + $spoTenantName + '-my.sharepoint.com/personal/*'
|
||||||
|
|
||||||
|
# Get the default quota for the tenant
|
||||||
|
$defaultQuota = (Get-SPOTenant).OneDriveStorageQuota
|
||||||
|
|
||||||
|
# Get a list of all 'personal' sites (e.g.: OneDrive for Business sites) within the tenant
|
||||||
|
$usersToReset = Get-SPOSite -Limit all -IncludePersonalSite $true | Where-Object {$_.Url -like $spoBaseWildcard -and $_.StorageQuota -lt $defaultQuota}
|
||||||
|
|
||||||
|
# Get a list of all 'personal' sites (e.g.: OneDrive for Business sites) within the tenant
|
||||||
|
foreach ($userToReset in $usersToReset) {
|
||||||
|
Write-Output -InputObject ('Resetting user ' + $userToReset.Owner)
|
||||||
|
Set-SPOSite -Identity $userToReset.Url -StorageQuotaReset
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
# Script to go through all SPO sites and check if a user is assigned to them
|
||||||
|
#
|
||||||
|
|
||||||
|
# This is the name of your tenant, as shown in the URL when accessing SharePoint online
|
||||||
|
# E.g.: https://<tenant-name>.sharepoint.com/
|
||||||
|
$spoTenantName = ''
|
||||||
|
|
||||||
|
# Who are we looking for? (uses a wildcard like comparison)
|
||||||
|
$loginNameLike = ''
|
||||||
|
|
||||||
|
# Connect to Sharepoint Online
|
||||||
|
Import-Module Microsoft.Online.SharePoint.PowerShell -DisableNameChecking
|
||||||
|
Connect-SPOService -Url ('https://' + $spoTenantName + '-admin.sharepoint.com')
|
||||||
|
|
||||||
|
# Get all SharePoint Online sites
|
||||||
|
$allSpoSites = Get-SPOSite -Limit All
|
||||||
|
|
||||||
|
# Run through the sites...
|
||||||
|
foreach ($spoSite in $allSpoSites) {
|
||||||
|
# Try to get a list of users for the current site - requires owner access to the site.
|
||||||
|
try {
|
||||||
|
$spoUsers = Get-SPOUser -Site $spoSite.Url -Limit All -ErrorAction:Stop | Where-Object {$_.LoginName -like $loginNameLike}
|
||||||
|
$usersRetrived = $true
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
$spoUsers = ''
|
||||||
|
$usersRetrived = $false
|
||||||
|
}
|
||||||
|
|
||||||
|
# If the previous try didn't fail then work on the retrived list
|
||||||
|
if ($usersRetrived) {
|
||||||
|
# If the list isn't empty, then set the output message to the userlogin name and site URL.
|
||||||
|
if ([string]$spoUsers.Count -gt 0) {
|
||||||
|
foreach ($spoUser in $spoUsers) {
|
||||||
|
$outputMessage = 'User ' + $spoUser.LoginName + ' found in site ' + $spoSite.Url
|
||||||
|
}
|
||||||
|
}
|
||||||
|
# Else (if the list is empty) set the output message to reflect that the user wasn't found in that group.
|
||||||
|
else {
|
||||||
|
$outputMessage = 'User not found in site ' + $spoSite.Url
|
||||||
|
}
|
||||||
|
}
|
||||||
|
# If users weren't retrived then set the output message to reflect that.
|
||||||
|
else {
|
||||||
|
$outputMessage = 'Unable to get users from site: ' + $spoSite.Url
|
||||||
|
}
|
||||||
|
# Finally write out the output message.
|
||||||
|
Write-Output -InputObject $outputMessage
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
# Script to go through all SPO sites and check if a user is assigned to them
|
||||||
|
#
|
||||||
|
|
||||||
|
# This is the name of your tenant, as shown in the URL when accessing SharePoint online
|
||||||
|
# E.g.: https://<tenant-name>.sharepoint.com/
|
||||||
|
$spoTenantName = ''
|
||||||
|
|
||||||
|
# Who are we looking for? (uses a wildcard like comparison)
|
||||||
|
$siteNameLike = '*'
|
||||||
|
|
||||||
|
# Connect to Sharepoint Online
|
||||||
|
Import-Module Microsoft.Online.SharePoint.PowerShell -DisableNameChecking
|
||||||
|
Connect-SPOService -Url ('https://' + $spoTenantName + '-admin.sharepoint.com')
|
||||||
|
|
||||||
|
# Get all SharePoint Online sites
|
||||||
|
$allSpoSites = Get-SPOSite -Limit All | Where-Object {$_.Title -like $siteNameLike}
|
||||||
|
|
||||||
|
Get-SpoSiteGroup
|
||||||
|
|
||||||
|
|
||||||
|
# Run through the sites...
|
||||||
|
foreach ($spoSite in $allSpoSites) {
|
||||||
|
# Try to get a list of users for the current site - requires owner access to the site.
|
||||||
|
$allSiteGroups = Get-SpoSiteGroup -Site $spoSite.Url
|
||||||
|
foreach ($siteGroup in $allSiteGroups) {
|
||||||
|
Write-Host
|
||||||
|
$siteGroup | Select-Object -ExpandProperty Users
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$spoUsers | Export-Csv C:\Temp\SISLive-Sharepoint-Site.csv -NoTypeInformation
|
||||||
|
|
||||||
|
$allSpoSites
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
# Enable all licenced users for Microsoft audio conferencing
|
||||||
|
#
|
||||||
|
# In addition to the SfB module this requires the MSOnline module so it can check who is licenced.
|
||||||
|
#
|
||||||
|
|
||||||
|
# Import MSOnline module and connect
|
||||||
|
Import-Module MSOnline
|
||||||
|
Connect-MsolService
|
||||||
|
|
||||||
|
# Load the Skype Online Connector module and connect
|
||||||
|
Import-Module SkypeOnlineConnector
|
||||||
|
$sfbSession = New-CsOnlineSession
|
||||||
|
Import-PSSession -Session $sfbSession
|
||||||
|
|
||||||
|
# This can be just one licence, or several, eg. MCOMEETADV (audio conferencing), ENTERPRISEPREMIUM (E5) and MEETING_ROOM all include audio conferencing.
|
||||||
|
# List of available SKUs can be obtained with (Get-MsolAccountSku).AccountSkuId
|
||||||
|
$audioConferencingLicences = @('MCOMEETADV','ENTERPRISEPREMIUM','MEETING_ROOM')
|
||||||
|
|
||||||
|
# Find everyone who has the audio conferencing licence(s) assigned
|
||||||
|
$users = @()
|
||||||
|
foreach ($audioConferencingLicence in $audioConferencingLicences) {
|
||||||
|
$users += (Get-MsolUser -All | Where-Object {($_.licenses).AccountSkuId -match $audioConferencingLicence}).UserPrincipalName
|
||||||
|
}
|
||||||
|
|
||||||
|
# Get the default service number from your tenant
|
||||||
|
$defaultServiceNumber = (Get-CsOnlineDialInConferencingBridge -Name 'Conference Bridge').DefaultServiceNumber
|
||||||
|
|
||||||
|
# Enable conferencing for everyone on that list who doesn't already have it enabled
|
||||||
|
foreach ($user in $users) {
|
||||||
|
$currentProvider = (Get-CsOnlineDialInConferencingUserInfo -Identity $user).Provider
|
||||||
|
if ($currentProvider -ne 'Microsoft') {
|
||||||
|
Enable-CsOnlineDialInConferencingUser -ServiceNumber $defaultServiceNumber -ReplaceProvider -SendEmail
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Disconnect
|
||||||
|
Remove-PSSession -Session $sfbSession
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
# Enable Microsoft audio conferencing for a list of users
|
||||||
|
#
|
||||||
|
# Expects a CSV file with two columns, one containing the UserPrincipalName and the other containing the ServiceNumber.
|
||||||
|
#
|
||||||
|
# A list of dedicated conference numbers can be retrieved with:
|
||||||
|
# (Get-CsOnlineDialInConferencingBridge -Name 'Conference Bridge').ServiceNumbers | Where-Object {$_.IsShared -eq $false}
|
||||||
|
#
|
||||||
|
# If the service number entry for a user in the CSV file is blank then the default will be used.
|
||||||
|
#
|
||||||
|
|
||||||
|
# Where is the list of users?
|
||||||
|
$userListPath = 'C:\Temp\UserList.csv'
|
||||||
|
|
||||||
|
# Load the Skype Online Connector module and connect
|
||||||
|
Import-Module SkypeOnlineConnector
|
||||||
|
$sfbSession = New-CsOnlineSession
|
||||||
|
Import-PSSession -Session $sfbSession
|
||||||
|
|
||||||
|
# Get list of users from file
|
||||||
|
$users = Import-Csv -Path $userListPath
|
||||||
|
|
||||||
|
# Get the conference bridge details
|
||||||
|
$conferenceBridge = Get-CsOnlineDialInConferencingBridge -Name 'Conference Bridge'
|
||||||
|
|
||||||
|
# Enable conferencing for everyone on that list who doesn't already have it enabled
|
||||||
|
foreach ($user in $users) {
|
||||||
|
if (($user.ServiceNumber).Length -gt 0) {
|
||||||
|
$serviceNumber = $conferenceBridge.ServiceNumbers | Where-Object {$_.Number -eq $user.ServiceNumber}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$serviceNumber = $conferenceBridge.DefaultServiceNumber
|
||||||
|
}
|
||||||
|
$currentProvider = (Get-CsOnlineDialInConferencingUserInfo -Identity $user.UserPrincipalName).Provider
|
||||||
|
if ($currentProvider -ne 'Microsoft') {
|
||||||
|
Enable-CsOnlineDialInConferencingUser -Identity $user.UserPrincipalName -ServiceNumber $serviceNumber.Number -ReplaceProvider -SendEmail
|
||||||
|
Write-Output -InputObject ('Audio conferencing enabled for ' + $user.UserPrincipalName + ' with number ' + $serviceNumber.Number + '.')
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Write-Output -InputObject ('Audio conferencing already enabled for ' + $user.UserPrincipalName + '.')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Disconnect
|
||||||
|
Remove-PSSession -Session $sfbSession
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# Enable Modern Authentication in Skype for Business Online
|
||||||
|
#
|
||||||
|
|
||||||
|
# Load the Skype Online Connector module
|
||||||
|
Import-Module SkypeOnlineConnector
|
||||||
|
|
||||||
|
# Establish a session to Exchange Online
|
||||||
|
$sfbSession = New-CsOnlineSession
|
||||||
|
Import-PSSession -Session $sfbSession
|
||||||
|
|
||||||
|
# Enable modern authentication
|
||||||
|
Set-CsOAuthConfiguration -ClientAdalAuthOverride Allowed
|
||||||
|
|
||||||
|
# Verify the setting has changed
|
||||||
|
Get-CsOAuthConfiguration | Format-Table -AutoSize Identity,ClientAdalAuthOverride
|
||||||
|
|
||||||
|
# End the PS Session
|
||||||
|
Remove-PSSession -Session $sfbSession
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
# Script to extract conferencing details for users from the AcpInfo setting
|
||||||
|
#
|
||||||
|
|
||||||
|
# Load the Skype Online Connector module
|
||||||
|
Import-Module SkypeOnlineConnector
|
||||||
|
|
||||||
|
# Establish a session to Exchange Online
|
||||||
|
$sfbSession = New-CsOnlineSession
|
||||||
|
Import-PSSession -Session $sfbSession
|
||||||
|
|
||||||
|
# Company Wildcard
|
||||||
|
$companyWildcard = '*'
|
||||||
|
|
||||||
|
# AcpInfo Wildcard
|
||||||
|
$acpInfoWildcard = '*BT*'
|
||||||
|
|
||||||
|
# Output file
|
||||||
|
$outputFile = 'C:\Temp\ConferencingUsers.csv'
|
||||||
|
|
||||||
|
# Get all conferencing users matching the above wildcarded info
|
||||||
|
$conferencingUsers = Get-CsOnlineUser -WarningAction:SilentlyContinue -ErrorAction:SilentlyContinue | Where-Object {($_.Company -like $companyWildcard) -and ($_.AcpInfo -like $acpInfoWildcard) -and ($_.Enabled -eq $true)} | Select-Object DisplayName,UserPrincipalName,AcpInfo
|
||||||
|
|
||||||
|
# Set up user details hash table
|
||||||
|
$userDetails = @()
|
||||||
|
|
||||||
|
# Run through the users, check if they're in the exclusions list and if not then pull the details out of the AcpInfo code
|
||||||
|
foreach ($conferencingUser in $conferencingUsers) {
|
||||||
|
$tollNumber = ''
|
||||||
|
if ([string]$conferencingUser.AcpInfo -match '<tollNumber>(?<tollNumber>.*)</tollNumber>') {
|
||||||
|
$tollNumber = $Matches.tollNumber
|
||||||
|
}
|
||||||
|
|
||||||
|
$tollFreeNumber = ''
|
||||||
|
if ([string]$conferencingUser.AcpInfo -match '<tollFreeNumber>(?<tollFreeNumber>.*)</tollFreeNumber>') {
|
||||||
|
$tollFreeNumber = $Matches.tollFreeNumber
|
||||||
|
}
|
||||||
|
|
||||||
|
$participantPassCode = ''
|
||||||
|
if ([string]$conferencingUser.AcpInfo -match '<participantPassCode>(?<participantPassCode>.*)</participantPassCode>') {
|
||||||
|
$participantPassCode = $Matches.participantPassCode
|
||||||
|
}
|
||||||
|
|
||||||
|
$domain = ''
|
||||||
|
if ([string]$conferencingUser.AcpInfo -match '<domain>(?<domain>.*)</domain>') {
|
||||||
|
$domain = $Matches.domain
|
||||||
|
}
|
||||||
|
|
||||||
|
$name = ''
|
||||||
|
if ([string]$conferencingUser.AcpInfo -match '<name>(?<name>.*)</name>') {
|
||||||
|
$name = $Matches.name
|
||||||
|
}
|
||||||
|
|
||||||
|
$url = ''
|
||||||
|
if ([string]$conferencingUser.AcpInfo -match '<url>(?<url>.*)</url>') {
|
||||||
|
$url = $Matches.url
|
||||||
|
}
|
||||||
|
|
||||||
|
$userDetails += [PSCustomObject]@{
|
||||||
|
'DisplayName' = [string]$conferencingUser.DisplayName
|
||||||
|
'UserPrincipalName' = [string]$conferencingUser.UserPrincipalName
|
||||||
|
'TollNumber' = [string]$tollNumber
|
||||||
|
'TollFreeNumber' = [string]$tollFreeNumber
|
||||||
|
'ParticipantPassCode' = [string]$participantPassCode
|
||||||
|
'Domain' = [string]$domain
|
||||||
|
'Name' = [string]$name
|
||||||
|
'Url' = [string]$url
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Output to CSV file
|
||||||
|
$userDetails | Export-Csv -Path $outputFile -NoTypeInformation
|
||||||
|
|
||||||
|
# End the PS Session
|
||||||
|
Remove-PSSession -Session $sfbSession
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
# Script to bulk remove 3rd party conferencing details for all users
|
||||||
|
#
|
||||||
|
|
||||||
|
# Load the Skype Online Connector module
|
||||||
|
Import-Module SkypeOnlineConnector
|
||||||
|
|
||||||
|
# Establish a session to Exchange Online
|
||||||
|
$sfbSession = New-CsOnlineSession
|
||||||
|
Import-PSSession -Session $sfbSession -AllowClobber
|
||||||
|
|
||||||
|
# Company Wildcard
|
||||||
|
$companyWildcard = '*'
|
||||||
|
|
||||||
|
# AcpInfo to match (doesn't need to be the full name)
|
||||||
|
$acpInfoWildcard = '*BT*'
|
||||||
|
|
||||||
|
# Get all conferencing users matching the above info
|
||||||
|
$thirdPartyAcpUsers = Get-CsOnlineUser -WarningAction:SilentlyContinue -ErrorAction:SilentlyContinue | Where-Object {($_.Company -like $companyWildcard) -and ($_.AcpInfo -like $acpInfoWildcard) -and ($_.Enabled -eq $true)}
|
||||||
|
|
||||||
|
# Run through the users and remove their ACP info
|
||||||
|
foreach ($thirdPartyAcpUser in $thirdPartyAcpUsers) {
|
||||||
|
$acpInfoName = ([xml]$thirdPartyAcpUser.AcpInfo).acpInformation.name
|
||||||
|
Remove-CsUserAcp -Identity $thirdPartyAcpUser.Identity -Name $acpInfoName
|
||||||
|
}
|
||||||
|
|
||||||
|
# End the PS Session
|
||||||
|
Remove-PSSession -Session $sfbSession
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
# Script to bulk remove conferencing details for a list of users
|
||||||
|
#
|
||||||
|
|
||||||
|
# Where is the list of user UPN's?
|
||||||
|
$userListPath = 'C:\Temp\UserList.txt'
|
||||||
|
|
||||||
|
# Load the Skype Online Connector module
|
||||||
|
Import-Module SkypeOnlineConnector
|
||||||
|
|
||||||
|
# Establish a session to Exchange Online
|
||||||
|
$sfbSession = New-CsOnlineSession
|
||||||
|
Import-PSSession -Session $sfbSession -AllowClobber
|
||||||
|
|
||||||
|
# Import list of users from file
|
||||||
|
$userList = Get-Content -Path $userListPath | Sort-Object
|
||||||
|
|
||||||
|
# Get CS Online user identities for all the users in the list
|
||||||
|
$csOnlineUsers = (Get-CsOnlineUser -WarningAction SilentlyContinue | Where-Object {$_.UserPrincipalName -in $userList}).Identity
|
||||||
|
|
||||||
|
# Run through the users and remove their ACP info
|
||||||
|
foreach ($csOnlineUser in $csOnlineUsers) {
|
||||||
|
Remove-CsUserAcp -Identity $csOnlineUser
|
||||||
|
}
|
||||||
|
|
||||||
|
# End the PS Session
|
||||||
|
Remove-PSSession -Session $sfbSession
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
# Update Microsoft audio conferencing for a list of users
|
||||||
|
#
|
||||||
|
# Expects a CSV file with two columns, one containing the UserPrincipalName and the other containing the ServiceNumber.
|
||||||
|
#
|
||||||
|
# A list of dedicated conference numbers can be retrieved with:
|
||||||
|
# (Get-CsOnlineDialInConferencingBridge -Name 'Conference Bridge').ServiceNumbers | Where-Object {$_.IsShared -eq $false}
|
||||||
|
#
|
||||||
|
# If the service number entry for a user in the CSV file is blank then the default will be used.
|
||||||
|
#
|
||||||
|
|
||||||
|
# Where is the list of users?
|
||||||
|
$userListPath = 'C:\Temp\UsersToUpdate.csv'
|
||||||
|
|
||||||
|
# Load the Skype Online Connector module and connect
|
||||||
|
Import-Module SkypeOnlineConnector
|
||||||
|
$sfbSession = New-CsOnlineSession
|
||||||
|
Import-PSSession -Session $sfbSession
|
||||||
|
|
||||||
|
# Get list of users from file
|
||||||
|
$users = Import-Csv -Path $userListPath
|
||||||
|
|
||||||
|
# Get the conference bridge details
|
||||||
|
$conferenceBridge = Get-CsOnlineDialInConferencingBridge -Name 'Conference Bridge'
|
||||||
|
|
||||||
|
# Enable conferencing for everyone on that list who doesn't already have it enabled
|
||||||
|
foreach ($user in $users) {
|
||||||
|
if (($user.ServiceNumber).Length -gt 0) {
|
||||||
|
$serviceNumber = $conferenceBridge.ServiceNumbers | Where-Object {$_.Number -eq $user.ServiceNumber}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$serviceNumber = $conferenceBridge.DefaultServiceNumber
|
||||||
|
}
|
||||||
|
$currentTollNumber = (Get-CsOnlineDialInConferencingUserInfo -Identity $user.UserPrincipalName).DefaultTollNumber
|
||||||
|
if ($currentTollNumber -ne $user.ServiceNumber) {
|
||||||
|
Set-CsOnlineDialInConferencingUser -Identity $user.UserPrincipalName -ServiceNumber $serviceNumber.Number -SendEmail
|
||||||
|
Write-Output -InputObject ('Audio conferencing updated for ' + $user.UserPrincipalName + ' with number ' + $serviceNumber.Number + '.')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Disconnect
|
||||||
|
Remove-PSSession -Session $sfbSession
|
||||||
@@ -0,0 +1,130 @@
|
|||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Update the DistinguishedName Attribute for all Active Directory Users
|
||||||
|
|
||||||
|
.DESCRIPTION
|
||||||
|
Update the DistinguishedName Attribute for all Active Directory Users.
|
||||||
|
It will update the 'CN=' to match the SamAccountName
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
PS C:\> .\Convert-ADDistinguishedNameForAllUser.ps1
|
||||||
|
|
||||||
|
.NOTES
|
||||||
|
MIND THE GAP:
|
||||||
|
This will change the DistinguishedName and this might break things
|
||||||
|
|
||||||
|
It will only update/change the DistinguishedName if the 'CN=' does NOT match the SamAccountName
|
||||||
|
|
||||||
|
I created this to bulk migrate older users, they had german umlauts and other crappy character in the DistinguishedName
|
||||||
|
|
||||||
|
.LINK
|
||||||
|
https://github.com/jhochwald/PowerShell-collection/
|
||||||
|
|
||||||
|
.LINK
|
||||||
|
Get-ADUser
|
||||||
|
|
||||||
|
.LINK
|
||||||
|
Rename-ADObject
|
||||||
|
#>
|
||||||
|
[CmdletBinding(ConfirmImpact = 'Medium',
|
||||||
|
SupportsShouldProcess)]
|
||||||
|
param ()
|
||||||
|
|
||||||
|
if ($pscmdlet.ShouldProcess('All Users', 'Set'))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$AllUsers = (Get-ADUser -Filter * -Properties SamAccountName, UserPrincipalName, DistinguishedName -ErrorAction Stop | Select-Object -Property SamAccountName, UserPrincipalName, DistinguishedName | Where-Object -FilterScript {
|
||||||
|
($_.UserPrincipalName) -and ($_.SamAccountName)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
# get error record
|
||||||
|
[Management.Automation.ErrorRecord]$e = $_
|
||||||
|
|
||||||
|
# retrieve information about runtime error
|
||||||
|
$info = [PSCustomObject]@{
|
||||||
|
Exception = $e.Exception.Message
|
||||||
|
Reason = $e.CategoryInfo.Reason
|
||||||
|
Target = $e.CategoryInfo.TargetName
|
||||||
|
Script = $e.InvocationInfo.ScriptName
|
||||||
|
Line = $e.InvocationInfo.ScriptLineNumber
|
||||||
|
Column = $e.InvocationInfo.OffsetInLine
|
||||||
|
}
|
||||||
|
|
||||||
|
$info | Out-String | Write-Verbose
|
||||||
|
|
||||||
|
Write-Error -Message $e.Exception.Message -ErrorAction Stop
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
foreach ($User in $AllUsers)
|
||||||
|
{
|
||||||
|
$OldRDN = (($User | Select-Object -Property @{
|
||||||
|
l = 'OldRDN'
|
||||||
|
e = {
|
||||||
|
$_.DistinguishedName.split(',')[0].split('=')[1]
|
||||||
|
}
|
||||||
|
}) | Select-Object -ExpandProperty OldRDN)
|
||||||
|
|
||||||
|
if ($OldRDN -ne ($User.SamAccountName))
|
||||||
|
{
|
||||||
|
# Mind the Gap: This will change the DistinguishedName and this might break things
|
||||||
|
$null = (Rename-ADObject -Identity $User.DistinguishedName -NewName $User.SamAccountName -Confirm:$false -ErrorAction Stop)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
# get error record
|
||||||
|
[Management.Automation.ErrorRecord]$e = $_
|
||||||
|
|
||||||
|
# retrieve information about runtime error
|
||||||
|
$info = [PSCustomObject]@{
|
||||||
|
Exception = $e.Exception.Message
|
||||||
|
Reason = $e.CategoryInfo.Reason
|
||||||
|
Target = $e.CategoryInfo.TargetName
|
||||||
|
Script = $e.InvocationInfo.ScriptName
|
||||||
|
Line = $e.InvocationInfo.ScriptLineNumber
|
||||||
|
Column = $e.InvocationInfo.OffsetInLine
|
||||||
|
}
|
||||||
|
|
||||||
|
$info | Out-String | Write-Verbose
|
||||||
|
|
||||||
|
Write-Warning -Message $e.Exception.Message -ErrorAction Continue -WarningAction Continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#region LICENSE
|
||||||
|
<#
|
||||||
|
BSD 3-Clause License
|
||||||
|
|
||||||
|
Copyright (c) 2021, enabling Technology
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||||
|
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
#>
|
||||||
|
#endregion LICENSE
|
||||||
|
|
||||||
|
#region DISCLAIMER
|
||||||
|
<#
|
||||||
|
DISCLAIMER:
|
||||||
|
- Use at your own risk, etc.
|
||||||
|
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
|
||||||
|
- This is a third-party Software
|
||||||
|
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
|
||||||
|
- The Software is not supported by Microsoft Corp (MSFT)
|
||||||
|
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
|
||||||
|
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
|
||||||
|
#>
|
||||||
|
#endregion DISCLAIMER
|
||||||
@@ -0,0 +1,399 @@
|
|||||||
|
function Copy-ADGroupUserMembership
|
||||||
|
{
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Copy the membership of a given group to another group in Active Directory
|
||||||
|
|
||||||
|
.DESCRIPTION
|
||||||
|
Copy the membership of a given group to another group in Active Directory.
|
||||||
|
By default only the members of the Source Group will be copied to the Target Group.
|
||||||
|
If the Parameter FULL is used, the members of the Target Group that are not a member of the Source Group will be removed.
|
||||||
|
If the Parameter SYNC is used, the Membership is synced between both groups. If a User is Member of the Target Group only, this membership will be copied to the Source as well.
|
||||||
|
|
||||||
|
.PARAMETER SourceGroup
|
||||||
|
Source-Group Object.
|
||||||
|
|
||||||
|
Specifies an Active Directory group object by providing one of the following values. The identifier in
|
||||||
|
parentheses is the LDAP display name for the attribute.
|
||||||
|
|
||||||
|
Distinguished Name
|
||||||
|
|
||||||
|
Example: CN=saradavisreports,OU=europe,CN=users,DC=corp,DC=contoso,DC=com
|
||||||
|
|
||||||
|
GUID (objectGUID)
|
||||||
|
|
||||||
|
Example: 599c3d2e-f72d-4d20-8a88-030d99495f20
|
||||||
|
|
||||||
|
Security Identifier (objectSid)
|
||||||
|
|
||||||
|
Example: S-1-5-21-3165297888-301567370-576410423-1103
|
||||||
|
|
||||||
|
Security Accounts Manager (SAM) Account Name (sAMAccountName)
|
||||||
|
|
||||||
|
Example: saradavisreports
|
||||||
|
|
||||||
|
The cmdlet searches the default naming context or partition to find the object. If two or more objects are
|
||||||
|
found, the cmdlet returns a non-terminating error.
|
||||||
|
|
||||||
|
This parameter can also get this object through the pipeline or you can set this parameter to an object
|
||||||
|
instance.
|
||||||
|
|
||||||
|
.PARAMETER TargetGroup
|
||||||
|
Target-Group Object.
|
||||||
|
|
||||||
|
Specifies an Active Directory group object by providing one of the following values. The identifier in
|
||||||
|
parentheses is the LDAP display name for the attribute.
|
||||||
|
|
||||||
|
Distinguished Name
|
||||||
|
|
||||||
|
Example: CN=saradavisreports,OU=europe,CN=users,DC=corp,DC=contoso,DC=com
|
||||||
|
|
||||||
|
GUID (objectGUID)
|
||||||
|
|
||||||
|
Example: 599c3d2e-f72d-4d20-8a88-030d99495f20
|
||||||
|
|
||||||
|
Security Identifier (objectSid)
|
||||||
|
|
||||||
|
Example: S-1-5-21-3165297888-301567370-576410423-1103
|
||||||
|
|
||||||
|
Security Accounts Manager (SAM) Account Name (sAMAccountName)
|
||||||
|
|
||||||
|
Example: saradavisreports
|
||||||
|
|
||||||
|
The cmdlet searches the default naming context or partition to find the object. If two or more objects are
|
||||||
|
found, the cmdlet returns a non-terminating error.
|
||||||
|
|
||||||
|
This parameter can also get this object through the pipeline or you can set this parameter to an object
|
||||||
|
instance.
|
||||||
|
|
||||||
|
.PARAMETER full
|
||||||
|
Remove all memberships from the Targewt that does NOT exist in the the Source.
|
||||||
|
|
||||||
|
.PARAMETER sync
|
||||||
|
Synchronies the group membership between Source-Group and Target-Group.
|
||||||
|
Even if a user is a member of the Target-Group only, it will be copied to the Source-Group as well.
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
PS C:\> Copy-ADGroupUserMembership -SourceGroup 'Sales' -TargetGroup 'Salesforce'
|
||||||
|
|
||||||
|
Copy the membership of the Group 'Sales' to 'Salesforce'
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
PS C:\> Copy-ADGroupUserMembership -SourceGroup 'Sales' -TargetGroup 'Salesforce' -sync
|
||||||
|
|
||||||
|
Copy the membership of the Group 'Sales' to 'Salesforce' and the other way around.
|
||||||
|
All Memberships of 'Salesforce' that does NOT exist in 'Sales' will be created in 'Sales' as well.
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
PS C:\> Copy-ADGroupUserMembership -SourceGroup 'Sales' -TargetGroup 'Salesforce' -full
|
||||||
|
|
||||||
|
Copy the membership of the Group 'Sales' to 'Salesforce'.
|
||||||
|
All Memberships of 'Salesforce' that does NOT exist in 'Sales' will be removed.
|
||||||
|
|
||||||
|
.NOTES
|
||||||
|
Initial AIT version of the function
|
||||||
|
|
||||||
|
.LINK
|
||||||
|
https://github.com/jhochwald/PowerShell-collection/
|
||||||
|
|
||||||
|
.LINK
|
||||||
|
Get-ADGroupMember
|
||||||
|
|
||||||
|
.LINK
|
||||||
|
Remove-ADGroupMember
|
||||||
|
|
||||||
|
.LINK
|
||||||
|
Add-ADGroupMember
|
||||||
|
|
||||||
|
.LINK
|
||||||
|
Copy-ADUserGroupMemberships
|
||||||
|
#>
|
||||||
|
[CmdletBinding(DefaultParameterSetName = 'default',
|
||||||
|
ConfirmImpact = 'Low',
|
||||||
|
SupportsShouldProcess)]
|
||||||
|
param
|
||||||
|
(
|
||||||
|
[Parameter(Mandatory,
|
||||||
|
ValueFromPipeline,
|
||||||
|
ValueFromPipelineByPropertyName,
|
||||||
|
Position = 0,
|
||||||
|
HelpMessage = 'Source-Group Object.')]
|
||||||
|
[ValidateNotNullOrEmpty()]
|
||||||
|
[Alias('Source')]
|
||||||
|
[string]
|
||||||
|
$SourceGroup,
|
||||||
|
[Parameter(Mandatory,
|
||||||
|
ValueFromPipeline,
|
||||||
|
ValueFromPipelineByPropertyName,
|
||||||
|
Position = 1,
|
||||||
|
HelpMessage = 'Target-Group Object.')]
|
||||||
|
[ValidateNotNullOrEmpty()]
|
||||||
|
[Alias('Target')]
|
||||||
|
[string]
|
||||||
|
$TargetGroup,
|
||||||
|
[Parameter(ParameterSetName = 'full',
|
||||||
|
ValueFromPipeline,
|
||||||
|
ValueFromPipelineByPropertyName,
|
||||||
|
Position = 2)]
|
||||||
|
[Alias('RemoveTargetOnlyMembers')]
|
||||||
|
[switch]
|
||||||
|
$full = $null,
|
||||||
|
[Parameter(ParameterSetName = 'sync',
|
||||||
|
ValueFromPipeline,
|
||||||
|
ValueFromPipelineByPropertyName,
|
||||||
|
Position = 2)]
|
||||||
|
[Alias('MakeFullSync')]
|
||||||
|
[switch]
|
||||||
|
$sync = $null
|
||||||
|
)
|
||||||
|
|
||||||
|
begin
|
||||||
|
{
|
||||||
|
if ($pscmdlet.ShouldProcess('Groups', 'Get information from Active Directory'))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$SourceMembers = (Get-ADGroupMember -Identity $SourceGroup -ErrorAction Stop | Select-Object -ExpandProperty distinguishedName | Sort-Object)
|
||||||
|
$TargetMembers = (Get-ADGroupMember -Identity $TargetGroup -ErrorAction Stop | Select-Object -ExpandProperty distinguishedName | Sort-Object)
|
||||||
|
|
||||||
|
# Check if we have any diferences
|
||||||
|
if (($SourceMembers) -and ($TargetMembers))
|
||||||
|
{
|
||||||
|
# Yep, there are differences
|
||||||
|
$Differences = (Compare-Object -ReferenceObject $SourceMembers -DifferenceObject $TargetMembers)
|
||||||
|
}
|
||||||
|
elseif (($SourceMembers) -and (-not($TargetMembers)))
|
||||||
|
{
|
||||||
|
# Target has no members
|
||||||
|
$Differences = 'SourceOnly'
|
||||||
|
}
|
||||||
|
elseif (-not($SourceMembers))
|
||||||
|
{
|
||||||
|
# Source has no members
|
||||||
|
Write-Error -Message ('{0} has no members!' -f $SourceGroup) -ErrorAction Stop
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
# Nope, there are no differences
|
||||||
|
$Differences = $null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
# get error record
|
||||||
|
[Management.Automation.ErrorRecord]$e = $_
|
||||||
|
|
||||||
|
# retrieve information about runtime error
|
||||||
|
$info = [PSCustomObject]@{
|
||||||
|
Exception = $e.Exception.Message
|
||||||
|
Reason = $e.CategoryInfo.Reason
|
||||||
|
Target = $e.CategoryInfo.TargetName
|
||||||
|
Script = $e.InvocationInfo.ScriptName
|
||||||
|
Line = $e.InvocationInfo.ScriptLineNumber
|
||||||
|
Column = $e.InvocationInfo.OffsetInLine
|
||||||
|
}
|
||||||
|
|
||||||
|
$info | Out-String | Write-Verbose
|
||||||
|
|
||||||
|
Write-Error -Message $e.Exception.Message -ErrorAction Stop
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
process
|
||||||
|
{
|
||||||
|
|
||||||
|
switch ($pscmdlet.ParameterSetName)
|
||||||
|
{
|
||||||
|
'full'
|
||||||
|
{
|
||||||
|
if ($pscmdlet.ShouldProcess($TargetGroup, 'Set'))
|
||||||
|
{
|
||||||
|
if ($Differences)
|
||||||
|
{
|
||||||
|
Write-Verbose -Message 'Remove Target-User from all groups where the Source-User is not a member of.'
|
||||||
|
|
||||||
|
$TargetOnlyMembers = ($Differences | Where-Object -Property SideIndicator -EQ -Value '=>')
|
||||||
|
|
||||||
|
if ($TargetOnlyMembers)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
foreach ($TargetOnlyMember in $TargetOnlyMembers.InputObject)
|
||||||
|
{
|
||||||
|
Write-Verbose -Message ('Process: {0}' -f $TargetOnlyMember)
|
||||||
|
|
||||||
|
$paramRemoveADGroupMember = @{
|
||||||
|
Identity = $TargetGroup
|
||||||
|
Members = $TargetOnlyMember
|
||||||
|
ErrorAction = 'Stop'
|
||||||
|
Confirm = $false
|
||||||
|
}
|
||||||
|
$null = (Remove-ADGroupMember @paramRemoveADGroupMember -Verbose)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
# get error record
|
||||||
|
[Management.Automation.ErrorRecord]$e = $_
|
||||||
|
|
||||||
|
# retrieve information about runtime error
|
||||||
|
$info = [PSCustomObject]@{
|
||||||
|
Exception = $e.Exception.Message
|
||||||
|
Reason = $e.CategoryInfo.Reason
|
||||||
|
Target = $e.CategoryInfo.TargetName
|
||||||
|
Script = $e.InvocationInfo.ScriptName
|
||||||
|
Line = $e.InvocationInfo.ScriptLineNumber
|
||||||
|
Column = $e.InvocationInfo.OffsetInLine
|
||||||
|
}
|
||||||
|
|
||||||
|
$info | Out-String | Write-Verbose
|
||||||
|
|
||||||
|
Write-Warning -Message $e.Exception.Message -ErrorAction Continue -WarningAction Continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Write-Verbose -Message 'No group difference found where the Target-User is a member and Source-User is not.'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'sync'
|
||||||
|
{
|
||||||
|
if ($pscmdlet.ShouldProcess($SourceGroup, 'Set'))
|
||||||
|
{
|
||||||
|
if ($Differences)
|
||||||
|
{
|
||||||
|
Write-Verbose -Message 'Make the Source-user a Member of all Groups only the Target-User is a member of.'
|
||||||
|
|
||||||
|
$TargetOnlyMembers = ($Differences | Where-Object -Property SideIndicator -EQ -Value '=>')
|
||||||
|
|
||||||
|
if ($TargetOnlyMembers)
|
||||||
|
{
|
||||||
|
Write-Verbose -Message ('Process: {0}' -f $TargetOnlyMembers)
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$paramAddADGroupMember = @{
|
||||||
|
Identity = $SourceGroup
|
||||||
|
Members = $TargetOnlyMembers.InputObject
|
||||||
|
ErrorAction = 'Stop'
|
||||||
|
Confirm = $false
|
||||||
|
}
|
||||||
|
$null = (Add-ADGroupMember @paramAddADGroupMember)
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
# get error record
|
||||||
|
[Management.Automation.ErrorRecord]$e = $_
|
||||||
|
|
||||||
|
# retrieve information about runtime error
|
||||||
|
$info = [PSCustomObject]@{
|
||||||
|
Exception = $e.Exception.Message
|
||||||
|
Reason = $e.CategoryInfo.Reason
|
||||||
|
Target = $e.CategoryInfo.TargetName
|
||||||
|
Script = $e.InvocationInfo.ScriptName
|
||||||
|
Line = $e.InvocationInfo.ScriptLineNumber
|
||||||
|
Column = $e.InvocationInfo.OffsetInLine
|
||||||
|
}
|
||||||
|
|
||||||
|
$info | Out-String | Write-Verbose
|
||||||
|
|
||||||
|
Write-Warning -Message $e.Exception.Message -ErrorAction Continue -WarningAction Continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Write-Verbose -Message 'No group difference found where the Target-User is a member and Source-User is not.'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'default'
|
||||||
|
{
|
||||||
|
# Do nothing special
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($pscmdlet.ShouldProcess($TargetGroup, 'Set'))
|
||||||
|
{
|
||||||
|
if ($Differences)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Write-Verbose -Message 'Process all Source-Group only members.'
|
||||||
|
|
||||||
|
$paramAddADGroupMember = @{
|
||||||
|
Identity = $TargetGroup
|
||||||
|
ErrorAction = 'Stop'
|
||||||
|
Confirm = $false
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($Differences -eq 'SourceOnly')
|
||||||
|
{
|
||||||
|
# Target has no members
|
||||||
|
$paramAddADGroupMember.Members = $SourceMembers
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$paramAddADGroupMember.Members = ($Differences | Where-Object -Property SideIndicator -EQ -Value '<=' | Select-Object -ExpandProperty InputObject)
|
||||||
|
}
|
||||||
|
|
||||||
|
$null = (Add-ADGroupMember @paramAddADGroupMember)
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
# get error record
|
||||||
|
[Management.Automation.ErrorRecord]$e = $_
|
||||||
|
|
||||||
|
# retrieve information about runtime error
|
||||||
|
$info = [PSCustomObject]@{
|
||||||
|
Exception = $e.Exception.Message
|
||||||
|
Reason = $e.CategoryInfo.Reason
|
||||||
|
Target = $e.CategoryInfo.TargetName
|
||||||
|
Script = $e.InvocationInfo.ScriptName
|
||||||
|
Line = $e.InvocationInfo.ScriptLineNumber
|
||||||
|
Column = $e.InvocationInfo.OffsetInLine
|
||||||
|
}
|
||||||
|
|
||||||
|
$info | Out-String | Write-Verbose
|
||||||
|
|
||||||
|
Write-Warning -Message $e.Exception.Message -ErrorAction Continue -WarningAction Continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#region LICENSE
|
||||||
|
<#
|
||||||
|
BSD 3-Clause License
|
||||||
|
|
||||||
|
Copyright (c) 2021, enabling Technology
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||||
|
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
#>
|
||||||
|
#endregion LICENSE
|
||||||
|
|
||||||
|
#region DISCLAIMER
|
||||||
|
<#
|
||||||
|
DISCLAIMER:
|
||||||
|
- Use at your own risk, etc.
|
||||||
|
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
|
||||||
|
- This is a third-party Software
|
||||||
|
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
|
||||||
|
- The Software is not supported by Microsoft Corp (MSFT)
|
||||||
|
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
|
||||||
|
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
|
||||||
|
#>
|
||||||
|
#endregion DISCLAIMER
|
||||||
@@ -0,0 +1,385 @@
|
|||||||
|
function Copy-ADUserGroupMembership
|
||||||
|
{
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Copy group memberships from a given Source-User to a Target-User in Active Directory
|
||||||
|
|
||||||
|
.DESCRIPTION
|
||||||
|
Copy group memberships from a given Source-User to a Target-User in Active Directory.
|
||||||
|
The function can also remove the Target-User from all groups where the Source-User is not a member off (optional) or make the Source-User a member of all groups where only the Target-User is a member of.
|
||||||
|
|
||||||
|
.PARAMETER SourceUser
|
||||||
|
Source-User Object.
|
||||||
|
|
||||||
|
Specifies an Active Directory user object by providing one of the following property values.
|
||||||
|
The identifier in parentheses is the LDAP display name for the attribute.
|
||||||
|
|
||||||
|
Distinguished Name
|
||||||
|
|
||||||
|
Example: CN=SaraDavis,CN=Europe,CN=Users,DC=corp,DC=contoso,DC=com
|
||||||
|
|
||||||
|
GUID (objectGUID)
|
||||||
|
|
||||||
|
Example: 599c3d2e-f72d-4d20-8a88-030d99495f20
|
||||||
|
|
||||||
|
Security Identifier (objectSid)
|
||||||
|
|
||||||
|
Example: S-1-5-21-3165297888-301567370-576410423-1103
|
||||||
|
|
||||||
|
SAM account name (sAMAccountName)
|
||||||
|
|
||||||
|
Example: saradavis
|
||||||
|
|
||||||
|
.PARAMETER TargetUser
|
||||||
|
Target-User Object.
|
||||||
|
|
||||||
|
Specifies an Active Directory user object by providing one of the following property values.
|
||||||
|
The identifier in parentheses is the LDAP display name for the attribute.
|
||||||
|
|
||||||
|
Distinguished Name
|
||||||
|
|
||||||
|
Example: CN=SaraDavis,CN=Europe,CN=Users,DC=corp,DC=contoso,DC=com
|
||||||
|
|
||||||
|
GUID (objectGUID)
|
||||||
|
|
||||||
|
Example: 599c3d2e-f72d-4d20-8a88-030d99495f20
|
||||||
|
|
||||||
|
Security Identifier (objectSid)
|
||||||
|
|
||||||
|
Example: S-1-5-21-3165297888-301567370-576410423-1103
|
||||||
|
|
||||||
|
SAM account name (sAMAccountName)
|
||||||
|
|
||||||
|
Example: saradavis
|
||||||
|
|
||||||
|
.PARAMETER full
|
||||||
|
Remove the Target User from all groups where the Source-User is not a member of.
|
||||||
|
|
||||||
|
.PARAMETER sync
|
||||||
|
Make the Source-User a member of all Groups where only the Target-User is a member of.
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
PS C:\> Copy-ADUserGroupMembership -SourceUser 'johndoe' -TargetUser 'janedoe'
|
||||||
|
|
||||||
|
Make janedoe a member of all groups where johndoe is a member of. Existing group memberships of janedoe will NOT be removed.
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
PS C:\> Copy-ADUserGroupMembership -SourceUser 'johndoe' -TargetUser 'janedoe' -full
|
||||||
|
|
||||||
|
Make janedoe a member of all groups where johndoe is a member of. Existing group memberships of janedoe WILL be removed.
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
PS C:\> Copy-ADUserGroupMembership -SourceUser 'johndoe' -TargetUser 'janedoe' -sync
|
||||||
|
|
||||||
|
Make janedoe a member of all groups where johndoe is a member of. Existing group memberships of janedoe WILL be applied to johndoe.
|
||||||
|
Lets call this a reverse Full Sync :)
|
||||||
|
|
||||||
|
.NOTES
|
||||||
|
Initial AIT version of the function
|
||||||
|
|
||||||
|
.LINK
|
||||||
|
https://github.com/jhochwald/PowerShell-collection/
|
||||||
|
|
||||||
|
.LINK
|
||||||
|
Get-ADUser
|
||||||
|
|
||||||
|
.LINK
|
||||||
|
Remove-ADGroupMember
|
||||||
|
|
||||||
|
.LINK
|
||||||
|
Add-ADGroupMember
|
||||||
|
|
||||||
|
.LINK
|
||||||
|
Copy-ADGroupUserMemberships
|
||||||
|
#>
|
||||||
|
[CmdletBinding(DefaultParameterSetName = 'default',
|
||||||
|
SupportsShouldProcess)]
|
||||||
|
param
|
||||||
|
(
|
||||||
|
[Parameter(Mandatory,
|
||||||
|
ValueFromPipeline,
|
||||||
|
ValueFromPipelineByPropertyName,
|
||||||
|
Position = 0,
|
||||||
|
HelpMessage = 'Source-User Object.')]
|
||||||
|
[ValidateNotNullOrEmpty()]
|
||||||
|
[Alias('Source')]
|
||||||
|
[string]
|
||||||
|
$SourceUser,
|
||||||
|
[Parameter(Mandatory,
|
||||||
|
ValueFromPipeline,
|
||||||
|
ValueFromPipelineByPropertyName,
|
||||||
|
Position = 1,
|
||||||
|
HelpMessage = 'Target-User Object.')]
|
||||||
|
[ValidateNotNullOrEmpty()]
|
||||||
|
[Alias('Target')]
|
||||||
|
[string]
|
||||||
|
$TargetUser,
|
||||||
|
[Parameter(ParameterSetName = 'full',
|
||||||
|
ValueFromPipeline,
|
||||||
|
ValueFromPipelineByPropertyName,
|
||||||
|
Position = 2)]
|
||||||
|
[Alias('RemoveTargetOnlyGroups')]
|
||||||
|
[switch]
|
||||||
|
$full = $null,
|
||||||
|
[Parameter(ParameterSetName = 'sync',
|
||||||
|
ValueFromPipeline,
|
||||||
|
ValueFromPipelineByPropertyName,
|
||||||
|
Position = 2)]
|
||||||
|
[Alias('MakeFullSync')]
|
||||||
|
[switch]
|
||||||
|
$sync = $null
|
||||||
|
)
|
||||||
|
|
||||||
|
begin
|
||||||
|
{
|
||||||
|
if ($pscmdlet.ShouldProcess('User', 'Get information from Active Directory'))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
# Get the Target-User
|
||||||
|
$TargetUserObject = (Get-ADUser -Identity $TargetUser -Properties memberOf -ErrorAction Stop)
|
||||||
|
|
||||||
|
# Get the Source-User
|
||||||
|
$SourceUserObject = (Get-ADUser -Identity $SourceUser -Properties memberOf -ErrorAction Stop)
|
||||||
|
|
||||||
|
# Sort and save the information we collected above
|
||||||
|
$SourceUserMembership = ($SourceUserObject.MemberOf | Sort-Object)
|
||||||
|
$TargetUserMembership = ($TargetUserObject.MemberOf | Sort-Object)
|
||||||
|
|
||||||
|
# Check if we have any diferences
|
||||||
|
if (($SourceUserMembership) -and ($TargetUserMembership))
|
||||||
|
{
|
||||||
|
# Yep, there are differences
|
||||||
|
$Differences = (Compare-Object -ReferenceObject $SourceUserMembership -DifferenceObject $TargetUserMembership)
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
# Nope, there are no differences
|
||||||
|
$Differences = $null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
# get error record
|
||||||
|
[Management.Automation.ErrorRecord]$e = $_
|
||||||
|
|
||||||
|
# retrieve information about runtime error
|
||||||
|
$info = [PSCustomObject]@{
|
||||||
|
Exception = $e.Exception.Message
|
||||||
|
Reason = $e.CategoryInfo.Reason
|
||||||
|
Target = $e.CategoryInfo.TargetName
|
||||||
|
Script = $e.InvocationInfo.ScriptName
|
||||||
|
Line = $e.InvocationInfo.ScriptLineNumber
|
||||||
|
Column = $e.InvocationInfo.OffsetInLine
|
||||||
|
}
|
||||||
|
|
||||||
|
$info | Out-String | Write-Verbose
|
||||||
|
|
||||||
|
Write-Error -Message $e.Exception.Message -ErrorAction Stop
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
process
|
||||||
|
{
|
||||||
|
switch ($pscmdlet.ParameterSetName)
|
||||||
|
{
|
||||||
|
'full'
|
||||||
|
{
|
||||||
|
if ($pscmdlet.ShouldProcess($SourceUser, 'Set'))
|
||||||
|
{
|
||||||
|
if ($Differences)
|
||||||
|
{
|
||||||
|
Write-Verbose -Message 'Remove Target-User from all groups where the Source-User is not a member of.'
|
||||||
|
|
||||||
|
$TargetOnlyGroups = ($Differences | Where-Object -Property SideIndicator -EQ -Value '=>')
|
||||||
|
|
||||||
|
if ($TargetOnlyGroups)
|
||||||
|
{
|
||||||
|
foreach ($TargetOnlyGroup in $TargetOnlyGroups.InputObject)
|
||||||
|
{
|
||||||
|
Write-Verbose -Message ('Process: {0}' -f $TargetOnlyGroup)
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$paramRemoveADGroupMember = @{
|
||||||
|
Identity = $TargetOnlyGroup
|
||||||
|
Members = $TargetUser
|
||||||
|
ErrorAction = 'Stop'
|
||||||
|
Confirm = $false
|
||||||
|
}
|
||||||
|
$null = (Remove-ADGroupMember @paramRemoveADGroupMember)
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
# get error record
|
||||||
|
[Management.Automation.ErrorRecord]$e = $_
|
||||||
|
|
||||||
|
# retrieve information about runtime error
|
||||||
|
$info = [PSCustomObject]@{
|
||||||
|
Exception = $e.Exception.Message
|
||||||
|
Reason = $e.CategoryInfo.Reason
|
||||||
|
Target = $e.CategoryInfo.TargetName
|
||||||
|
Script = $e.InvocationInfo.ScriptName
|
||||||
|
Line = $e.InvocationInfo.ScriptLineNumber
|
||||||
|
Column = $e.InvocationInfo.OffsetInLine
|
||||||
|
}
|
||||||
|
|
||||||
|
$info | Out-String | Write-Verbose
|
||||||
|
|
||||||
|
Write-Warning -Message $e.Exception.Message -ErrorAction Continue -WarningAction Continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Write-Verbose -Message 'No group difference fround where the Target-User is a member and Source-User is not.'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'sync'
|
||||||
|
{
|
||||||
|
if ($pscmdlet.ShouldProcess($SourceUser, 'Set'))
|
||||||
|
{
|
||||||
|
if ($Differences)
|
||||||
|
{
|
||||||
|
Write-Verbose -Message 'Make the Source-user a Member of all Groups only the Target-User is a member of.'
|
||||||
|
|
||||||
|
$TargetOnlyGroups = ($Differences | Where-Object -Property SideIndicator -EQ -Value '=>')
|
||||||
|
|
||||||
|
if ($TargetOnlyGroups)
|
||||||
|
{
|
||||||
|
foreach ($TargetOnlyGroup in $TargetOnlyGroups.InputObject)
|
||||||
|
{
|
||||||
|
Write-Verbose -Message ('Process: {0}' -f $TargetOnlyGroup)
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$paramAddADGroupMember = @{
|
||||||
|
Identity = $TargetOnlyGroup
|
||||||
|
Members = $SourceUser
|
||||||
|
ErrorAction = 'Stop'
|
||||||
|
Confirm = $false
|
||||||
|
}
|
||||||
|
$null = (Add-ADGroupMember @paramAddADGroupMember)
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
# get error record
|
||||||
|
[Management.Automation.ErrorRecord]$e = $_
|
||||||
|
|
||||||
|
# retrieve information about runtime error
|
||||||
|
$info = [PSCustomObject]@{
|
||||||
|
Exception = $e.Exception.Message
|
||||||
|
Reason = $e.CategoryInfo.Reason
|
||||||
|
Target = $e.CategoryInfo.TargetName
|
||||||
|
Script = $e.InvocationInfo.ScriptName
|
||||||
|
Line = $e.InvocationInfo.ScriptLineNumber
|
||||||
|
Column = $e.InvocationInfo.OffsetInLine
|
||||||
|
}
|
||||||
|
|
||||||
|
$info | Out-String | Write-Verbose
|
||||||
|
|
||||||
|
Write-Warning -Message $e.Exception.Message -ErrorAction Continue -WarningAction Continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Write-Verbose -Message 'No group difference fround where the Target-User is a member and Source-User is not.'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'default'
|
||||||
|
{
|
||||||
|
# Do nothing special
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($pscmdlet.ShouldProcess($TargetUser, 'Set'))
|
||||||
|
{
|
||||||
|
if ($Differences)
|
||||||
|
{
|
||||||
|
$SourceOnlyGroups = ($Differences | Where-Object -Property SideIndicator -EQ -Value '<=')
|
||||||
|
|
||||||
|
if ($SourceOnlyGroups)
|
||||||
|
{
|
||||||
|
Write-Verbose -Message 'Process all Groups where only the Source-user is a member of.'
|
||||||
|
|
||||||
|
foreach ($SourceOnlyGroup in $SourceOnlyGroups.InputObject)
|
||||||
|
{
|
||||||
|
Write-Verbose -Message ('Process: {0}' -f $SourceOnlyGroup)
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$paramAddADGroupMember = @{
|
||||||
|
Identity = $SourceOnlyGroup
|
||||||
|
Members = $TargetUser
|
||||||
|
ErrorAction = 'Stop'
|
||||||
|
Confirm = $false
|
||||||
|
}
|
||||||
|
$null = (Add-ADGroupMember @paramAddADGroupMember)
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
# get error record
|
||||||
|
[Management.Automation.ErrorRecord]$e = $_
|
||||||
|
|
||||||
|
# retrieve information about runtime error
|
||||||
|
$info = [PSCustomObject]@{
|
||||||
|
Exception = $e.Exception.Message
|
||||||
|
Reason = $e.CategoryInfo.Reason
|
||||||
|
Target = $e.CategoryInfo.TargetName
|
||||||
|
Script = $e.InvocationInfo.ScriptName
|
||||||
|
Line = $e.InvocationInfo.ScriptLineNumber
|
||||||
|
Column = $e.InvocationInfo.OffsetInLine
|
||||||
|
}
|
||||||
|
|
||||||
|
$info | Out-String | Write-Verbose
|
||||||
|
|
||||||
|
Write-Warning -Message $e.Exception.Message -ErrorAction Continue -WarningAction Continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Write-Warning -Message 'No group difference fround where the Source-User is a member and Source-User is not.' -WarningAction Continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#region LICENSE
|
||||||
|
<#
|
||||||
|
BSD 3-Clause License
|
||||||
|
|
||||||
|
Copyright (c) 2021, enabling Technology
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||||
|
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
#>
|
||||||
|
#endregion LICENSE
|
||||||
|
|
||||||
|
#region DISCLAIMER
|
||||||
|
<#
|
||||||
|
DISCLAIMER:
|
||||||
|
- Use at your own risk, etc.
|
||||||
|
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
|
||||||
|
- This is a third-party Software
|
||||||
|
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
|
||||||
|
- The Software is not supported by Microsoft Corp (MSFT)
|
||||||
|
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
|
||||||
|
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
|
||||||
|
#>
|
||||||
|
#endregion DISCLAIMER
|
||||||
@@ -0,0 +1,224 @@
|
|||||||
|
function Copy-ADUserGroupMembershipSimple
|
||||||
|
{
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Copy group memberships from a given Source User to a Target User(s) in Active Directory
|
||||||
|
|
||||||
|
.DESCRIPTION
|
||||||
|
Copy group memberships from a given Source User to a Target User(s) in Active Directory.
|
||||||
|
Simple Version of Copy-ADUserGroupMemberships
|
||||||
|
|
||||||
|
.PARAMETER SourceUser
|
||||||
|
Source-User Object.
|
||||||
|
|
||||||
|
Specifies an Active Directory group object by providing one of the following values.
|
||||||
|
The identifier in parentheses is the LDAP display name for the attribute.
|
||||||
|
|
||||||
|
Distinguished Name
|
||||||
|
Example: CN=johndoe,OU=europe,CN=users,DC=corp,DC=contoso,DC=com
|
||||||
|
|
||||||
|
GUID (objectGUID)
|
||||||
|
Example: 599c3d2e-f72d-4d20-8a88-030d99495f20
|
||||||
|
|
||||||
|
Security Identifier (objectSid)
|
||||||
|
Example: S-1-5-21-3165297888-301567370-576410423-1103
|
||||||
|
|
||||||
|
Security Accounts Manager (SAM) Account Name (sAMAccountName)
|
||||||
|
Example: johndoe
|
||||||
|
|
||||||
|
.PARAMETER TargetUser
|
||||||
|
Target-User Object.
|
||||||
|
|
||||||
|
Specifies an Active Directory group object by providing one of the following values.
|
||||||
|
The identifier in parentheses is the LDAP display name for the attribute.
|
||||||
|
|
||||||
|
Distinguished Name
|
||||||
|
Example: CN=janedoe,OU=europe,CN=users,DC=corp,DC=contoso,DC=com
|
||||||
|
|
||||||
|
GUID (objectGUID)
|
||||||
|
Example: 599c3d2e-f72d-4d20-8a88-030d99495f20
|
||||||
|
|
||||||
|
Security Identifier (objectSid)
|
||||||
|
Example: S-1-5-21-3165297888-301567370-576410423-1103
|
||||||
|
|
||||||
|
Security Accounts Manager (SAM) Account Name (sAMAccountName)
|
||||||
|
Example: janedoe
|
||||||
|
|
||||||
|
.PARAMETER PassThru
|
||||||
|
Use the -PassThru parameter with the previous command to receive feedback about what groups the Target is being added as a member of.
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
PS C:\> Copy-ADUserGroupMembershipSimple -SourceUser 'SourceUser' -TargetUser 'TargetUser'
|
||||||
|
|
||||||
|
Copy group memberships from SourceUser to TargetUser
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
PS C:\> Copy-ADUserGroupMembershipSimple -SourceUser 'SourceUser' -TargetUser 'TargetUser1', 'TargetUser2'
|
||||||
|
|
||||||
|
Copy group memberships from SourceUser to TargetUser1 and TargetUser2
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
PS C:\> Copy-ADUserGroupMembershipSimple -SourceUser 'SourceUser' -TargetUser 'TargetUser' -PassThru
|
||||||
|
|
||||||
|
Use the -PassThru parameter with the previous command to receive feedback about what groups the Target is being added as a member of.
|
||||||
|
|
||||||
|
.NOTES
|
||||||
|
Releasenotes:
|
||||||
|
1.0.0 2019-07-09: Initial Release
|
||||||
|
|
||||||
|
THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER.
|
||||||
|
|
||||||
|
.LINK
|
||||||
|
Copy-ADUserGroupMemberships
|
||||||
|
|
||||||
|
.LINK
|
||||||
|
https://github.com/jhochwald/PowerShell-collection/
|
||||||
|
|
||||||
|
.LINK
|
||||||
|
Get-ADUser
|
||||||
|
|
||||||
|
.LINK
|
||||||
|
Add-ADGroupMember
|
||||||
|
#>
|
||||||
|
|
||||||
|
[CmdletBinding(ConfirmImpact = 'Low',
|
||||||
|
SupportsShouldProcess = $true)]
|
||||||
|
param
|
||||||
|
(
|
||||||
|
[Parameter(Mandatory = $true,
|
||||||
|
ValueFromPipeline = $true,
|
||||||
|
ValueFromPipelineByPropertyName = $true,
|
||||||
|
Position = 0,
|
||||||
|
HelpMessage = 'Source-User Object.')]
|
||||||
|
[ValidateNotNullOrEmpty()]
|
||||||
|
[Alias('Source')]
|
||||||
|
[string]
|
||||||
|
$SourceUser,
|
||||||
|
[Parameter(Mandatory = $true,
|
||||||
|
ValueFromPipeline = $true,
|
||||||
|
ValueFromPipelineByPropertyName = $true,
|
||||||
|
Position = 1,
|
||||||
|
HelpMessage = 'Target-User Object.')]
|
||||||
|
[ValidateNotNullOrEmpty()]
|
||||||
|
[Alias('Target')]
|
||||||
|
[string[]]
|
||||||
|
$TargetUser,
|
||||||
|
[Parameter(ValueFromPipeline = $true,
|
||||||
|
ValueFromPipelineByPropertyName = $true,
|
||||||
|
Position = 2)]
|
||||||
|
[switch]
|
||||||
|
$PassThru = $null
|
||||||
|
)
|
||||||
|
|
||||||
|
process
|
||||||
|
{
|
||||||
|
if ($pscmdlet.ShouldProcess($TargetUser, 'Modify/add Group Membership'))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$paramGetADUser = @{
|
||||||
|
Identity = $SourceUser
|
||||||
|
Properties = 'memberof'
|
||||||
|
Verbose = $(if ($pscmdlet.MyInvocation.BoundParameters['Verbose'].IsPresent)
|
||||||
|
{
|
||||||
|
$true
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
$paramAddADGroupMember = @{
|
||||||
|
Members = $TargetUser
|
||||||
|
Verbose = $(if ($pscmdlet.MyInvocation.BoundParameters['Verbose'].IsPresent)
|
||||||
|
{
|
||||||
|
$true
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
# Workaround: If not present it is empty not false
|
||||||
|
$false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
PassThru = $(if ($pscmdlet.MyInvocation.BoundParameters['PassThru'].IsPresent)
|
||||||
|
{
|
||||||
|
$true
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
# Workaround: If not present it is empty not false
|
||||||
|
$false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (($pscmdlet.MyInvocation.BoundParameters['PassThru'].IsPresent))
|
||||||
|
{
|
||||||
|
# Show the output / PassThru in a nice format
|
||||||
|
((Get-ADUser @paramGetADUser) | Select-Object -ExpandProperty memberof | Add-ADGroupMember @paramAddADGroupMember | Select-Object -ExpandProperty SamAccountName)
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
# Do not show any output / Verbose will be shown
|
||||||
|
$null = ((Get-ADUser @paramGetADUser) | Select-Object -ExpandProperty memberof | Add-ADGroupMember @paramAddADGroupMember)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
#region ErrorHandler
|
||||||
|
# get error record
|
||||||
|
[Management.Automation.ErrorRecord]$e = $_
|
||||||
|
|
||||||
|
# retrieve information about runtime error
|
||||||
|
$info = [PSCustomObject]@{
|
||||||
|
Exception = $e.Exception.Message
|
||||||
|
Reason = $e.CategoryInfo.Reason
|
||||||
|
Target = $e.CategoryInfo.TargetName
|
||||||
|
Script = $e.InvocationInfo.ScriptName
|
||||||
|
Line = $e.InvocationInfo.ScriptLineNumber
|
||||||
|
Column = $e.InvocationInfo.OffsetInLine
|
||||||
|
}
|
||||||
|
|
||||||
|
$info | Out-String | Write-Verbose
|
||||||
|
|
||||||
|
Write-Error -Message ($info.Exception) -ErrorAction Stop
|
||||||
|
|
||||||
|
# Only here to catch a global ErrorAction overwrite
|
||||||
|
break
|
||||||
|
#endregion ErrorHandler
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#region LICENSE
|
||||||
|
<#
|
||||||
|
BSD 3-Clause License
|
||||||
|
|
||||||
|
Copyright (c) 2021, enabling Technology
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||||
|
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
#>
|
||||||
|
#endregion LICENSE
|
||||||
|
|
||||||
|
#region DISCLAIMER
|
||||||
|
<#
|
||||||
|
DISCLAIMER:
|
||||||
|
- Use at your own risk, etc.
|
||||||
|
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
|
||||||
|
- This is a third-party Software
|
||||||
|
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
|
||||||
|
- The Software is not supported by Microsoft Corp (MSFT)
|
||||||
|
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
|
||||||
|
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
|
||||||
|
#>
|
||||||
|
#endregion DISCLAIMER
|
||||||
@@ -0,0 +1,169 @@
|
|||||||
|
function Find-enADDuplicateServicePrincipalName
|
||||||
|
{
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Find all duplicate Service Principal Names (SPNs)
|
||||||
|
|
||||||
|
.DESCRIPTION
|
||||||
|
Find all duplicate Service Principal Names (SPNs) in the Active Directory
|
||||||
|
|
||||||
|
.INPUTS
|
||||||
|
NONE
|
||||||
|
|
||||||
|
.OUTPUTS
|
||||||
|
Boolean
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
PS C:\> Find-enADDuplicateServicePrincipalName
|
||||||
|
|
||||||
|
.NOTES
|
||||||
|
Releasenotes:
|
||||||
|
1.0.1 2019-07-26 Refactored, License change to BSD 3-Clause
|
||||||
|
1.0.0 2019-01-01 Initial Version
|
||||||
|
|
||||||
|
THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER.
|
||||||
|
|
||||||
|
Dependencies:
|
||||||
|
Active Directory PowerShell Module
|
||||||
|
|
||||||
|
.LINK
|
||||||
|
https://www.enatec.io
|
||||||
|
|
||||||
|
.LINK
|
||||||
|
Get-ADObject
|
||||||
|
#>
|
||||||
|
[CmdletBinding(ConfirmImpact = 'None')]
|
||||||
|
[OutputType([bool])]
|
||||||
|
param ()
|
||||||
|
|
||||||
|
begin
|
||||||
|
{
|
||||||
|
# Create a new Object
|
||||||
|
$AllObject = @()
|
||||||
|
}
|
||||||
|
|
||||||
|
process
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
# We use Get-ADObject because this seems to be fast enough
|
||||||
|
$paramGetADObject = @{
|
||||||
|
Filter = "(objectClass -eq 'user') -or (objectClass -eq 'computer') -and (servicePrincipalName -like '*')"
|
||||||
|
Properties = 'SamAccountName', 'servicePrincipalName'
|
||||||
|
ErrorAction = 'Stop'
|
||||||
|
WarningAction = 'Continue'
|
||||||
|
}
|
||||||
|
$AllServicePrincipalNames = (Get-ADObject @paramGetADObject)
|
||||||
|
|
||||||
|
# Loop over the List we got from Get-ADObject
|
||||||
|
foreach ($SPNObject in $AllServicePrincipalNames)
|
||||||
|
{
|
||||||
|
$SamAccountName = $SPNObject.SamAccountName
|
||||||
|
$ServicePrincipalNames = $SPNObject.ServicePrincipalName
|
||||||
|
|
||||||
|
|
||||||
|
foreach ($ServicePrincipalName in $ServicePrincipalNames)
|
||||||
|
{
|
||||||
|
if ($AllObject.ServicePrincipalName -like $ServicePrincipalName)
|
||||||
|
{
|
||||||
|
$MatchedSPNs = ($AllObject.ServicePrincipalName -like $ServicePrincipalName)
|
||||||
|
|
||||||
|
# Loop over the matching list og SPNs
|
||||||
|
foreach ($MatchSPN in $MatchedSPNs)
|
||||||
|
{
|
||||||
|
$MatchSamAccountName = $MatchSPN.SamAccountName
|
||||||
|
|
||||||
|
# Ding. ding, we have a winner
|
||||||
|
if ($MatchSamAccountName -ne $SamAccountName)
|
||||||
|
{
|
||||||
|
$paramWriteWarning = @{
|
||||||
|
Message = ('Duplicated SPN has been found for {0}!!!' -f $ServicePrincipalName)
|
||||||
|
ErrorAction = 'Continue'
|
||||||
|
WarningAction = 'Continue'
|
||||||
|
}
|
||||||
|
Write-Warning @paramWriteWarning
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
# Create a new Object
|
||||||
|
$SingleObject = (New-Object -TypeName PSObject -Property @{
|
||||||
|
SamAccountName = $SamAccountName
|
||||||
|
ServicePrincipalName = $ServicePrincipalName
|
||||||
|
})
|
||||||
|
|
||||||
|
# Add the Values to the List
|
||||||
|
$AllObject += $SingleObject
|
||||||
|
|
||||||
|
# Cleanup
|
||||||
|
$SingleObject = $null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
#region ErrorHandler
|
||||||
|
# get error record
|
||||||
|
[Management.Automation.ErrorRecord]$e = $_
|
||||||
|
|
||||||
|
# retrieve information about runtime error
|
||||||
|
$info = [PSCustomObject]@{
|
||||||
|
Exception = $e.Exception.Message
|
||||||
|
Reason = $e.CategoryInfo.Reason
|
||||||
|
Target = $e.CategoryInfo.TargetName
|
||||||
|
Script = $e.InvocationInfo.ScriptName
|
||||||
|
Line = $e.InvocationInfo.ScriptLineNumber
|
||||||
|
Column = $e.InvocationInfo.OffsetInLine
|
||||||
|
}
|
||||||
|
|
||||||
|
$info | Out-String | Write-Verbose
|
||||||
|
|
||||||
|
Write-Error -Message ($info.Exception) -ErrorAction Stop
|
||||||
|
|
||||||
|
# Only here to catch a global ErrorAction overwrite
|
||||||
|
break
|
||||||
|
#endregion ErrorHandler
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
end
|
||||||
|
{
|
||||||
|
# Dump all SPNs, if verbose
|
||||||
|
$AllObject | Out-String | Write-Verbose
|
||||||
|
|
||||||
|
# Cleanup
|
||||||
|
$AllObject = $null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#region LICENSE
|
||||||
|
<#
|
||||||
|
BSD 3-Clause License
|
||||||
|
|
||||||
|
Copyright (c) 2021, enabling Technology
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||||
|
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
#>
|
||||||
|
#endregion LICENSE
|
||||||
|
|
||||||
|
#region DISCLAIMER
|
||||||
|
<#
|
||||||
|
DISCLAIMER:
|
||||||
|
- Use at your own risk, etc.
|
||||||
|
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
|
||||||
|
- This is a third-party Software
|
||||||
|
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
|
||||||
|
- The Software is not supported by Microsoft Corp (MSFT)
|
||||||
|
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
|
||||||
|
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
|
||||||
|
#>
|
||||||
|
#endregion DISCLAIMER
|
||||||
@@ -0,0 +1,203 @@
|
|||||||
|
function Get-ADUserLockouts
|
||||||
|
{
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Tracking down account lockout sources with PowerShell
|
||||||
|
|
||||||
|
.DESCRIPTION
|
||||||
|
Tracking down account lockout sources with PowerShell
|
||||||
|
|
||||||
|
.PARAMETER Identity
|
||||||
|
Just scan for a single User?
|
||||||
|
|
||||||
|
.PARAMETER StartTime
|
||||||
|
Start-point
|
||||||
|
|
||||||
|
.PARAMETER EndTime
|
||||||
|
Endpoint
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
PS C:\> Get-ADUserLockout
|
||||||
|
|
||||||
|
Tracking down account lockout sources for all users for the last 7 days
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
Get-ADUser -Filter {Department -eq 'Development'} | Get-ADUserLockout
|
||||||
|
|
||||||
|
Tracking down account lockout sources for all users in the Development Department for the last 7 days
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
Get-ADUserLockout -StartTime (Get-Date).AddDays(-2) -EndTime (Get-Date).AddDays(-1)
|
||||||
|
|
||||||
|
Tracking down account lockout sources for all users for the last day
|
||||||
|
|
||||||
|
.NOTES
|
||||||
|
Original by Anthony Howell (@ThePoShWolf) - MIT Licenses
|
||||||
|
Copyright (c) 2018 Anthony Howell
|
||||||
|
|
||||||
|
.LINK
|
||||||
|
https://theposhwolf.com/howtos/Get-ADUserLockouts/
|
||||||
|
|
||||||
|
.LINK
|
||||||
|
https://github.com/ThePoShWolf/Utilities/blob/master/ActiveDirectory/Get-ADUserLockouts.ps1
|
||||||
|
#>
|
||||||
|
[CmdletBinding(DefaultParameterSetName = 'All',
|
||||||
|
ConfirmImpact = 'None')]
|
||||||
|
[OutputType([pscustomobject])]
|
||||||
|
param
|
||||||
|
(
|
||||||
|
[Parameter(ParameterSetName = 'ByUser',
|
||||||
|
ValueFromPipeline)]
|
||||||
|
[string]
|
||||||
|
$Identity,
|
||||||
|
[Parameter(ValueFromPipeline,
|
||||||
|
ValueFromPipelineByPropertyName)]
|
||||||
|
[Alias('Start')]
|
||||||
|
[datetime]
|
||||||
|
$StartTime = (Get-Date).AddDays(-8),
|
||||||
|
[Parameter(ValueFromPipeline,
|
||||||
|
ValueFromPipelineByPropertyName)]
|
||||||
|
[Alias('End')]
|
||||||
|
[datetime]
|
||||||
|
$EndTime = (Get-Date).AddDays(-1)
|
||||||
|
)
|
||||||
|
|
||||||
|
begin
|
||||||
|
{
|
||||||
|
$filterHt = @{
|
||||||
|
LogName = 'Security'
|
||||||
|
ID = 4740
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($PSBoundParameters.ContainsKey('StartTime'))
|
||||||
|
{
|
||||||
|
$filterHt['StartTime'] = $StartTime
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($PSBoundParameters.ContainsKey('EndTime'))
|
||||||
|
{
|
||||||
|
$filterHt['EndTime'] = $EndTime
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$PDCEmulator = ((Get-ADDomain -ErrorAction Stop).PDCEmulator)
|
||||||
|
|
||||||
|
Write-Verbose -Message ('Use {0} to find the lockout events' -f $PDCEmulator)
|
||||||
|
|
||||||
|
# Query the event log just once instead of for each user if using the pipeline
|
||||||
|
$events = (Get-WinEvent -ComputerName $PDCEmulator -FilterHashtable $filterHt -ErrorAction Stop)
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
# get error record
|
||||||
|
[Management.Automation.ErrorRecord]$e = $_
|
||||||
|
|
||||||
|
# retrieve information about runtime error
|
||||||
|
$info = [PSCustomObject]@{
|
||||||
|
Exception = $e.Exception.Message
|
||||||
|
Reason = $e.CategoryInfo.Reason
|
||||||
|
Target = $e.CategoryInfo.TargetName
|
||||||
|
Script = $e.InvocationInfo.ScriptName
|
||||||
|
Line = $e.InvocationInfo.ScriptLineNumber
|
||||||
|
Column = $e.InvocationInfo.OffsetInLine
|
||||||
|
}
|
||||||
|
|
||||||
|
# output information. Post-process collected info, and log info (optional)
|
||||||
|
$info | Out-String | Write-Verbose
|
||||||
|
|
||||||
|
Write-Error -Message $e.Exception.Message -ErrorAction Stop -Exception $e.Exception -TargetObject $e.CategoryInfo.TargetName
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Verbose -Message 'Found the following events:'
|
||||||
|
Write-Verbose -Message $events
|
||||||
|
}
|
||||||
|
|
||||||
|
process
|
||||||
|
{
|
||||||
|
if ($PSCmdlet.ParameterSetName -eq 'ByUser')
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Write-Verbose -Message ('Querry AD Info for {0}' -f $Identity)
|
||||||
|
|
||||||
|
$user = (Get-ADUser -Identity $Identity -ErrorAction Stop)
|
||||||
|
|
||||||
|
Write-Verbose -Message ('Found the following AD Info for {0}:' -f $Identity)
|
||||||
|
Write-Verbose -Message $user
|
||||||
|
|
||||||
|
# Filter the events
|
||||||
|
$output = $events | Where-Object -FilterScript {
|
||||||
|
$_.Properties[0].Value -eq $user.SamAccountName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
# get error record
|
||||||
|
[Management.Automation.ErrorRecord]$e = $_
|
||||||
|
|
||||||
|
# retrieve information about runtime error
|
||||||
|
$info = [PSCustomObject]@{
|
||||||
|
Exception = $e.Exception.Message
|
||||||
|
Reason = $e.CategoryInfo.Reason
|
||||||
|
Target = $e.CategoryInfo.TargetName
|
||||||
|
Script = $e.InvocationInfo.ScriptName
|
||||||
|
Line = $e.InvocationInfo.ScriptLineNumber
|
||||||
|
Column = $e.InvocationInfo.OffsetInLine
|
||||||
|
}
|
||||||
|
|
||||||
|
# output information. Post-process collected info, and log info (optional)
|
||||||
|
$info | Out-String | Write-Verbose
|
||||||
|
|
||||||
|
Write-Error -Message $e.Exception.Message -ErrorAction Stop -Exception $e.Exception -TargetObject $e.CategoryInfo.TargetName
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$output = $events
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($event in $output)
|
||||||
|
{
|
||||||
|
[pscustomobject]@{
|
||||||
|
UserName = $event.Properties[0].Value
|
||||||
|
CallerComputer = $event.Properties[1].Value
|
||||||
|
TimeStamp = $event.TimeCreated
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#region LICENSE
|
||||||
|
<#
|
||||||
|
BSD 3-Clause License
|
||||||
|
|
||||||
|
Copyright (c) 2021, enabling Technology
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||||
|
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
#>
|
||||||
|
#endregion LICENSE
|
||||||
|
|
||||||
|
#region DISCLAIMER
|
||||||
|
<#
|
||||||
|
DISCLAIMER:
|
||||||
|
- Use at your own risk, etc.
|
||||||
|
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
|
||||||
|
- This is a third-party Software
|
||||||
|
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
|
||||||
|
- The Software is not supported by Microsoft Corp (MSFT)
|
||||||
|
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
|
||||||
|
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
|
||||||
|
#>
|
||||||
|
#endregion DISCLAIMER
|
||||||
@@ -0,0 +1,196 @@
|
|||||||
|
function Get-enADDNSServerInformation
|
||||||
|
{
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Retrieve information about the Active Directory Domain Name Servers
|
||||||
|
|
||||||
|
.DESCRIPTION
|
||||||
|
Retrieve information about the Active Directory Domain Name Servers
|
||||||
|
|
||||||
|
.PARAMETER Domain
|
||||||
|
A description of the Domain parameter.
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
PS ~> Get-enADDNSServerInformation
|
||||||
|
|
||||||
|
Retrieve information about the Active Directory Domain Name Servers, use the current domain
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
PS ~> Get-enADDNSServerInformation | Export-CSV -Path C:\scripts\PowerShell\Reports\DNS_Zones.csv -NoTypeInformation -Force -Confirm:$false
|
||||||
|
Retrieve information about the Active Directory Domain Name Servers, use the current domain and exports it to CSV
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
PS ~> Get-enADDNSServerInformation | ConvertTo-Json -Depth 10 | Set-Content -Path C:\scripts\PowerShell\Reports\DNS_Zones.json -Force -Confirm:$false
|
||||||
|
Retrieve information about the Active Directory Domain Name Servers, use the current domain and exports it to a JSON File
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
PS ~> Get-enADDNSServerInformation -Domain 'contoso.com'
|
||||||
|
|
||||||
|
Retrieve information about the Active Directory Domain Name Servers in the Domain contoso.com
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
PS ~> Get-enADDNSServerInformation -Domain 'contoso.com', 'corp.contoso.net'
|
||||||
|
|
||||||
|
Retrieve information about the Active Directory Domain Name Servers in the Domain contoso.com and corp.contoso.net
|
||||||
|
|
||||||
|
.OUTPUTS
|
||||||
|
psobject
|
||||||
|
|
||||||
|
.INPUTS
|
||||||
|
String
|
||||||
|
|
||||||
|
.NOTES
|
||||||
|
TODO: Need refactoring: Object handler sucks
|
||||||
|
TODO: Find a non WMI based Solution for this
|
||||||
|
|
||||||
|
Version: 1.0.1
|
||||||
|
|
||||||
|
GUID: 4404141a-1731-4786-8bbf-ee6706765050
|
||||||
|
|
||||||
|
Author: Joerg Hochwald
|
||||||
|
|
||||||
|
Companyname: enabling Technology
|
||||||
|
|
||||||
|
Copyright: Copyright (c) 2ß18-2019, enabling Technology - All rights reserved.
|
||||||
|
|
||||||
|
License: https://opensource.org/licenses/BSD-3-Clause
|
||||||
|
|
||||||
|
Releasenotes:
|
||||||
|
1.0.1 2019-07-26 Refactored, License change to BSD 3-Clause
|
||||||
|
1.0.0 2019-01-01 Initial Version
|
||||||
|
|
||||||
|
THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER.
|
||||||
|
|
||||||
|
.LINK
|
||||||
|
https://www.enatec.io
|
||||||
|
|
||||||
|
.LINK
|
||||||
|
http://msdn.microsoft.com/en-us/library/windows/desktop/aa393295(v=vs.85).aspx
|
||||||
|
|
||||||
|
.LINK
|
||||||
|
Get-ADDomainController
|
||||||
|
|
||||||
|
.LINK
|
||||||
|
Get-WmiObject
|
||||||
|
#>
|
||||||
|
|
||||||
|
[CmdletBinding(ConfirmImpact = 'None')]
|
||||||
|
[OutputType([psobject])]
|
||||||
|
param
|
||||||
|
(
|
||||||
|
[Parameter(ValueFromPipeline,
|
||||||
|
ValueFromPipelineByPropertyName)]
|
||||||
|
[string[]]
|
||||||
|
$Domain = ([DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain().Name.ToString())
|
||||||
|
)
|
||||||
|
|
||||||
|
begin
|
||||||
|
{
|
||||||
|
$DNSReport = @()
|
||||||
|
}
|
||||||
|
|
||||||
|
process
|
||||||
|
{
|
||||||
|
foreach ($DomainEach in $Domain)
|
||||||
|
{
|
||||||
|
$AllDomainControllers = (Get-ADDomainController -Filter {
|
||||||
|
Site -like '*' -and Domain -eq $DomainEach
|
||||||
|
} | Select-Object -ExpandProperty Name)
|
||||||
|
|
||||||
|
foreach ($SingleDomainController in $AllDomainControllers)
|
||||||
|
{
|
||||||
|
# Prevent Null Pointer Exceptions
|
||||||
|
if ($SingleDomainController)
|
||||||
|
{
|
||||||
|
# TODO: Find a non WMI based Solution for this
|
||||||
|
$Forwarders = (Get-WmiObject -ComputerName $SingleDomainController -Namespace root\MicrosoftDNS -Class MicrosoftDNS_Server -ErrorAction SilentlyContinue)
|
||||||
|
|
||||||
|
# TODO: Find a non WMI based Solution for this
|
||||||
|
$NetworkInterface = (Get-WmiObject -ComputerName $SingleDomainController -Query 'Select * From Win32_NetworkAdapterConfiguration Where IPEnabled=TRUE' -ErrorAction SilentlyContinue)
|
||||||
|
|
||||||
|
$DNSReport += 1 | Select-Object -Property @{
|
||||||
|
name = 'DC'
|
||||||
|
expression = {
|
||||||
|
$SingleDomainController
|
||||||
|
}
|
||||||
|
}, @{
|
||||||
|
name = 'Domain'
|
||||||
|
expression = {
|
||||||
|
$DomainEach
|
||||||
|
}
|
||||||
|
}, @{
|
||||||
|
name = 'DNSHostName'
|
||||||
|
expression = {
|
||||||
|
$NetworkInterface.DNSHostName
|
||||||
|
}
|
||||||
|
}, @{
|
||||||
|
name = 'IPAddress'
|
||||||
|
expression = {
|
||||||
|
$NetworkInterface.IPAddress
|
||||||
|
}
|
||||||
|
}, @{
|
||||||
|
name = 'DNSServerAddresses'
|
||||||
|
expression = {
|
||||||
|
$Forwarders.ServerAddresses
|
||||||
|
}
|
||||||
|
}, @{
|
||||||
|
name = 'DNSServerSearchOrder'
|
||||||
|
expression = {
|
||||||
|
$NetworkInterface.DNSServerSearchOrder
|
||||||
|
}
|
||||||
|
}, @{
|
||||||
|
name = 'Forwarders'
|
||||||
|
expression = {
|
||||||
|
$Forwarders.Forwarders
|
||||||
|
}
|
||||||
|
}, @{
|
||||||
|
name = 'BootMethod'
|
||||||
|
expression = {
|
||||||
|
$Forwarders.BootMethod
|
||||||
|
}
|
||||||
|
}, @{
|
||||||
|
name = 'ScavengingInterval'
|
||||||
|
expression = {
|
||||||
|
$Forwarders.ScavengingInterval
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
end
|
||||||
|
{
|
||||||
|
$DNSReport
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#region LICENSE
|
||||||
|
<#
|
||||||
|
BSD 3-Clause License
|
||||||
|
|
||||||
|
Copyright (c) 2021, enabling Technology
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||||
|
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
#>
|
||||||
|
#endregion LICENSE
|
||||||
|
|
||||||
|
#region DISCLAIMER
|
||||||
|
<#
|
||||||
|
DISCLAIMER:
|
||||||
|
- Use at your own risk, etc.
|
||||||
|
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
|
||||||
|
- This is a third-party Software
|
||||||
|
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
|
||||||
|
- The Software is not supported by Microsoft Corp (MSFT)
|
||||||
|
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
|
||||||
|
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
|
||||||
|
#>
|
||||||
|
#endregion DISCLAIMER
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
Function Get-enDomainControllerInfo
|
||||||
|
{
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Get a list of domain controllers
|
||||||
|
|
||||||
|
.DESCRIPTION
|
||||||
|
Will provide a list of domain controllers in your current domain.
|
||||||
|
Optionally you can also request a discovery of the "closest" one.
|
||||||
|
|
||||||
|
.PARAMETER ComputerName
|
||||||
|
Retrieve information about the specified domain controller.
|
||||||
|
This is a RegEx match so you can match multiple domain controllers with your pattern.
|
||||||
|
|
||||||
|
.PARAMETER Discover
|
||||||
|
Use Discover to return the information of the closest domain controller.
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
Get-enDomainControllerInfo
|
||||||
|
|
||||||
|
Retrieve a list of all domain controllers in your domain.
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
Get-enDomainControllerInfo -Computer 01
|
||||||
|
|
||||||
|
Retrieve a list of all domain controllers with "01" in their name.
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
Get-enDomainControllerInfo -Discover
|
||||||
|
|
||||||
|
Retrieve the name of the closest domain controller.
|
||||||
|
|
||||||
|
.NOTES
|
||||||
|
#>
|
||||||
|
[CmdletBinding(DefaultParameterSetName = 'all')]
|
||||||
|
Param (
|
||||||
|
[Parameter(Position = 0, ParameterSetName = 'dc')]
|
||||||
|
[string]$ComputerName,
|
||||||
|
[Parameter(ParameterSetName = 'all')]
|
||||||
|
[switch]$Discover
|
||||||
|
)
|
||||||
|
|
||||||
|
begin
|
||||||
|
{
|
||||||
|
$DirectoryContext = [DirectoryServices.ActiveDirectory.DirectoryContext]::New('Domain')
|
||||||
|
$SelectProperties = 'Name', 'Forest', 'Domain', 'SiteName', 'Roles', 'CurrentTime', 'HighestCommittedUsn', 'OSVersion'
|
||||||
|
}
|
||||||
|
|
||||||
|
process
|
||||||
|
{
|
||||||
|
If ($Discover)
|
||||||
|
{
|
||||||
|
$LocatorFlag = [DirectoryServices.ActiveDirectory.LocatorOptions]::ForceRediscovery
|
||||||
|
$Info = ([DirectoryServices.ActiveDirectory.DomainController]::FindOne($DirectoryContext, $LocatorFlag) | Select-Object -Property $SelectProperties)
|
||||||
|
}
|
||||||
|
elseif ($ComputerName)
|
||||||
|
{
|
||||||
|
$Info = ([DirectoryServices.ActiveDirectory.DomainController]::FindAll($DirectoryContext) | Where-Object -Property Name -Match -Value $ComputerName | Select-Object -Property $SelectProperties)
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$Info = ([DirectoryServices.ActiveDirectory.DomainController]::FindAll($DirectoryContext) | Select-Object -Property $SelectProperties)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
end
|
||||||
|
{
|
||||||
|
$Info
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#region LICENSE
|
||||||
|
<#
|
||||||
|
BSD 3-Clause License
|
||||||
|
|
||||||
|
Copyright (c) 2021, enabling Technology
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||||
|
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
#>
|
||||||
|
#endregion LICENSE
|
||||||
|
|
||||||
|
#region DISCLAIMER
|
||||||
|
<#
|
||||||
|
DISCLAIMER:
|
||||||
|
- Use at your own risk, etc.
|
||||||
|
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
|
||||||
|
- This is a third-party Software
|
||||||
|
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
|
||||||
|
- The Software is not supported by Microsoft Corp (MSFT)
|
||||||
|
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
|
||||||
|
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
|
||||||
|
#>
|
||||||
|
#endregion DISCLAIMER
|
||||||
@@ -0,0 +1,148 @@
|
|||||||
|
#requires -Version 3.0 -Modules ActiveDirectory
|
||||||
|
|
||||||
|
function Get-enADFSMORole
|
||||||
|
{
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Retrieve the FSMO Role in the Forest/Domain
|
||||||
|
|
||||||
|
.DESCRIPTION
|
||||||
|
Retrieve the FSMO Role in the Forest/Domain of Active Directory
|
||||||
|
|
||||||
|
.PARAMETER Credential
|
||||||
|
Specify the alternative credential to use
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
Get-enADFSMORole
|
||||||
|
|
||||||
|
Retrieve the FSMO Role in the Forest/Domain of Active Directory
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
Get-enADFSMORole -Credential (Get-Credential)
|
||||||
|
|
||||||
|
Retrieve the FSMO Role in the Forest/Domain of Active Directory
|
||||||
|
|
||||||
|
.NOTES
|
||||||
|
Releasenotes:
|
||||||
|
1.0.1 2019-07-26 Refactored, License change to BSD 3-Clause
|
||||||
|
1.0.0 2019-01-01 Initial Version
|
||||||
|
|
||||||
|
THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER.
|
||||||
|
|
||||||
|
Dependencies:
|
||||||
|
Active Directory PowerShell Module
|
||||||
|
|
||||||
|
.LINK
|
||||||
|
https://www.enatec.io
|
||||||
|
|
||||||
|
.LINK
|
||||||
|
Get-ADForest
|
||||||
|
|
||||||
|
.LINK
|
||||||
|
Get-ADDomain
|
||||||
|
#>
|
||||||
|
|
||||||
|
[CmdletBinding(ConfirmImpact = 'None')]
|
||||||
|
[OutputType([psobject])]
|
||||||
|
param
|
||||||
|
(
|
||||||
|
[Parameter(ValueFromPipeline,
|
||||||
|
ValueFromPipelineByPropertyName)]
|
||||||
|
[System.Management.Automation.Credential()]
|
||||||
|
[Alias('RunAs')]
|
||||||
|
[pscredential]
|
||||||
|
$Credential = [pscredential]::Empty
|
||||||
|
)
|
||||||
|
|
||||||
|
begin
|
||||||
|
{
|
||||||
|
$Properties = $null
|
||||||
|
}
|
||||||
|
|
||||||
|
process
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if ($PSBoundParameters['Credential'])
|
||||||
|
{
|
||||||
|
# Query with the credentials specified
|
||||||
|
$ForestRoles = (Get-ADForest -Credential $Credential -ErrorAction 'Stop' -ErrorVariable ErrorGetADForest)
|
||||||
|
$DomainRoles = (Get-ADDomain -Credential $Credential -ErrorAction 'Stop' -ErrorVariable ErrorGetADDomain)
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
# Query with the current credentials
|
||||||
|
$ForestRoles = (Get-ADForest)
|
||||||
|
$DomainRoles = (Get-ADDomain)
|
||||||
|
}
|
||||||
|
|
||||||
|
# Define Properties
|
||||||
|
$Properties = @{
|
||||||
|
SchemaMaster = $ForestRoles.SchemaMaster
|
||||||
|
DomainNamingMaster = $ForestRoles.DomainNamingMaster
|
||||||
|
InfraStructureMaster = $DomainRoles.InfraStructureMaster
|
||||||
|
RIDMaster = $DomainRoles.RIDMaster
|
||||||
|
PDCEmulator = $DomainRoles.PDCEmulator
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
#region ErrorHandler
|
||||||
|
# get error record
|
||||||
|
[Management.Automation.ErrorRecord]$e = $_
|
||||||
|
|
||||||
|
# retrieve information about runtime error
|
||||||
|
$info = [PSCustomObject]@{
|
||||||
|
Exception = $e.Exception.Message
|
||||||
|
Reason = $e.CategoryInfo.Reason
|
||||||
|
Target = $e.CategoryInfo.TargetName
|
||||||
|
Script = $e.InvocationInfo.ScriptName
|
||||||
|
Line = $e.InvocationInfo.ScriptLineNumber
|
||||||
|
Column = $e.InvocationInfo.OffsetInLine
|
||||||
|
}
|
||||||
|
|
||||||
|
$info | Out-String | Write-Verbose
|
||||||
|
|
||||||
|
Write-Error -Message ($info.Exception) -ErrorAction Stop
|
||||||
|
|
||||||
|
# Only here to catch a global ErrorAction overwrite
|
||||||
|
break
|
||||||
|
#endregion ErrorHandler
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
end
|
||||||
|
{
|
||||||
|
$Properties
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#region LICENSE
|
||||||
|
<#
|
||||||
|
BSD 3-Clause License
|
||||||
|
|
||||||
|
Copyright (c) 2021, enabling Technology
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||||
|
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
#>
|
||||||
|
#endregion LICENSE
|
||||||
|
|
||||||
|
#region DISCLAIMER
|
||||||
|
<#
|
||||||
|
DISCLAIMER:
|
||||||
|
- Use at your own risk, etc.
|
||||||
|
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
|
||||||
|
- This is a third-party Software
|
||||||
|
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
|
||||||
|
- The Software is not supported by Microsoft Corp (MSFT)
|
||||||
|
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
|
||||||
|
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
|
||||||
|
#>
|
||||||
|
#endregion DISCLAIMER
|
||||||
@@ -0,0 +1,189 @@
|
|||||||
|
function Get-enADForestInformation
|
||||||
|
{
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Retrieve information about an Active Directory Forest
|
||||||
|
|
||||||
|
.DESCRIPTION
|
||||||
|
Retrieve information about an Active Directory Forest
|
||||||
|
|
||||||
|
.PARAMETER ForestName
|
||||||
|
Forest name to retrieve information about
|
||||||
|
|
||||||
|
.PARAMETER Credential
|
||||||
|
Credential to use for retrieval
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
PS ~> Get-enADForestInformation
|
||||||
|
|
||||||
|
Retrieve information about the current Active Directory Forest
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
PS ~> Get-enADForestInformation | Select-Object ApplicationPartitions
|
||||||
|
|
||||||
|
Retrieve information about Application Partitions from the current Active Directory Forest
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
PS ~> Get-enADForestInformation | Select-Object GlobalCatalogs
|
||||||
|
|
||||||
|
Retrieve als Global Catalog Servers from the current Active Directory Forest
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
PS ~> (Get-enADForestInformation) | Select-Object -ExpandProperty GlobalCatalogs
|
||||||
|
|
||||||
|
Retrieve als Global Catalog Servers from the current Active Directory Forest. More details then the above example, cause it will show all the details for each Global Catalog Servers.
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
PS ~> Get-enADForestInformation | Select-Object NamingRoleOwner
|
||||||
|
|
||||||
|
Retrieve information about the Naming master Roles holder from the current Active Directory Forest
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
PS ~> (Get-enADForestInformation).Sites
|
||||||
|
|
||||||
|
Retrieve information about Active Directory Sites from the current Active Directory Forest
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
PS ~> Get-enADForestInformation -Credential (Get-Credential)
|
||||||
|
|
||||||
|
Retrieve information about the current Active Directory Forest, with special credentials (e.g. RunAs)
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
PS ~> Get-enADForestInformation -ForestName Value
|
||||||
|
|
||||||
|
Retrieve information about Active Directory Forest specified in Value
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
PS ~> Get-enADForestInformation -ForestName Value -Credential Value
|
||||||
|
|
||||||
|
Retrieve information about Active Directory Forest specified in Value, with special credentials (e.g. RunAs)
|
||||||
|
|
||||||
|
.OUTPUTS
|
||||||
|
psobject
|
||||||
|
|
||||||
|
.INPUTS
|
||||||
|
String
|
||||||
|
pscredential
|
||||||
|
|
||||||
|
.NOTES
|
||||||
|
Releasenotes:
|
||||||
|
1.0.1 2019-07-26 Refactored, License change to BSD 3-Clause
|
||||||
|
1.0.0 2019-01-01 Initial Version
|
||||||
|
|
||||||
|
THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER.
|
||||||
|
|
||||||
|
.LINK
|
||||||
|
https://www.enatec.io
|
||||||
|
|
||||||
|
.LINK
|
||||||
|
Get-ADForest
|
||||||
|
#>
|
||||||
|
[CmdletBinding(ConfirmImpact = 'None')]
|
||||||
|
[OutputType([psobject])]
|
||||||
|
param
|
||||||
|
(
|
||||||
|
[Parameter(ValueFromPipeline,
|
||||||
|
ValueFromPipelineByPropertyName)]
|
||||||
|
[Alias('Forest')]
|
||||||
|
[string]
|
||||||
|
$ForestName = ([DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest().Name.ToString()),
|
||||||
|
[Parameter(ValueFromPipeline,
|
||||||
|
ValueFromPipelineByPropertyName)]
|
||||||
|
[System.Management.Automation.Credential()]
|
||||||
|
[pscredential]
|
||||||
|
$Credential
|
||||||
|
)
|
||||||
|
|
||||||
|
begin
|
||||||
|
{
|
||||||
|
# Cleanup
|
||||||
|
$output = $null
|
||||||
|
$ActiveDirectoryContext = $null
|
||||||
|
}
|
||||||
|
|
||||||
|
process
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if ($Credential)
|
||||||
|
{
|
||||||
|
$credentialUser = ($Credential.UserName.ToString())
|
||||||
|
$credentialPassword = ($Credential.GetNetworkCredential().Password.ToString())
|
||||||
|
$ActiveDirectoryContext = (New-Object -TypeName System.DirectoryServices.ActiveDirectory.DirectoryContext -ArgumentList ('forest', $ForestName, $credentialUser, $credentialPassword))
|
||||||
|
|
||||||
|
# Cleanup
|
||||||
|
$credentialUser = $null
|
||||||
|
$credentialPassword = $null
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$ActiveDirectoryContext = (New-Object -TypeName System.DirectoryServices.ActiveDirectory.DirectoryContext -ArgumentList ('forest', $ForestName))
|
||||||
|
}
|
||||||
|
|
||||||
|
$output = ([DirectoryServices.ActiveDirectory.Forest]::GetForest($ActiveDirectoryContext))
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
#region ErrorHandler
|
||||||
|
# get error record
|
||||||
|
[Management.Automation.ErrorRecord]$e = $_
|
||||||
|
|
||||||
|
# retrieve information about runtime error
|
||||||
|
$info = [PSCustomObject]@{
|
||||||
|
Exception = $e.Exception.Message
|
||||||
|
Reason = $e.CategoryInfo.Reason
|
||||||
|
Target = $e.CategoryInfo.TargetName
|
||||||
|
Script = $e.InvocationInfo.ScriptName
|
||||||
|
Line = $e.InvocationInfo.ScriptLineNumber
|
||||||
|
Column = $e.InvocationInfo.OffsetInLine
|
||||||
|
}
|
||||||
|
|
||||||
|
$info | Out-String | Write-Verbose
|
||||||
|
|
||||||
|
Write-Error -Message ($info.Exception) -ErrorAction Stop
|
||||||
|
|
||||||
|
# Only here to catch a global ErrorAction overwrite
|
||||||
|
break
|
||||||
|
#endregion ErrorHandler
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
end
|
||||||
|
{
|
||||||
|
$output
|
||||||
|
|
||||||
|
# Cleanup
|
||||||
|
$output = $null
|
||||||
|
$ActiveDirectoryContext = $null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#region LICENSE
|
||||||
|
<#
|
||||||
|
BSD 3-Clause License
|
||||||
|
|
||||||
|
Copyright (c) 2021, enabling Technology
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||||
|
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
#>
|
||||||
|
#endregion LICENSE
|
||||||
|
|
||||||
|
#region DISCLAIMER
|
||||||
|
<#
|
||||||
|
DISCLAIMER:
|
||||||
|
- Use at your own risk, etc.
|
||||||
|
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
|
||||||
|
- This is a third-party Software
|
||||||
|
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
|
||||||
|
- The Software is not supported by Microsoft Corp (MSFT)
|
||||||
|
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
|
||||||
|
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
|
||||||
|
#>
|
||||||
|
#endregion DISCLAIMER
|
||||||
@@ -0,0 +1,158 @@
|
|||||||
|
function Get-enADGPOReplication
|
||||||
|
{
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Retrieve one or all the GPO and report their DSVersions and SysVolVersions
|
||||||
|
|
||||||
|
.DESCRIPTION
|
||||||
|
Retrieve one or all the GPO and report their DSVersions and SysVolVersions (Users and Computers)
|
||||||
|
|
||||||
|
.PARAMETER GPOName
|
||||||
|
Specify the name of the GPO
|
||||||
|
|
||||||
|
.PARAMETER All
|
||||||
|
Specify that you want to retrieve all the GPO (slow if you have a lot of Domain Controllers)
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
Get-enADGPOReplication -GPOName "Default Domain Policy"
|
||||||
|
|
||||||
|
Retrieve one GPO and report their DSVersions and SysVolVersions (Users and Computers)
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
Get-enADGPOReplication -All
|
||||||
|
|
||||||
|
Retrieve all the GPO and report their DSVersions and SysVolVersions (Users and Computers)
|
||||||
|
|
||||||
|
.NOTES
|
||||||
|
Releasenotes:
|
||||||
|
1.0.1 2019-07-26 Refactored, License change to BSD 3-Clause
|
||||||
|
1.0.0 2019-01-01 Initial Version
|
||||||
|
|
||||||
|
THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER.
|
||||||
|
|
||||||
|
Dependencies:
|
||||||
|
Active Directory PowerShell Module
|
||||||
|
|
||||||
|
.LINK
|
||||||
|
https://www.enatec.io
|
||||||
|
|
||||||
|
.LINK
|
||||||
|
Get-ADDomainController
|
||||||
|
|
||||||
|
.LINK
|
||||||
|
Get-GPO
|
||||||
|
#>
|
||||||
|
|
||||||
|
[CmdletBinding(DefaultParameterSetName = 'All',
|
||||||
|
ConfirmImpact = 'None')]
|
||||||
|
param
|
||||||
|
(
|
||||||
|
[Parameter(ParameterSetName = 'One', HelpMessage = 'Specify the name of the GPO',
|
||||||
|
Mandatory,
|
||||||
|
ValueFromPipeline,
|
||||||
|
ValueFromPipelineByPropertyName)]
|
||||||
|
[ValidateNotNullOrEmpty()]
|
||||||
|
[Alias('GPO')]
|
||||||
|
[String[]]
|
||||||
|
$GPOName,
|
||||||
|
[Parameter(ParameterSetName = 'All',
|
||||||
|
ValueFromPipeline,
|
||||||
|
ValueFromPipelineByPropertyName)]
|
||||||
|
[Switch]
|
||||||
|
$All
|
||||||
|
)
|
||||||
|
|
||||||
|
process
|
||||||
|
{
|
||||||
|
foreach ($DomainController in ((Get-ADDomainController -ErrorAction Stop -ErrorVariable ErrorProcessGetDC -Filter *).hostname))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if ($psBoundParameters['GPOName'])
|
||||||
|
{
|
||||||
|
foreach ($GPOItem in $GPOName)
|
||||||
|
{
|
||||||
|
$GPO = (Get-GPO -Name $GPOItem -Server $DomainController -ErrorAction Stop -ErrorVariable ErrorProcessGetGPO)
|
||||||
|
|
||||||
|
[pscustomobject][ordered] @{
|
||||||
|
GroupPolicyName = $GPOItem
|
||||||
|
DomainController = $DomainController
|
||||||
|
UserVersion = $GPO.User.DSVersion
|
||||||
|
UserSysVolVersion = $GPO.User.SysvolVersion
|
||||||
|
ComputerVersion = $GPO.Computer.DSVersion
|
||||||
|
ComputerSysVolVersion = $GPO.Computer.SysvolVersion
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($psBoundParameters['All'])
|
||||||
|
{
|
||||||
|
$GPOList = (Get-GPO -All -Server $DomainController -ErrorAction Stop -ErrorVariable ErrorProcessGetGPOAll)
|
||||||
|
|
||||||
|
foreach ($GPO in $GPOList)
|
||||||
|
{
|
||||||
|
[pscustomobject][ordered] @{
|
||||||
|
GroupPolicyName = $GPO.DisplayName
|
||||||
|
DomainController = $DomainController
|
||||||
|
UserVersion = $GPO.User.DSVersion
|
||||||
|
UserSysVolVersion = $GPO.User.SysvolVersion
|
||||||
|
ComputerVersion = $GPO.Computer.DSVersion
|
||||||
|
ComputerSysVolVersion = $GPO.Computer.SysvolVersion
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
Write-Warning -Message '[PROCESS] Something wrong happened'
|
||||||
|
|
||||||
|
if ($ErrorProcessGetDC)
|
||||||
|
{
|
||||||
|
Write-Warning -Message '[PROCESS] Error while running retrieving Domain Controllers with Get-ADDomainController'
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($ErrorProcessGetGPO)
|
||||||
|
{
|
||||||
|
Write-Warning -Message '[PROCESS] Error while running Get-GPO'
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($ErrorProcessGetGPOAll)
|
||||||
|
{
|
||||||
|
Write-Warning -Message '[PROCESS] Error while running Get-GPO -All'
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Warning -Message "[PROCESS] $($Error[0].exception.message)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#region LICENSE
|
||||||
|
<#
|
||||||
|
BSD 3-Clause License
|
||||||
|
|
||||||
|
Copyright (c) 2021, enabling Technology
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||||
|
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
#>
|
||||||
|
#endregion LICENSE
|
||||||
|
|
||||||
|
#region DISCLAIMER
|
||||||
|
<#
|
||||||
|
DISCLAIMER:
|
||||||
|
- Use at your own risk, etc.
|
||||||
|
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
|
||||||
|
- This is a third-party Software
|
||||||
|
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
|
||||||
|
- The Software is not supported by Microsoft Corp (MSFT)
|
||||||
|
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
|
||||||
|
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
|
||||||
|
#>
|
||||||
|
#endregion DISCLAIMER
|
||||||
@@ -0,0 +1,210 @@
|
|||||||
|
function Get-enADGroupChange
|
||||||
|
{
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Retrieve information about changed Active Directory groups
|
||||||
|
|
||||||
|
.DESCRIPTION
|
||||||
|
Retrieve information about one, or more, changed Active Directory groups
|
||||||
|
|
||||||
|
.PARAMETER Server
|
||||||
|
Active DirectoryDomain Controller to querry.
|
||||||
|
Default is the logon server
|
||||||
|
|
||||||
|
.PARAMETER MonitorGroup
|
||||||
|
Group to monitor, multi value is supported.
|
||||||
|
Defaults to all Groups with admins.
|
||||||
|
|
||||||
|
Specifies an Active Directory object by providing one of the following property values. The identifier in
|
||||||
|
parentheses is the LDAP display name for the attribute.
|
||||||
|
|
||||||
|
Distinguished Name
|
||||||
|
|
||||||
|
Example: CN=DOM-ADM,OU=groups,OU=asia,DC=corp,DC=contoso,DC=com
|
||||||
|
|
||||||
|
GUID (objectGUID)
|
||||||
|
|
||||||
|
Example: 599c3d2e-f72d-4d20-8a88-030d99495f20
|
||||||
|
|
||||||
|
The cmdlet searches the default naming context or partition to find the object. If two or more objects are
|
||||||
|
found, the cmdlet returns a non-terminating error.
|
||||||
|
|
||||||
|
.PARAMETER Hour
|
||||||
|
Period to query, value in hours
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
Get-enADGroupChange
|
||||||
|
|
||||||
|
Retrieve information about changed Active Directory groups
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
Get-enADGroupChange -MonitorGroup 'DOM-ADM'
|
||||||
|
|
||||||
|
Retrieve information about changes to theActive Directory group DOM-ADM
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
Get-enADGroupChange -Server DC03
|
||||||
|
|
||||||
|
Retrieve information about changed Active Directory groups on DC03
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
Get-enADGroupChange -Hour 72
|
||||||
|
|
||||||
|
Retrieve information about Active Directory groups that have been changed within the last 72 hours
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
Get-enADGroupChange -Server DC02 -Hour 96
|
||||||
|
|
||||||
|
Retrieve information about Active Directory groups that have been changed within the last 96 hours on DC02
|
||||||
|
|
||||||
|
.OUTPUTS
|
||||||
|
psobject
|
||||||
|
|
||||||
|
.NOTES
|
||||||
|
Releasenotes:
|
||||||
|
1.0.1 2019-07-26 Refactored, License change to BSD 3-Clause
|
||||||
|
1.0.0 2019-01-01 Initial Version
|
||||||
|
|
||||||
|
THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER.
|
||||||
|
|
||||||
|
.INPUTS
|
||||||
|
String
|
||||||
|
Int
|
||||||
|
|
||||||
|
.LINK
|
||||||
|
https://www.enatec.io
|
||||||
|
|
||||||
|
.LINK
|
||||||
|
Get-ADDomainController
|
||||||
|
|
||||||
|
.LINK
|
||||||
|
Get-ADGroup
|
||||||
|
|
||||||
|
.LINK
|
||||||
|
Get-ADReplicationAttributeMetadata
|
||||||
|
|
||||||
|
.LINK
|
||||||
|
Get-Date
|
||||||
|
#>
|
||||||
|
|
||||||
|
[CmdletBinding(ConfirmImpact = 'None')]
|
||||||
|
[OutputType([psobject])]
|
||||||
|
param
|
||||||
|
(
|
||||||
|
[Parameter(ValueFromPipeline,
|
||||||
|
ValueFromPipelineByPropertyName)]
|
||||||
|
[ValidateNotNullOrEmpty()]
|
||||||
|
[Alias('DomainController')]
|
||||||
|
[string]
|
||||||
|
$Server = (Get-ADDomainController -Discover | Select-Object -ExpandProperty HostName),
|
||||||
|
[Parameter(ValueFromPipeline,
|
||||||
|
ValueFromPipelineByPropertyName)]
|
||||||
|
[ValidateNotNullOrEmpty()]
|
||||||
|
[Alias('Group')]
|
||||||
|
[string[]]
|
||||||
|
$MonitorGroup = (Get-ADGroup -Filter ' AdminCount -eq 1 ' -Server $Server | Select-Object -ExpandProperty ObjectGUID),
|
||||||
|
[Parameter(ValueFromPipeline,
|
||||||
|
ValueFromPipelineByPropertyName)]
|
||||||
|
[Alias('Period')]
|
||||||
|
[int]
|
||||||
|
$Hour = 24
|
||||||
|
)
|
||||||
|
|
||||||
|
begin
|
||||||
|
{
|
||||||
|
# Create a new object
|
||||||
|
$Members = @()
|
||||||
|
|
||||||
|
Write-Verbose -Message ('Processing group {0} via Server {1}' -f $MonitorGroup, $Server)
|
||||||
|
}
|
||||||
|
|
||||||
|
process
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
foreach ($SingleGroup in $MonitorGroup)
|
||||||
|
{
|
||||||
|
Write-Verbose -Message ('Processing group {0}' -f $SingleGroup)
|
||||||
|
|
||||||
|
# Querry the info and add to the Object
|
||||||
|
$Members += (Get-ADReplicationAttributeMetadata -Server $Server -Object $SingleGroup -ShowAllLinkedValues | Where-Object -FilterScript {
|
||||||
|
$_.IsLinkValue
|
||||||
|
} | Select-Object -Property @{
|
||||||
|
name = 'GroupDN'
|
||||||
|
expression = {
|
||||||
|
$SingleGroup.DistinguishedName
|
||||||
|
}
|
||||||
|
}, @{
|
||||||
|
name = 'GroupName'
|
||||||
|
expression = {
|
||||||
|
$SingleGroup.Name
|
||||||
|
}
|
||||||
|
}, *)
|
||||||
|
}
|
||||||
|
|
||||||
|
# Filter
|
||||||
|
$Members | Where-Object -FilterScript {
|
||||||
|
$_.LastOriginatingChangeTime -gt (Get-Date).AddHours(-1 * $Hour)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
#region ErrorHandler
|
||||||
|
# get error record
|
||||||
|
[Management.Automation.ErrorRecord]$e = $_
|
||||||
|
|
||||||
|
# retrieve information about runtime error
|
||||||
|
$info = [PSCustomObject]@{
|
||||||
|
Exception = $e.Exception.Message
|
||||||
|
Reason = $e.CategoryInfo.Reason
|
||||||
|
Target = $e.CategoryInfo.TargetName
|
||||||
|
Script = $e.InvocationInfo.ScriptName
|
||||||
|
Line = $e.InvocationInfo.ScriptLineNumber
|
||||||
|
Column = $e.InvocationInfo.OffsetInLine
|
||||||
|
}
|
||||||
|
|
||||||
|
$info | Out-String | Write-Verbose
|
||||||
|
|
||||||
|
Write-Error -Message ($info.Exception) -ErrorAction Stop
|
||||||
|
|
||||||
|
# Only here to catch a global ErrorAction overwrite
|
||||||
|
break
|
||||||
|
#endregion ErrorHandler
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
end
|
||||||
|
{
|
||||||
|
$Members = $null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#region LICENSE
|
||||||
|
<#
|
||||||
|
BSD 3-Clause License
|
||||||
|
|
||||||
|
Copyright (c) 2021, enabling Technology
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||||
|
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
#>
|
||||||
|
#endregion LICENSE
|
||||||
|
|
||||||
|
#region DISCLAIMER
|
||||||
|
<#
|
||||||
|
DISCLAIMER:
|
||||||
|
- Use at your own risk, etc.
|
||||||
|
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
|
||||||
|
- This is a third-party Software
|
||||||
|
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
|
||||||
|
- The Software is not supported by Microsoft Corp (MSFT)
|
||||||
|
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
|
||||||
|
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
|
||||||
|
#>
|
||||||
|
#endregion DISCLAIMER
|
||||||
@@ -0,0 +1,378 @@
|
|||||||
|
function Get-enADObject
|
||||||
|
{
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Export Active Directory Objects
|
||||||
|
|
||||||
|
.DESCRIPTION
|
||||||
|
Export Active Directory Objects
|
||||||
|
|
||||||
|
.PARAMETER ADObjectFilter
|
||||||
|
Provide specific AD Objects to report on. Otherwise, all AD Objects will be reported. Please review the examples provided.
|
||||||
|
|
||||||
|
.PARAMETER DetailedReport
|
||||||
|
Provides a full report of all attributes. Otherwise, only a refined report will be given.
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
PS ~> Get-enADObject | Export-Csv C:\scripts\PowerShell\Reports\ADObjects.csv -notypeinformation -encoding UTF8
|
||||||
|
|
||||||
|
Export Active Directory Objects
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
PS ~> {objectclass -eq "publicFolder"} | Get-enADObject -DetailedReport | Export-Csv C:\scripts\PowerShell\Reports\PFs.csv -NoTypeInformation -Encoding UTF8
|
||||||
|
|
||||||
|
Export Active Directory Objects
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
PS ~> '{proxyaddresses -like "*contoso.com"}' | Get-enADObject | Export-Csv C:\scripts\PowerShell\Reports\ADObjects.csv -notypeinformation -encoding UTF8
|
||||||
|
|
||||||
|
Export Active Directory Objects
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
PS ~> '{proxyaddresses -like "*contoso.com"}' | Get-enADObject -DetailedReport | Export-Csv C:\scripts\PowerShell\Reports\ADObjects_Detailed.csv -notypeinformation -encoding UTF8
|
||||||
|
|
||||||
|
Export Active Directory Objects
|
||||||
|
|
||||||
|
.OUTPUTS
|
||||||
|
PSObject
|
||||||
|
|
||||||
|
.NOTES
|
||||||
|
Releasenotes:
|
||||||
|
1.0.1 2019-07-26 Refactored, License change to BSD 3-Clause
|
||||||
|
1.0.0 2019-01-01 Initial Version
|
||||||
|
|
||||||
|
THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER.
|
||||||
|
|
||||||
|
Dependencies:
|
||||||
|
Active Directory PowerShell Module
|
||||||
|
|
||||||
|
.LINK
|
||||||
|
https://www.enatec.io
|
||||||
|
|
||||||
|
.LINK
|
||||||
|
Get-ADObject
|
||||||
|
#>
|
||||||
|
[CmdletBinding(ConfirmImpact = 'None')]
|
||||||
|
[OutputType([psobject])]
|
||||||
|
param (
|
||||||
|
[switch]
|
||||||
|
$DetailedReport,
|
||||||
|
[Parameter(ValueFromPipeline)]
|
||||||
|
[string[]]
|
||||||
|
$ADObjectFilter
|
||||||
|
)
|
||||||
|
|
||||||
|
begin
|
||||||
|
{
|
||||||
|
if ($DetailedReport)
|
||||||
|
{
|
||||||
|
$Selectproperties = @(
|
||||||
|
'DisplayName', 'UserPrincipalName', 'mail', 'CN', 'mailNickname', 'Name', 'GivenName', 'Surname', 'StreetAddress'
|
||||||
|
'City', 'State', 'Country', 'PostalCode', 'Company', 'Title', 'Department', 'Description', 'OfficePhone'
|
||||||
|
'MobilePhone', 'HomePhone', 'Fax', 'SamAccountName', 'DistinguishedName', 'Office', 'Enabled'
|
||||||
|
'whenChanged', 'whenCreated', 'adminCount', 'AccountNotDelegated', 'AllowReversiblePasswordEncryption'
|
||||||
|
'CannotChangePassword', 'Deleted', 'DoesNotRequirePreAuth', 'HomedirRequired', 'isDeleted', 'LockedOut'
|
||||||
|
'mAPIRecipient', 'mDBUseDefaults', 'MNSLogonAccount', 'msExchHideFromAddressLists'
|
||||||
|
'msNPAllowDialin', 'PasswordExpired', 'PasswordNeverExpires', 'PasswordNotRequired', 'ProtectedFromAccidentalDeletion'
|
||||||
|
'SmartcardLogonRequired', 'TrustedForDelegation', 'TrustedToAuthForDelegation', 'UseDESKeyOnly', 'logonHours'
|
||||||
|
'msExchMailboxGuid', 'replicationSignature', 'AccountExpirationDate', 'AccountLockoutTime', 'Created', 'createTimeStamp'
|
||||||
|
'LastBadPasswordAttempt', 'LastLogonDate', 'Modified', 'modifyTimeStamp', 'msTSExpireDate', 'PasswordLastSet'
|
||||||
|
'msExchMailboxSecurityDescriptor', 'nTSecurityDescriptor', 'BadLogonCount', 'codePage', 'countryCode'
|
||||||
|
'deletedItemFlags', 'dLMemDefault', 'garbageCollPeriod', 'instanceType', 'msDS-SupportedEncryptionTypes'
|
||||||
|
'msDS-User-Account-Control-Computed', 'msExchALObjectVersion', 'msExchMobileMailboxFlags', 'msExchRecipientDisplayType'
|
||||||
|
'msExchUserAccountControl', 'primaryGroupID', 'replicatedObjectVersion', 'sAMAccountType', 'sDRightsEffective'
|
||||||
|
'userAccountControl', 'accountExpires', 'lastLogonTimestamp', 'lockoutTime', 'msExchRecipientTypeDetails', 'msExchVersion'
|
||||||
|
'pwdLastSet', 'uSNChanged', 'uSNCreated', 'ObjectGUID', 'objectSid', 'SID', 'autoReplyMessage', 'CanonicalName'
|
||||||
|
'displayNamePrintable', 'Division', 'EmployeeID', 'EmployeeNumber', 'HomeDirectory', 'HomeDrive', 'homeMDB', 'homeMTA'
|
||||||
|
'HomePage', 'Initials', 'LastKnownParent', 'legacyExchangeDN', 'LogonWorkstations'
|
||||||
|
'Manager', 'msExchHomeServerName', 'msExchUserCulture', 'msTSLicenseVersion', 'msTSManagingLS'
|
||||||
|
'ObjectCategory', 'ObjectClass', 'Organization', 'OtherName', 'POBox', 'PrimaryGroup'
|
||||||
|
'ProfilePath', 'ScriptPath', 'sn', 'textEncodedORAddress', 'userParameters'
|
||||||
|
)
|
||||||
|
|
||||||
|
$CalculatedProps = @(
|
||||||
|
@{
|
||||||
|
n = 'OU'
|
||||||
|
e = {
|
||||||
|
$_.DistinguishedName -replace '^.+?,(?=(OU|CN)=)'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
@{
|
||||||
|
n = 'proxyAddresses'
|
||||||
|
e = {
|
||||||
|
($_.proxyAddresses | Where-Object -FilterScript {
|
||||||
|
$_ -ne $null
|
||||||
|
}) -join '|'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
@{
|
||||||
|
n = 'altRecipientBL'
|
||||||
|
e = {
|
||||||
|
($_.altRecipientBL | Where-Object -FilterScript {
|
||||||
|
$_ -ne $null
|
||||||
|
}) -join ';'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
@{
|
||||||
|
n = 'AuthenticationPolicy'
|
||||||
|
e = {
|
||||||
|
($_.AuthenticationPolicy | Where-Object -FilterScript {
|
||||||
|
$_ -ne $null
|
||||||
|
}) -join ';'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
@{
|
||||||
|
n = 'AuthenticationPolicySilo'
|
||||||
|
e = {
|
||||||
|
($_.AuthenticationPolicySilo | Where-Object -FilterScript {
|
||||||
|
$_ -ne $null
|
||||||
|
}) -join ';'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
@{
|
||||||
|
n = 'Certificates'
|
||||||
|
e = {
|
||||||
|
($_.Certificates | Where-Object -FilterScript {
|
||||||
|
$_ -ne $null
|
||||||
|
}) -join ';'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
@{
|
||||||
|
n = 'CompoundIdentitySupported'
|
||||||
|
e = {
|
||||||
|
($_.CompoundIdentitySupported | Where-Object -FilterScript {
|
||||||
|
$_ -ne $null
|
||||||
|
}) -join ';'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
@{
|
||||||
|
n = 'dSCorePropagationData'
|
||||||
|
e = {
|
||||||
|
($_.dSCorePropagationData | Where-Object -FilterScript {
|
||||||
|
$_ -ne $null
|
||||||
|
}) -join ';'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
@{
|
||||||
|
n = 'KerberosEncryptionType'
|
||||||
|
e = {
|
||||||
|
($_.KerberosEncryptionType | Where-Object -FilterScript {
|
||||||
|
$_ -ne $null
|
||||||
|
}) -join ';'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
@{
|
||||||
|
n = 'managedObjects'
|
||||||
|
e = {
|
||||||
|
($_.managedObjects | Where-Object -FilterScript {
|
||||||
|
$_ -ne $null
|
||||||
|
}) -join ';'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
@{
|
||||||
|
n = 'MemberOf'
|
||||||
|
e = {
|
||||||
|
($_.MemberOf | Where-Object -FilterScript {
|
||||||
|
$_ -ne $null
|
||||||
|
}) -join ';'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
@{
|
||||||
|
n = 'msExchADCGlobalNames'
|
||||||
|
e = {
|
||||||
|
($_.msExchADCGlobalNames | Where-Object -FilterScript {
|
||||||
|
$_ -ne $null
|
||||||
|
}) -join ';'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
@{
|
||||||
|
n = 'msExchPoliciesExcluded'
|
||||||
|
e = {
|
||||||
|
($_.msExchPoliciesExcluded | Where-Object -FilterScript {
|
||||||
|
$_ -ne $null
|
||||||
|
}) -join ';'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
@{
|
||||||
|
n = 'PrincipalsAllowedToDelegateToAccount'
|
||||||
|
e = {
|
||||||
|
($_.PrincipalsAllowedToDelegateToAccount | Where-Object -FilterScript {
|
||||||
|
$_ -ne $null
|
||||||
|
}) -join ';'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
@{
|
||||||
|
n = 'protocolSettings'
|
||||||
|
e = {
|
||||||
|
($_.protocolSettings | Where-Object -FilterScript {
|
||||||
|
$_ -ne $null
|
||||||
|
}) -join ';'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
@{
|
||||||
|
n = 'publicDelegatesBL'
|
||||||
|
e = {
|
||||||
|
($_.publicDelegatesBL | Where-Object -FilterScript {
|
||||||
|
$_ -ne $null
|
||||||
|
}) -join ';'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
@{
|
||||||
|
n = 'securityProtocol'
|
||||||
|
e = {
|
||||||
|
($_.securityProtocol | Where-Object -FilterScript {
|
||||||
|
$_ -ne $null
|
||||||
|
}) -join ';'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
@{
|
||||||
|
n = 'ServicePrincipalNames'
|
||||||
|
e = {
|
||||||
|
($_.ServicePrincipalNames | Where-Object -FilterScript {
|
||||||
|
$_ -ne $null
|
||||||
|
}) -join ';'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
@{
|
||||||
|
n = 'showInAddressBook'
|
||||||
|
e = {
|
||||||
|
($_.showInAddressBook | Where-Object -FilterScript {
|
||||||
|
$_ -ne $null
|
||||||
|
}) -join ';'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
@{
|
||||||
|
n = 'SIDHistory'
|
||||||
|
e = {
|
||||||
|
($_.SIDHistory | Where-Object -FilterScript {
|
||||||
|
$_ -ne $null
|
||||||
|
}) -join ';'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
@{
|
||||||
|
n = 'userCertificate'
|
||||||
|
e = {
|
||||||
|
($_.userCertificate | Where-Object -FilterScript {
|
||||||
|
$_ -ne $null
|
||||||
|
}) -join ';'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
$ExtensionAttribute = @(
|
||||||
|
'extensionAttribute1', 'extensionAttribute2', 'extensionAttribute3', 'extensionAttribute4', 'extensionAttribute5'
|
||||||
|
'extensionAttribute6', 'extensionAttribute7', 'extensionAttribute8', 'extensionAttribute9', 'extensionAttribute10'
|
||||||
|
'extensionAttribute11', 'extensionAttribute12', 'extensionAttribute13', 'extensionAttribute14', 'extensionAttribute15'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$Props = @(
|
||||||
|
'DisplayName', 'UserPrincipalName', 'mail', 'CN', 'mailNickname', 'Name', 'GivenName', 'Surname', 'StreetAddress',
|
||||||
|
'City', 'State', 'Country', 'PostalCode', 'Company', 'Title', 'Department', 'Description', 'OfficePhone'
|
||||||
|
'MobilePhone', 'HomePhone', 'Fax', 'SamAccountName', 'DistinguishedName', 'Office', 'Enabled'
|
||||||
|
'whenChanged', 'whenCreated', 'adminCount', 'Memberof', 'msExchPoliciesExcluded', 'proxyAddresses'
|
||||||
|
)
|
||||||
|
|
||||||
|
$Selectproperties = @(
|
||||||
|
'DisplayName', 'UserPrincipalName', 'mail', 'CN', 'mailNickname', 'Name', 'GivenName', 'Surname', 'StreetAddress',
|
||||||
|
'City', 'State', 'Country', 'PostalCode', 'Company', 'Title', 'Department', 'Description', 'OfficePhone'
|
||||||
|
'MobilePhone', 'HomePhone', 'Fax', 'SamAccountName', 'DistinguishedName', 'Office', 'Enabled'
|
||||||
|
'whenChanged', 'whenCreated', 'adminCount'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
$CalculatedProps = @(
|
||||||
|
@{
|
||||||
|
n = 'proxyAddresses'
|
||||||
|
e = {
|
||||||
|
($_.proxyAddresses | Where-Object -FilterScript {
|
||||||
|
$_ -ne $null
|
||||||
|
}) -join '|'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
@{
|
||||||
|
n = 'OU'
|
||||||
|
e = {
|
||||||
|
$_.DistinguishedName -replace '^.+?,(?=(OU|CN)=)'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
@{
|
||||||
|
n = 'MemberOf'
|
||||||
|
e = {
|
||||||
|
($_.MemberOf | Where-Object -FilterScript {
|
||||||
|
$_ -ne $null
|
||||||
|
}) -join ';'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
@{
|
||||||
|
n = 'msExchPoliciesExcluded'
|
||||||
|
e = {
|
||||||
|
($_.msExchPoliciesExcluded | Where-Object -FilterScript {
|
||||||
|
$_ -ne $null
|
||||||
|
}) -join ';'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
process
|
||||||
|
{
|
||||||
|
if ($ADObjectFilter)
|
||||||
|
{
|
||||||
|
foreach ($CurADObjectFilter in $ADObjectFilter)
|
||||||
|
{
|
||||||
|
if (! $DetailedReport)
|
||||||
|
{
|
||||||
|
Get-ADObject -Filter $CurADObjectFilter -Properties $Props -ResultSetSize $null | Select-Object -Property ($Selectproperties + $CalculatedProps)
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Get-ADObject -Filter $CurADObjectFilter -Properties * -ResultSetSize $null | Select-Object -Property ($Selectproperties + $CalculatedProps + $ExtensionAttribute)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (! $DetailedReport)
|
||||||
|
{
|
||||||
|
Get-ADObject -Filter * -Properties $Props -ResultSetSize $null | Select-Object -Property ($Selectproperties + $CalculatedProps)
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Get-ADObject -Filter * -Properties * -ResultSetSize $null | Select-Object -Property ($Selectproperties + $CalculatedProps + $ExtensionAttribute)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#region LICENSE
|
||||||
|
<#
|
||||||
|
BSD 3-Clause License
|
||||||
|
|
||||||
|
Copyright (c) 2021, enabling Technology
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||||
|
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
#>
|
||||||
|
#endregion LICENSE
|
||||||
|
|
||||||
|
#region DISCLAIMER
|
||||||
|
<#
|
||||||
|
DISCLAIMER:
|
||||||
|
- Use at your own risk, etc.
|
||||||
|
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
|
||||||
|
- This is a third-party Software
|
||||||
|
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
|
||||||
|
- The Software is not supported by Microsoft Corp (MSFT)
|
||||||
|
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
|
||||||
|
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
|
||||||
|
#>
|
||||||
|
#endregion DISCLAIMER
|
||||||
@@ -0,0 +1,178 @@
|
|||||||
|
function Get-enADServicePrincipalName
|
||||||
|
{
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Retrieves all Service Principal Names (SPNs)
|
||||||
|
|
||||||
|
.DESCRIPTION
|
||||||
|
Retrieves all Service Principal Names (SPNs) from Active Directory
|
||||||
|
|
||||||
|
.INPUTS
|
||||||
|
NONE
|
||||||
|
|
||||||
|
.OUTPUTS
|
||||||
|
PSObject
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
PS /> Get-enADServicePrincipalName
|
||||||
|
|
||||||
|
Retrieves all Service Principal Names (SPNs) from Active Directory
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
PS ~> Get-enADServicePrincipalName | Where-Object -FilterScript { $_.ObjectClass -eq 'user' }
|
||||||
|
|
||||||
|
Retrieves all user class Service Principal Names (SPNs) from Active Directory
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
PS ~> Get-enADServicePrincipalName | Where-Object -FilterScript { $_.DNSHostName -like 'server01.contoso.com' }
|
||||||
|
|
||||||
|
Retrieves all Service Principal Names (SPNs) for the Server 'server01.contoso.com' from Active Directory
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
PS ~> Get-enADServicePrincipalName | Where-Object -FilterScript { $_.Name -like '*Krb*' }
|
||||||
|
|
||||||
|
Retrieves all Kerberos related Service Principal Names (SPNs) from Active Directory
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
PS ~> Get-enADServicePrincipalName | Where-Object -FilterScript { $_.SPN -like '*Krb*' }
|
||||||
|
|
||||||
|
Retrieves all Kerberos related Service Principal Names (SPNs) from Active Directory
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
PS ~> Get-enADServicePrincipalName | Export-Csv -Path C:\scripts\PowerShell\Reports\ADServicePrincipalNames.csv
|
||||||
|
|
||||||
|
Retrieves all Service Principal Names (SPNs) from Active Directory and export them to a CSV
|
||||||
|
|
||||||
|
.NOTES
|
||||||
|
Releasenotes:
|
||||||
|
1.0.1 2019-07-26 Refactored, License change to BSD 3-Clause
|
||||||
|
1.0.0 2019-01-01 Initial Version
|
||||||
|
|
||||||
|
THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER.
|
||||||
|
|
||||||
|
Dependencies:
|
||||||
|
Active Directory PowerShell Module
|
||||||
|
|
||||||
|
.LINK
|
||||||
|
https://www.enatec.io
|
||||||
|
|
||||||
|
.LINK
|
||||||
|
Get-ADObject
|
||||||
|
#>
|
||||||
|
[CmdletBinding(ConfirmImpact = 'None')]
|
||||||
|
[OutputType([psobject])]
|
||||||
|
param ()
|
||||||
|
|
||||||
|
begin
|
||||||
|
{
|
||||||
|
# Create a new Object
|
||||||
|
$AllObject = @()
|
||||||
|
}
|
||||||
|
|
||||||
|
process
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
# We use Get-ADObject because this seems to be fast enough
|
||||||
|
$paramGetADObject = @{
|
||||||
|
Filter = "(objectClass -eq 'user') -or (objectClass -eq 'computer') -and (servicePrincipalName -like '*')"
|
||||||
|
Properties = 'Name', 'servicePrincipalName', 'DistinguishedName', 'ObjectClass', 'DNSHostName', 'whenCreated'
|
||||||
|
}
|
||||||
|
$AllServicePrincipalNames = (Get-ADObject @paramGetADObject)
|
||||||
|
|
||||||
|
# Loop over the List we got from Get-ADObject
|
||||||
|
foreach ($SingleServicePrincipalName in $AllServicePrincipalNames)
|
||||||
|
{
|
||||||
|
# Get the values for the Service Principal Name
|
||||||
|
$ObjectClass = $SingleServicePrincipalName.ObjectClass
|
||||||
|
$DistinguishedName = $SingleServicePrincipalName.DistinguishedName
|
||||||
|
$Name = $SingleServicePrincipalName.Name
|
||||||
|
$whenCreated = $SingleServicePrincipalName.whenCreated
|
||||||
|
$DNSHostName = $SingleServicePrincipalName.DNSHostName
|
||||||
|
|
||||||
|
# Loop over all Service Principal Names - Remeber, there could be more then one Service Principal Names value per record
|
||||||
|
foreach ($ServicePrincipalName in $SingleServicePrincipalName.servicePrincipalName)
|
||||||
|
{
|
||||||
|
# Create a new Object
|
||||||
|
$SingleObject = (New-Object -TypeName PSObject -Property @{
|
||||||
|
Name = $Name
|
||||||
|
SPN = $ServicePrincipalName
|
||||||
|
ObjectClass = $ObjectClass
|
||||||
|
DistinguishedName = $DistinguishedName
|
||||||
|
WhenCreated = $whenCreated
|
||||||
|
DNSHostName = $DNSHostName
|
||||||
|
})
|
||||||
|
|
||||||
|
# Add the Values to the List
|
||||||
|
$AllObject += $SingleObject
|
||||||
|
|
||||||
|
# Cleanup
|
||||||
|
$SingleObject = $null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
#region ErrorHandler
|
||||||
|
# get error record
|
||||||
|
[Management.Automation.ErrorRecord]$e = $_
|
||||||
|
|
||||||
|
# retrieve information about runtime error
|
||||||
|
$info = [PSCustomObject]@{
|
||||||
|
Exception = $e.Exception.Message
|
||||||
|
Reason = $e.CategoryInfo.Reason
|
||||||
|
Target = $e.CategoryInfo.TargetName
|
||||||
|
Script = $e.InvocationInfo.ScriptName
|
||||||
|
Line = $e.InvocationInfo.ScriptLineNumber
|
||||||
|
Column = $e.InvocationInfo.OffsetInLine
|
||||||
|
}
|
||||||
|
|
||||||
|
$info | Out-String | Write-Verbose
|
||||||
|
|
||||||
|
Write-Error -Message ($info.Exception) -ErrorAction Stop
|
||||||
|
|
||||||
|
# Only here to catch a global ErrorAction overwrite
|
||||||
|
break
|
||||||
|
#endregion ErrorHandler
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
end
|
||||||
|
{
|
||||||
|
# Dump
|
||||||
|
$AllObject
|
||||||
|
|
||||||
|
# Cleanup
|
||||||
|
$AllObject = $null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#region LICENSE
|
||||||
|
<#
|
||||||
|
BSD 3-Clause License
|
||||||
|
|
||||||
|
Copyright (c) 2021, enabling Technology
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||||
|
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
#>
|
||||||
|
#endregion LICENSE
|
||||||
|
|
||||||
|
#region DISCLAIMER
|
||||||
|
<#
|
||||||
|
DISCLAIMER:
|
||||||
|
- Use at your own risk, etc.
|
||||||
|
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
|
||||||
|
- This is a third-party Software
|
||||||
|
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
|
||||||
|
- The Software is not supported by Microsoft Corp (MSFT)
|
||||||
|
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
|
||||||
|
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
|
||||||
|
#>
|
||||||
|
#endregion DISCLAIMER
|
||||||
@@ -0,0 +1,132 @@
|
|||||||
|
function Get-enADSiteAndSubnetInfo
|
||||||
|
{
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Retrieve Site names, subnets names and descriptions.
|
||||||
|
|
||||||
|
.DESCRIPTION
|
||||||
|
Retrieve Site names, subnets names and descriptions from the Active Directory
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
PS ~> Get-enADSiteAndSubnetInfo
|
||||||
|
|
||||||
|
Retrieve Site names, subnets names and descriptions from the Active Directory
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
PS ~> Get-enADSiteAndSubnetInfo | Export-Csv -Path C:\scripts\PowerShell\Reports\ADSiteInventory.csv
|
||||||
|
|
||||||
|
Retrieve Site names, subnets names and descriptions from the Active Directory
|
||||||
|
|
||||||
|
.OUTPUTS
|
||||||
|
PSObject
|
||||||
|
|
||||||
|
.NOTES
|
||||||
|
Releasenotes:
|
||||||
|
1.0.1 2019-07-26 Refactored, License change to BSD 3-Clause
|
||||||
|
1.0.0 2019-01-01 Initial Version
|
||||||
|
|
||||||
|
THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER.
|
||||||
|
|
||||||
|
Dependencies:
|
||||||
|
Active Directory PowerShell Module
|
||||||
|
|
||||||
|
.LINK
|
||||||
|
https://www.enatec.io
|
||||||
|
#>
|
||||||
|
[CmdletBinding(ConfirmImpact = 'None')]
|
||||||
|
[OutputType([psobject])]
|
||||||
|
param ()
|
||||||
|
|
||||||
|
begin
|
||||||
|
{
|
||||||
|
Write-Verbose -Message '[BEGIN] Starting Script...'
|
||||||
|
}
|
||||||
|
|
||||||
|
process
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
# Domain and Sites Information
|
||||||
|
$Forest = ([DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest())
|
||||||
|
$SiteInfo = ([DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest().Sites)
|
||||||
|
|
||||||
|
# Forest Context
|
||||||
|
$ForestType = ([DirectoryServices.ActiveDirectory.DirectoryContexttype]'forest')
|
||||||
|
$ForestContext = (New-Object -TypeName System.DirectoryServices.ActiveDirectory.DirectoryContext -ArgumentList $ForestType, $Forest)
|
||||||
|
|
||||||
|
# Distinguished Name of the Configuration Partition
|
||||||
|
$Configuration = ([ADSI]'LDAP://RootDSE').configurationNamingContext
|
||||||
|
|
||||||
|
# Get the Subnet Container
|
||||||
|
$SubnetsContainer = ([ADSI]('LDAP://CN=Subnets,CN=Sites,{0}' -f $Configuration))
|
||||||
|
$SubnetsContainerchildren = ($SubnetsContainer.Children)
|
||||||
|
|
||||||
|
foreach ($item in $SiteInfo)
|
||||||
|
{
|
||||||
|
Write-Verbose -Message ('[PROCESS] SITE: {0}' -f $item.name)
|
||||||
|
|
||||||
|
$output = @{
|
||||||
|
Name = $item.name
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($i in $item.Subnets.name)
|
||||||
|
{
|
||||||
|
Write-Verbose -Message ('[PROCESS] SUBNET: {0}' -f $i)
|
||||||
|
|
||||||
|
$output.Subnet = $i
|
||||||
|
$SubnetAdditionalInfo = $SubnetsContainerchildren.Where( {
|
||||||
|
$_.name -match $i
|
||||||
|
})
|
||||||
|
|
||||||
|
Write-Verbose -Message ('[PROCESS] SUBNET: {0} - DESCRIPTION: {1}' -f $i, $SubnetAdditionalInfo.Description)
|
||||||
|
|
||||||
|
$output.Description = $($SubnetAdditionalInfo.Description)
|
||||||
|
|
||||||
|
Write-Verbose -Message '[PROCESS] OUTPUT INFO'
|
||||||
|
|
||||||
|
New-Object -TypeName PSObject -Property $output
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
Write-Warning -Message '[PROCESS] Something Wrong Happened'
|
||||||
|
Write-Warning -Message $Error[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
end
|
||||||
|
{
|
||||||
|
Write-Verbose -Message '[END] Script Completed!'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#region LICENSE
|
||||||
|
<#
|
||||||
|
BSD 3-Clause License
|
||||||
|
|
||||||
|
Copyright (c) 2021, enabling Technology
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||||
|
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
#>
|
||||||
|
#endregion LICENSE
|
||||||
|
|
||||||
|
#region DISCLAIMER
|
||||||
|
<#
|
||||||
|
DISCLAIMER:
|
||||||
|
- Use at your own risk, etc.
|
||||||
|
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
|
||||||
|
- This is a third-party Software
|
||||||
|
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
|
||||||
|
- The Software is not supported by Microsoft Corp (MSFT)
|
||||||
|
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
|
||||||
|
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
|
||||||
|
#>
|
||||||
|
#endregion DISCLAIMER
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
Function Get-enDomainInfo
|
||||||
|
{
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Retrieve domain information include site details
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
Get-enDomainInfo
|
||||||
|
|
||||||
|
.NOTES
|
||||||
|
#>
|
||||||
|
[CmdletBinding()]
|
||||||
|
Param ()
|
||||||
|
|
||||||
|
begin
|
||||||
|
{
|
||||||
|
$SelectProperties = 'Name', 'Forest', 'Parent', 'Children', 'DomainMode', 'DomainModeLevel', 'DomainControllers', 'PdcRoleOwner', 'RidRoleOwner', 'InfrastructureRoleOwner', 'Sites'
|
||||||
|
}
|
||||||
|
|
||||||
|
process
|
||||||
|
{
|
||||||
|
$CurrentDomain = [DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
|
||||||
|
$null = ($CurrentDomain | Add-Member -MemberType NoteProperty -Name Sites -Value ([DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest().Sites))
|
||||||
|
$Domain = ($CurrentDomain | Select-Object -Property $SelectProperties)
|
||||||
|
|
||||||
|
<#
|
||||||
|
switch($domainModeLevel)
|
||||||
|
{
|
||||||
|
{$domainModeLevel -like "0"} {"2000 Mixed/Native"}
|
||||||
|
{$domainModeLevel -like "1"} {"2003 Interim"}
|
||||||
|
{$domainModeLevel -like "2"} {"2003"}
|
||||||
|
{$domainModeLevel -like "3"} {"2008"}
|
||||||
|
{$domainModeLevel -like "4"} {"2008 R2"}
|
||||||
|
{$domainModeLevel -like "5"} {"2012"}
|
||||||
|
{$domainModeLevel -like "6"} {"2012 R2"}
|
||||||
|
{$domainModeLevel -like "7"} {"2016"}
|
||||||
|
default {"Unknown"}
|
||||||
|
}
|
||||||
|
#>
|
||||||
|
}
|
||||||
|
|
||||||
|
end
|
||||||
|
{
|
||||||
|
$Domain
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#region LICENSE
|
||||||
|
<#
|
||||||
|
BSD 3-Clause License
|
||||||
|
|
||||||
|
Copyright (c) 2021, enabling Technology
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||||
|
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
#>
|
||||||
|
#endregion LICENSE
|
||||||
|
|
||||||
|
#region DISCLAIMER
|
||||||
|
<#
|
||||||
|
DISCLAIMER:
|
||||||
|
- Use at your own risk, etc.
|
||||||
|
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
|
||||||
|
- This is a third-party Software
|
||||||
|
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
|
||||||
|
- The Software is not supported by Microsoft Corp (MSFT)
|
||||||
|
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
|
||||||
|
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
|
||||||
|
#>
|
||||||
|
#endregion DISCLAIMER
|
||||||
29
Powershell/PowerShell-collection/ActiveDirectory/LICENSE
Normal file
29
Powershell/PowerShell-collection/ActiveDirectory/LICENSE
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
BSD 3-Clause License
|
||||||
|
|
||||||
|
Copyright (c) 2021, enabling Technology <http://enatec.io>
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
* Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
* Neither the name of the copyright holder nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
# Legacy Notice
|
||||||
|
|
||||||
|
I no longer run Exchange, Skype for Business, or any other Office Server on Premises.
|
||||||
|
This is my personal [reaction](https://hochwald.net/microsoft-rolls-back-decision-to-take-away-internal-usage-rights-from-partners/) to the changes that Microsoft [announced](https://hochwald.net/microsoft-is-going-to-kill-internal-use-rights-benefit-for-partners/) for the Internal Use Rights (IUR) program. I know that they decided to reverse that changes and in theory, I could still legally use the software. However, I decided to decommission everything licensed under the terms of the Internal Use Rights (IUR) program.
|
||||||
|
In my opinion, the community always should have some benefits from the Internal Use Rights (IUR) program and/or Action Pack. Now that I decided to drop out, there will be no more such benefits.
|
||||||
|
|
||||||
|
I will _no longer maintain_ the scripts related to the Microsoft Office (on Premises) servers. They will remain here, but unmaintained. Fork the repository and maintain or extend them if you like to. The [License](https://github.com/jhochwald/PowerShell-collection/blob/master/LICENSE) allows that easily.
|
||||||
@@ -0,0 +1,413 @@
|
|||||||
|
#requires -Version 3.0 -Modules ActiveDirectory
|
||||||
|
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Tool that bulk imports or removes User pictures, based on AD Group Membership
|
||||||
|
|
||||||
|
.DESCRIPTION
|
||||||
|
Tool that bulk imports or removes User pictures, based on AD Group Membership
|
||||||
|
If a user is in both groups, the picture will be removed!
|
||||||
|
Idea based on my old tool to import Active Directory pictures.
|
||||||
|
They are a bit to tiny, so I use Exchange now to make them look better in Exchange and Skype.
|
||||||
|
|
||||||
|
.PARAMETER AddGroup
|
||||||
|
Active Directory Group with users that would like to have a picture.
|
||||||
|
For all Members of this group, the Tool will try to set an image.
|
||||||
|
|
||||||
|
.PARAMETER RemGroup
|
||||||
|
Active Directory Group with users that would like have have the picture removed.
|
||||||
|
For all Members of this group, the Tool will try to remove the existing image (If set).
|
||||||
|
|
||||||
|
.PARAMETER PictureDir
|
||||||
|
Directory that contains the pictures
|
||||||
|
|
||||||
|
.PARAMETER Extension
|
||||||
|
Extension of the pictures
|
||||||
|
|
||||||
|
.PARAMETER workaround
|
||||||
|
Workaround for Exchange 2016 on Windows Server 2016
|
||||||
|
|
||||||
|
.PARAMETER UPNDomain
|
||||||
|
The default Domain, to add to the UPN
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
# Use the Groups 'ADDPIXX' and 'NOPIXX' to Set/Remove the User Pictures
|
||||||
|
# There was an Issue with the User joerg.hochwald (Possible Picture Problem!
|
||||||
|
PS C:\> .\Set-ADAllUserPicture.ps1 -AddGroup 'ADDPIXX' -RemGroup 'NOPIXX' -PictureDir 'c:\upixx\' -workaround -UPNDomain 'jhochwald.com'
|
||||||
|
|
||||||
|
WARNING: Unable to set Image c:\upixx\joerg.hochwald.jpg for User joerg.hochwald
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
# Use the Groups 'ADDPIXX' and 'NOPIXX' to Set/Remove the User Pictures
|
||||||
|
# There was an Issue with the User jane.doe - Check that this user has a provissioned Mailbox (on Prem or Cloud)
|
||||||
|
PS C:\> .\Set-ADAllUserPicture.ps1 -AddGroup 'ADDPIXX' -RemGroup 'NOPIXX' -PictureDir 'c:\upixx\' -workaround -UPNDomain 'jhochwald.com'
|
||||||
|
|
||||||
|
WARNING: Unable to handle jane.doe - Check that this user has a valid Mailbox!
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
# Use the Groups 'ADDPIXX' and 'NOPIXX' to Set/Remove the User Pictures - Everything went well
|
||||||
|
PS C:\> .\Set-ADAllUserPicture.ps1 -AddGroup 'ADDPIXX' -RemGroup 'NOPIXX' -PictureDir 'c:\upixx\' -workaround -UPNDomain 'jhochwald.com'
|
||||||
|
|
||||||
|
WARNING: Unable to handle jane.doe - Check that this user has a valid Mailbox!
|
||||||
|
|
||||||
|
.NOTES
|
||||||
|
TODO: There is no logging! Only the Exchange RBAC logging is in use
|
||||||
|
TODO: A few error handlers are still missing
|
||||||
|
|
||||||
|
If a user is in both groups, the picture will be removed!
|
||||||
|
Verbose could be very verbose. This is due to the fact, that the complete Exchange logging will be shown!
|
||||||
|
|
||||||
|
There are a few possibilities for Warnings and Errors. (Mostly for missing things)
|
||||||
|
|
||||||
|
Disclaimer: The code is provided 'as is,' with all possible faults, defects or errors, and without warranty of any kind.
|
||||||
|
#>
|
||||||
|
param
|
||||||
|
(
|
||||||
|
[Parameter(Mandatory,
|
||||||
|
ValueFromPipeline,
|
||||||
|
ValueFromPipelineByPropertyName,
|
||||||
|
Position = 1,
|
||||||
|
HelpMessage = 'Active Directory Group with users that would like to have a picture')]
|
||||||
|
[ValidateNotNullOrEmpty()]
|
||||||
|
[Alias('positive')]
|
||||||
|
[string]
|
||||||
|
$AddGroup,
|
||||||
|
[Parameter(Mandatory,
|
||||||
|
ValueFromPipeline,
|
||||||
|
ValueFromPipelineByPropertyName,
|
||||||
|
Position = 2,
|
||||||
|
HelpMessage = 'Active Directory Group with users that would like have have the picture removed.')]
|
||||||
|
[ValidateNotNullOrEmpty()]
|
||||||
|
[string]
|
||||||
|
$RemGroup,
|
||||||
|
[Parameter(Mandatory,
|
||||||
|
ValueFromPipeline,
|
||||||
|
ValueFromPipelineByPropertyName,
|
||||||
|
Position = 3,
|
||||||
|
HelpMessage = 'Directory that contains the picures')]
|
||||||
|
[ValidateNotNullOrEmpty()]
|
||||||
|
[Alias('PixxDir')]
|
||||||
|
[string]
|
||||||
|
$PictureDir,
|
||||||
|
[Parameter(ValueFromPipeline,
|
||||||
|
ValueFromPipelineByPropertyName,
|
||||||
|
Position = 5)]
|
||||||
|
[Alias('defaultDomain')]
|
||||||
|
[string]
|
||||||
|
$UPNDomain,
|
||||||
|
[Parameter(ValueFromPipeline,
|
||||||
|
ValueFromPipelineByPropertyName,
|
||||||
|
Position = 4)]
|
||||||
|
[ValidateSet('png', 'jpg', 'gif', 'bmp')]
|
||||||
|
[ValidateNotNullOrEmpty()]
|
||||||
|
[string]
|
||||||
|
$Extension = 'jpg',
|
||||||
|
[switch]
|
||||||
|
$workaround = $false
|
||||||
|
)
|
||||||
|
|
||||||
|
begin
|
||||||
|
{
|
||||||
|
if ($workaround)
|
||||||
|
{
|
||||||
|
# Unsupported Workaround according to https://hochwald.net/workaround-for-get-help-issue-with-exchange-2016-on-windows-server-2016/
|
||||||
|
$null = (Add-PSSnapin -Name Microsoft.Exchange.Management.PowerShell.SnapIn)
|
||||||
|
}
|
||||||
|
|
||||||
|
# Cleanup
|
||||||
|
$AddUserPixx = $null
|
||||||
|
$NoUserPixx = $null
|
||||||
|
|
||||||
|
# Check the source directory string and fix it if needed
|
||||||
|
if (-not ($PictureDir).EndsWith('\'))
|
||||||
|
{
|
||||||
|
# Fix it
|
||||||
|
$PictureDir = $PictureDir + '\'
|
||||||
|
|
||||||
|
$paramWriteVerbose = @{
|
||||||
|
Message = 'Fixed the Source Directory String!'
|
||||||
|
}
|
||||||
|
Write-Verbose @paramWriteVerbose
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$paramGetADGroupMember = @{
|
||||||
|
Identity = $AddGroup
|
||||||
|
ErrorAction = 'Stop'
|
||||||
|
WarningAction = 'SilentlyContinue'
|
||||||
|
}
|
||||||
|
$AddUserPixx = (Get-ADGroupMember @paramGetADGroupMember | Select-Object -ExpandProperty samaccountname)
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
$paramWriteError = @{
|
||||||
|
Message = ('Unable to find {0}' -f $AddGroup)
|
||||||
|
ErrorAction = 'Stop'
|
||||||
|
}
|
||||||
|
Write-Error @paramWriteError
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$paramGetADGroupMember = @{
|
||||||
|
Identity = $RemGroup
|
||||||
|
ErrorAction = 'Stop'
|
||||||
|
WarningAction = 'SilentlyContinue'
|
||||||
|
}
|
||||||
|
$NoUserPixx = (Get-ADGroupMember @paramGetADGroupMember | Select-Object -ExpandProperty samaccountname)
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
$paramWriteError = @{
|
||||||
|
Message = ('Unable to find {0}' -f $AddGroup)
|
||||||
|
ErrorAction = 'Stop'
|
||||||
|
}
|
||||||
|
Write-Error @paramWriteError
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
function Test-ValidEmail
|
||||||
|
{
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Simple Function to check if a String is a valid Mail
|
||||||
|
|
||||||
|
.DESCRIPTION
|
||||||
|
Simple Function to check if a String is a valid Mail and return a Bool
|
||||||
|
|
||||||
|
.PARAMETER address
|
||||||
|
Address String to Check
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
# Not a valid String
|
||||||
|
PS C:\> Test-ValidEmail -address 'Joerg.Hochwald'
|
||||||
|
False
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
# Valid String
|
||||||
|
PS C:\> Test-ValidEmail -address 'Joerg.Hochwald@outlook.com'
|
||||||
|
True
|
||||||
|
|
||||||
|
.NOTES
|
||||||
|
Disclaimer: The code is provided 'as is,' with all possible faults, defects or errors, and without warranty of any kind.
|
||||||
|
|
||||||
|
Author: Joerg Hochwald
|
||||||
|
#>
|
||||||
|
|
||||||
|
[OutputType([bool])]
|
||||||
|
param
|
||||||
|
(
|
||||||
|
[Parameter(Mandatory,
|
||||||
|
HelpMessage = 'Address String to Check')]
|
||||||
|
[ValidateNotNullOrEmpty()]
|
||||||
|
[string]
|
||||||
|
$address
|
||||||
|
)
|
||||||
|
|
||||||
|
process
|
||||||
|
{
|
||||||
|
($address -as [mailaddress]).Address -eq $address -and $address -ne $null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
process
|
||||||
|
{
|
||||||
|
if (-not ($AddUserPixx.samaccountname))
|
||||||
|
{
|
||||||
|
$paramWriteVerbose = @{
|
||||||
|
Message = ('The AD Group {0} has no members.' -f $AddGroup)
|
||||||
|
}
|
||||||
|
Write-Verbose @paramWriteVerbose
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
# Add a counter
|
||||||
|
$AddUserPixxCount = (($AddUserPixx.samaccountname).count)
|
||||||
|
|
||||||
|
$paramWriteVerbose = @{
|
||||||
|
Message = ('The AD Group {0} has {1} members.' -f $AddGroup, $AddUserPixxCount)
|
||||||
|
}
|
||||||
|
Write-Verbose @paramWriteVerbose
|
||||||
|
|
||||||
|
foreach ($AddUser in $AddUserPixx.samaccountname)
|
||||||
|
{
|
||||||
|
if (($NoUserPixx.samaccountname) -notcontains $AddUser)
|
||||||
|
{
|
||||||
|
# Check the UPN and Fix it, if possible
|
||||||
|
if (-not (Test-ValidEmail -address ($AddUser)))
|
||||||
|
{
|
||||||
|
if (-not ($UPNDomain))
|
||||||
|
{
|
||||||
|
# Whoopsie
|
||||||
|
$paramWriteError = @{
|
||||||
|
Message = 'UPN Default Domain not set but needed!'
|
||||||
|
ErrorAction = 'Stop'
|
||||||
|
}
|
||||||
|
Write-Error @paramWriteError
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
# Let us fix this
|
||||||
|
$AddUserUPN = ($AddUser + '@' + $UPNDomain)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Build the Full Image Path
|
||||||
|
$SingleUserPicture = ($PictureDir + $AddUser + '.' + $Extension)
|
||||||
|
|
||||||
|
# Check if Picture exists
|
||||||
|
$paramTestPath = @{
|
||||||
|
Path = $SingleUserPicture
|
||||||
|
ErrorAction = 'Stop'
|
||||||
|
WarningAction = 'SilentlyContinue'
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Test-Path @paramTestPath)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$paramSetUserPhoto = @{
|
||||||
|
Identity = $AddUserUPN
|
||||||
|
PictureData = ([IO.File]::ReadAllBytes($SingleUserPicture))
|
||||||
|
Confirm = $false
|
||||||
|
ErrorAction = 'Stop'
|
||||||
|
WarningAction = 'SilentlyContinue'
|
||||||
|
}
|
||||||
|
|
||||||
|
$null = (Set-UserPhoto @paramSetUserPhoto)
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
$paramWriteWarning = @{
|
||||||
|
Message = ('Unable to set Image {0} for User {1}' -f $SingleUserPicture, $AddUser)
|
||||||
|
ErrorAction = 'SilentlyContinue'
|
||||||
|
}
|
||||||
|
Write-Warning @paramWriteWarning
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$paramWriteWarning = @{
|
||||||
|
Message = ('The Image {0} for User {1} was not found' -f $SingleUserPicture, $AddUser)
|
||||||
|
ErrorAction = 'SilentlyContinue'
|
||||||
|
}
|
||||||
|
Write-Warning @paramWriteWarning
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$paramWriteVerbose = @{
|
||||||
|
Message = ('Sorry, User {0} is member of {1} and {2}' -f $AddUser, $AddGroup, $RemGroup)
|
||||||
|
}
|
||||||
|
Write-Verbose @paramWriteVerbose
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-not ($NoUserPixx.samaccountname))
|
||||||
|
{
|
||||||
|
$paramWriteVerbose = @{
|
||||||
|
Message = ('The AD Group {0} has no members.' -f $RemGroup)
|
||||||
|
}
|
||||||
|
Write-Verbose @paramWriteVerbose
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
# Add a counter
|
||||||
|
$NoUserPixxCount = (($NoUserPixx.samaccountname).count)
|
||||||
|
|
||||||
|
$paramWriteVerbose = @{
|
||||||
|
Message = ('The AD Group {0} has {1} members.' -f $RemGroup, $NoUserPixxCount)
|
||||||
|
}
|
||||||
|
Write-Verbose @paramWriteVerbose
|
||||||
|
|
||||||
|
foreach ($NoUser in $NoUserPixx.samaccountname)
|
||||||
|
{
|
||||||
|
# Check the UPN and Fix it, if possible
|
||||||
|
if (-not (Test-ValidEmail -address ($NoUser)))
|
||||||
|
{
|
||||||
|
if (-not ($UPNDomain))
|
||||||
|
{
|
||||||
|
# Whoopsie
|
||||||
|
$paramWriteError = @{
|
||||||
|
Message = 'UPN Default Domain not set but needed!'
|
||||||
|
ErrorAction = 'Stop'
|
||||||
|
}
|
||||||
|
Write-Error @paramWriteError
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
# Let us fix this
|
||||||
|
$NoUserUPN = ($NoUser + '@' + $UPNDomain)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$paramSetUserPhoto = @{
|
||||||
|
Identity = $NoUserUPN
|
||||||
|
Confirm = $false
|
||||||
|
ErrorAction = 'Stop'
|
||||||
|
WarningAction = 'SilentlyContinue'
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$null = (Remove-UserPhoto @paramSetUserPhoto)
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
$paramWriteWarning = @{
|
||||||
|
Message = ('Unable to handle {0} - Check that this user has a valid Mailbox!' -f $NoUser)
|
||||||
|
ErrorAction = 'SilentlyContinue'
|
||||||
|
}
|
||||||
|
Write-Warning @paramWriteWarning
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
end
|
||||||
|
{
|
||||||
|
# Cleaniup
|
||||||
|
$AddUserPixx = $null
|
||||||
|
$NoUserPixx = $null
|
||||||
|
$AddUserPixxCount = $null
|
||||||
|
$NoUserPixxCount = $null
|
||||||
|
|
||||||
|
# Do a garbage collection: Call the .NET function to cleanup some stuff
|
||||||
|
$null = ([GC]::Collect())
|
||||||
|
}
|
||||||
|
|
||||||
|
#region LICENSE
|
||||||
|
<#
|
||||||
|
BSD 3-Clause License
|
||||||
|
|
||||||
|
Copyright (c) 2021, enabling Technology
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||||
|
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
#>
|
||||||
|
#endregion LICENSE
|
||||||
|
|
||||||
|
#region DISCLAIMER
|
||||||
|
<#
|
||||||
|
DISCLAIMER:
|
||||||
|
- Use at your own risk, etc.
|
||||||
|
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
|
||||||
|
- This is a third-party Software
|
||||||
|
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
|
||||||
|
- The Software is not supported by Microsoft Corp (MSFT)
|
||||||
|
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
|
||||||
|
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
|
||||||
|
#>
|
||||||
|
#endregion DISCLAIMER
|
||||||
@@ -0,0 +1,118 @@
|
|||||||
|
#requires -Version 3.0 -Modules ActiveDirectory
|
||||||
|
|
||||||
|
function Set-ADServerUsage
|
||||||
|
{
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Set all Active Directory related commands to use a special kind of server
|
||||||
|
|
||||||
|
.DESCRIPTION
|
||||||
|
By default the Active Directory related commands search for a DC. By default I want to make
|
||||||
|
use of the closest one. When I make BULK operations, I would like to use the Server with
|
||||||
|
the PDC role. This becomes handy often!
|
||||||
|
|
||||||
|
.PARAMETER pdc
|
||||||
|
Use the Active Directory Server who holds the PDC role.
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
# Use the closest Server
|
||||||
|
PS> Set-ADServerUsage
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
# Use the Server with the PDC role
|
||||||
|
PS> Set-ADServerUsage -pdc
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
# When it comes to scripts that do bulk operations, especially bulk loads and manipulation,
|
||||||
|
# I use the following within the Script:
|
||||||
|
if (Get-Command Set-ADServerUsage -ErrorAction SilentlyContinue)
|
||||||
|
{
|
||||||
|
Set-ADServerUsage -pdc
|
||||||
|
}
|
||||||
|
|
||||||
|
.NOTES
|
||||||
|
I use this helper function in my PROFILE. Therefore, some things a bit special.
|
||||||
|
Who want's an error message every time a window opens under normal circumstances?
|
||||||
|
#>
|
||||||
|
param
|
||||||
|
(
|
||||||
|
[Parameter(ValueFromPipeline,
|
||||||
|
ValueFromPipelineByPropertyName,
|
||||||
|
Position = 1)]
|
||||||
|
[switch]
|
||||||
|
$pdc
|
||||||
|
)
|
||||||
|
|
||||||
|
begin
|
||||||
|
{
|
||||||
|
# Defaults
|
||||||
|
$SC = 'SilentlyContinue'
|
||||||
|
|
||||||
|
# Cleanup
|
||||||
|
$dc = $null
|
||||||
|
}
|
||||||
|
|
||||||
|
process
|
||||||
|
{
|
||||||
|
<#
|
||||||
|
The following would do the trick:
|
||||||
|
#requires -Modules ActiveDirectory
|
||||||
|
But I don't want any error messages, so I decided to use this old-school way to figure
|
||||||
|
out if we are capable do what I want.
|
||||||
|
#>
|
||||||
|
if ((Get-Command -Name Get-ADDomain -ErrorAction $SC) -and (Get-Command -Name Get-ADDomainController -ErrorAction $SC) )
|
||||||
|
{
|
||||||
|
if ($pdc)
|
||||||
|
{
|
||||||
|
# Use the PDC instead
|
||||||
|
$dc = ((Get-ADDomain -ErrorAction $SC -WarningAction $SC).PDCEmulator)
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
# Use the closest DC
|
||||||
|
$dc = (Get-ADDomainController -Discover -NextClosestSite -ErrorAction $SC -WarningAction $SC)
|
||||||
|
}
|
||||||
|
|
||||||
|
# Skip everything if we do NOT have the proper information.
|
||||||
|
<#
|
||||||
|
Under normal circumstances this is pretty useless, but I use some virtual machines that have the RSAT tools installed, but they are not domain joined.
|
||||||
|
The fore I make this check. If all the systems that have the RSAT installed are domain joined, this test is obsolete.
|
||||||
|
#>
|
||||||
|
if ($dc)
|
||||||
|
{
|
||||||
|
# Make use of the Server from above
|
||||||
|
$PSDefaultParameterValues.add('*-AD*:Server', "$dc")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#region LICENSE
|
||||||
|
<#
|
||||||
|
BSD 3-Clause License
|
||||||
|
|
||||||
|
Copyright (c) 2021, enabling Technology
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||||
|
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
#>
|
||||||
|
#endregion LICENSE
|
||||||
|
|
||||||
|
#region DISCLAIMER
|
||||||
|
<#
|
||||||
|
DISCLAIMER:
|
||||||
|
- Use at your own risk, etc.
|
||||||
|
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
|
||||||
|
- This is a third-party Software
|
||||||
|
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
|
||||||
|
- The Software is not supported by Microsoft Corp (MSFT)
|
||||||
|
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
|
||||||
|
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
|
||||||
|
#>
|
||||||
|
#endregion DISCLAIMER
|
||||||
@@ -0,0 +1,255 @@
|
|||||||
|
function Invoke-AdvancedInstallerUpdate
|
||||||
|
{
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Sample function to rebuild a given Advanced Installer Project
|
||||||
|
|
||||||
|
.DESCRIPTION
|
||||||
|
Rebuild a given Advanced Installer Project.
|
||||||
|
Sample script to update the build Number from our build server and create a new MSI installer.
|
||||||
|
|
||||||
|
.PARAMETER Project
|
||||||
|
Advanced installer project name (the name of the Project file, without the AIP extension).
|
||||||
|
Example: DummyProduct for DummyProduct.aip
|
||||||
|
|
||||||
|
.PARAMETER Path
|
||||||
|
Specifies the path to Advanced Installer Project File.
|
||||||
|
|
||||||
|
.PARAMETER Version
|
||||||
|
Version of the new build.
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
PS C:\> Invoke-AdvancedInstallerUpdate -Project 'DummyProduct' -Path 'x:\dev\projects\DummyProduct\' -Version '1.0.3'
|
||||||
|
|
||||||
|
.NOTES
|
||||||
|
Sample Project
|
||||||
|
|
||||||
|
.LINK
|
||||||
|
https://www.advancedinstaller.com/user-guide/powershell-automation.html
|
||||||
|
#>
|
||||||
|
[CmdletBinding(ConfirmImpact = 'None')]
|
||||||
|
param
|
||||||
|
(
|
||||||
|
[Parameter(Mandatory,
|
||||||
|
ValueFromPipeline,
|
||||||
|
ValueFromPipelineByPropertyName,
|
||||||
|
Position = 0,
|
||||||
|
HelpMessage = 'Advanced installer project name (the name of the Project file, without the AIP extension).')]
|
||||||
|
[ValidateNotNullOrEmpty()]
|
||||||
|
[Alias('ProjectName', 'aipName')]
|
||||||
|
[string]
|
||||||
|
$Project,
|
||||||
|
[Parameter(Mandatory,
|
||||||
|
ValueFromPipeline,
|
||||||
|
ValueFromPipelineByPropertyName,
|
||||||
|
Position = 1,
|
||||||
|
HelpMessage = 'Specifies the path to Advanced Installer Project File.')]
|
||||||
|
[ValidateNotNullOrEmpty()]
|
||||||
|
[Alias('aipPath')]
|
||||||
|
[string]
|
||||||
|
$Path,
|
||||||
|
[Parameter(Mandatory,
|
||||||
|
ValueFromPipeline,
|
||||||
|
ValueFromPipelineByPropertyName,
|
||||||
|
Position = 2,
|
||||||
|
HelpMessage = 'Version of the new build.')]
|
||||||
|
[ValidateNotNullOrEmpty()]
|
||||||
|
[Alias('aipVersion')]
|
||||||
|
[string]
|
||||||
|
$Version
|
||||||
|
)
|
||||||
|
|
||||||
|
begin
|
||||||
|
{
|
||||||
|
# Create the full path of the Avanced Installer Project file
|
||||||
|
$AdvancedInstallerProjectName = $Path + $Project + '.aip'
|
||||||
|
|
||||||
|
# Check if the File exists
|
||||||
|
if (-not (Test-Path -Path $AdvancedInstallerProjectName -ErrorAction SilentlyContinue))
|
||||||
|
{
|
||||||
|
#region ErrorHandler
|
||||||
|
$paramWriteError = @{
|
||||||
|
Message = ('The given File {0} was not found' -f $AdvancedInstallerProjectName)
|
||||||
|
TargetObject = $AdvancedInstallerProjectName
|
||||||
|
Category = 'ObjectNotFound'
|
||||||
|
RecommendedAction = 'Check filename'
|
||||||
|
ErrorAction = 'Stop'
|
||||||
|
}
|
||||||
|
Write-Error @paramWriteError
|
||||||
|
|
||||||
|
# Only here to catch a global ErrorAction overwrite
|
||||||
|
break
|
||||||
|
#endregion ErrorHandler
|
||||||
|
}
|
||||||
|
|
||||||
|
# New Version number
|
||||||
|
$AdvancedInstallerProjectVersion = $Version
|
||||||
|
}
|
||||||
|
|
||||||
|
process
|
||||||
|
{
|
||||||
|
# Cleanup
|
||||||
|
$AdvancedInstallerProject = $null
|
||||||
|
|
||||||
|
# Creates a new PS object for Advanced Installer interaction
|
||||||
|
$AdvancedInstaller = (New-Object -ComObject AdvancedInstaller)
|
||||||
|
|
||||||
|
# Load the Advanced Installer object
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$AdvancedInstallerProject = $AdvancedInstaller.LoadProject($AdvancedInstallerProjectName)
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
#region ErrorHandler
|
||||||
|
# get error record
|
||||||
|
[Management.Automation.ErrorRecord]$e = $_
|
||||||
|
|
||||||
|
# retrieve information about runtime error
|
||||||
|
$info = [PSCustomObject]@{
|
||||||
|
Exception = $e.Exception.Message
|
||||||
|
Reason = $e.CategoryInfo.Reason
|
||||||
|
Target = $e.CategoryInfo.TargetName
|
||||||
|
Script = $e.InvocationInfo.ScriptName
|
||||||
|
Line = $e.InvocationInfo.ScriptLineNumber
|
||||||
|
Column = $e.InvocationInfo.OffsetInLine
|
||||||
|
}
|
||||||
|
|
||||||
|
$info | Out-String | Write-Verbose
|
||||||
|
|
||||||
|
Write-Error -Message ($info.Exception) -ErrorAction Stop
|
||||||
|
|
||||||
|
# Only here to catch a global ErrorAction overwrite
|
||||||
|
break
|
||||||
|
#endregion ErrorHandler
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($AdvancedInstallerProject)
|
||||||
|
{
|
||||||
|
# Modidy the version number
|
||||||
|
$AdvancedInstallerProject.ProductDetails.Version = $AdvancedInstallerProjectVersion
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
# Build the project
|
||||||
|
$AdvancedInstallerProjectBuild = ($AdvancedInstallerProject.Build())
|
||||||
|
|
||||||
|
Write-Verbose -Message $AdvancedInstallerProjectBuild
|
||||||
|
|
||||||
|
# Save the modified file
|
||||||
|
try
|
||||||
|
{
|
||||||
|
# Note: Remove the $null if you would like to see the output
|
||||||
|
$null = ($AdvancedInstallerProject.SaveAs($AdvancedInstallerProjectName))
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
#region ErrorHandler
|
||||||
|
# get error record
|
||||||
|
[Management.Automation.ErrorRecord]$e = $_
|
||||||
|
|
||||||
|
# retrieve information about runtime error
|
||||||
|
$info = [PSCustomObject]@{
|
||||||
|
Exception = $e.Exception.Message
|
||||||
|
Reason = $e.CategoryInfo.Reason
|
||||||
|
Target = $e.CategoryInfo.TargetName
|
||||||
|
Script = $e.InvocationInfo.ScriptName
|
||||||
|
Line = $e.InvocationInfo.ScriptLineNumber
|
||||||
|
Column = $e.InvocationInfo.OffsetInLine
|
||||||
|
}
|
||||||
|
|
||||||
|
$info | Out-String | Write-Verbose
|
||||||
|
|
||||||
|
Write-Error -Message ($info.Exception) -ErrorAction Stop
|
||||||
|
|
||||||
|
# Only here to catch a global ErrorAction overwrite
|
||||||
|
break
|
||||||
|
#endregion ErrorHandler
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
Write-Error -Message 'Build failed'
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
# Cleanup
|
||||||
|
$AdvancedInstallerProject = $null
|
||||||
|
$AdvancedInstaller = $null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
#region ErrorHandler
|
||||||
|
$paramWriteError = @{
|
||||||
|
Message = ('Unable to load {0}' -f $AdvancedInstallerProjectName)
|
||||||
|
TargetObject = $AdvancedInstallerProjectName
|
||||||
|
Category = 'InvalidData'
|
||||||
|
RecommendedAction = 'Check file'
|
||||||
|
ErrorAction = 'Stop'
|
||||||
|
}
|
||||||
|
Write-Error @paramWriteError
|
||||||
|
|
||||||
|
# Only here to catch a global ErrorAction overwrite
|
||||||
|
break
|
||||||
|
#endregion ErrorHandler
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
end
|
||||||
|
{
|
||||||
|
# Create a filter for the MSI
|
||||||
|
$AdvancedInstallerProjectMSI = $Project + '.msi'
|
||||||
|
|
||||||
|
# Cleanup
|
||||||
|
$AdvancedInstallerProjectMSIPath = $null
|
||||||
|
|
||||||
|
# Loop over the returned object and try to find the MSI
|
||||||
|
$AdvancedInstallerProjectMSIPath = ($AdvancedInstallerProjectBuild.Split("`n") | ForEach-Object {
|
||||||
|
if ($_ -match $AdvancedInstallerProjectMSI)
|
||||||
|
{
|
||||||
|
$_
|
||||||
|
}
|
||||||
|
})
|
||||||
|
# TODO: The method is a bit crappy
|
||||||
|
|
||||||
|
if ($AdvancedInstallerProjectMSIPath)
|
||||||
|
{
|
||||||
|
Write-Host -Object $AdvancedInstallerProjectMSIPath
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Write-Warning -Message 'New MSI was not found' -WarningAction Continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#region LICENSE
|
||||||
|
<#
|
||||||
|
BSD 3-Clause License
|
||||||
|
|
||||||
|
Copyright (c) 2021, enabling Technology
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||||
|
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
#>
|
||||||
|
#endregion LICENSE
|
||||||
|
|
||||||
|
#region DISCLAIMER
|
||||||
|
<#
|
||||||
|
DISCLAIMER:
|
||||||
|
- Use at your own risk, etc.
|
||||||
|
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
|
||||||
|
- This is a third-party Software
|
||||||
|
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
|
||||||
|
- The Software is not supported by Microsoft Corp (MSFT)
|
||||||
|
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
|
||||||
|
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
|
||||||
|
#>
|
||||||
|
#endregion DISCLAIMER
|
||||||
29
Powershell/PowerShell-collection/AdvancedInstaller/LICENSE
Normal file
29
Powershell/PowerShell-collection/AdvancedInstaller/LICENSE
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
BSD 3-Clause License
|
||||||
|
|
||||||
|
Copyright (c) 2021, enabling Technology <http://enatec.io>
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
* Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
* Neither the name of the copyright holder nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
@@ -0,0 +1,199 @@
|
|||||||
|
#requires -Version 3.0 -Modules AzureAD
|
||||||
|
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Script to monitor and return large number of user devices in Azure Active Directory.
|
||||||
|
|
||||||
|
.DESCRIPTION
|
||||||
|
Script to monitor and return large number of user devices in Active Directory.
|
||||||
|
The default limit in Azure is 20 devices
|
||||||
|
|
||||||
|
.PARAMETER All
|
||||||
|
If true, return all users.
|
||||||
|
|
||||||
|
.PARAMETER HighDeviceCount
|
||||||
|
Enter the threshold for devices that you want to return
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
Get-AzureADUserDevices.ps1 -HighDeviceCount 15 -All $true
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
Get-AzureADUserDevices.ps1 -HighDeviceCount 5 -All $true
|
||||||
|
|
||||||
|
.NOTES
|
||||||
|
Reworked version of Ben Whitmore Get-UserDevices that use the AzureAD module instead of the MsolService module
|
||||||
|
|
||||||
|
.LINK
|
||||||
|
https://github.com/byteben/AzureAD/blob/master/Get-UserDevices.ps1
|
||||||
|
#>
|
||||||
|
[CmdletBinding(ConfirmImpact = 'None')]
|
||||||
|
param
|
||||||
|
(
|
||||||
|
[Parameter(ValueFromPipeline,
|
||||||
|
ValueFromPipelineByPropertyName,
|
||||||
|
Position = 0)]
|
||||||
|
[ValidateNotNullOrEmpty()]
|
||||||
|
[int]
|
||||||
|
$HighDeviceCount = 15,
|
||||||
|
[Parameter(ValueFromPipeline,
|
||||||
|
ValueFromPipelineByPropertyName,
|
||||||
|
Position = 1)]
|
||||||
|
[bool]
|
||||||
|
$All
|
||||||
|
)
|
||||||
|
|
||||||
|
begin
|
||||||
|
{
|
||||||
|
# Defaults
|
||||||
|
$STP = 'Stop'
|
||||||
|
|
||||||
|
# Set some default
|
||||||
|
if (-not ($HighDeviceCount))
|
||||||
|
{
|
||||||
|
$HighDeviceCount = 15
|
||||||
|
}
|
||||||
|
|
||||||
|
# Connect to Azure Active Directory, if needed
|
||||||
|
if ($AzureActiveDirectoryConnection.Account -eq $null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$Global:AzureActiveDirectoryConnection = (Connect-AzureAD -ErrorAction $STP)
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
#region ErrorHandler
|
||||||
|
# get error record
|
||||||
|
[Management.Automation.ErrorRecord]$e = $_
|
||||||
|
|
||||||
|
# retrieve information about runtime error
|
||||||
|
$info = [PSCustomObject]@{
|
||||||
|
Exception = $e.Exception.Message
|
||||||
|
Reason = $e.CategoryInfo.Reason
|
||||||
|
Target = $e.CategoryInfo.TargetName
|
||||||
|
Script = $e.InvocationInfo.ScriptName
|
||||||
|
Line = $e.InvocationInfo.ScriptLineNumber
|
||||||
|
Column = $e.InvocationInfo.OffsetInLine
|
||||||
|
}
|
||||||
|
|
||||||
|
$info | Out-String | Write-Verbose
|
||||||
|
|
||||||
|
Write-Error -Message ($info.Exception) -ErrorAction $STP
|
||||||
|
|
||||||
|
# Only here to catch a global ErrorAction overwrite
|
||||||
|
break
|
||||||
|
#endregion ErrorHandler
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Initialize Array to hold users and number of devices
|
||||||
|
$DeviceCountHigh = @()
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
# Splatting
|
||||||
|
$paramGetAzureADUser = @{
|
||||||
|
filter = "userType eq 'Member'"
|
||||||
|
All = $All
|
||||||
|
ErrorAction = $STP
|
||||||
|
}
|
||||||
|
|
||||||
|
# Get list of users from Azure Active Directory
|
||||||
|
$Users = (Get-AzureADUser @paramGetAzureADUser | Select-Object -Property UserPrincipalName, ObjectId)
|
||||||
|
|
||||||
|
# Splatting
|
||||||
|
$paramGetAzureADDevice = @{
|
||||||
|
All = $true
|
||||||
|
ErrorAction = $STP
|
||||||
|
}
|
||||||
|
|
||||||
|
# Get a list of Devices and the ownership information from the Azure Active Directory
|
||||||
|
$Devices = (Get-AzureADDevice @paramGetAzureADDevice | Get-AzureADDeviceRegisteredOwner -ErrorAction $STP)
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
#region ErrorHandler
|
||||||
|
# get error record
|
||||||
|
[Management.Automation.ErrorRecord]$e = $_
|
||||||
|
|
||||||
|
# retrieve information about runtime error
|
||||||
|
$info = @{
|
||||||
|
Exception = $e.Exception.Message
|
||||||
|
Reason = $e.CategoryInfo.Reason
|
||||||
|
Target = $e.CategoryInfo.TargetName
|
||||||
|
Script = $e.InvocationInfo.ScriptName
|
||||||
|
Line = $e.InvocationInfo.ScriptLineNumber
|
||||||
|
Column = $e.InvocationInfo.OffsetInLine
|
||||||
|
}
|
||||||
|
|
||||||
|
$info | Out-String | Write-Verbose
|
||||||
|
|
||||||
|
Write-Error -Message ($info.Exception) -ErrorAction $STP
|
||||||
|
|
||||||
|
# Only here to catch a global ErrorAction overwrite
|
||||||
|
break
|
||||||
|
#endregion ErrorHandler
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
process
|
||||||
|
{
|
||||||
|
foreach ($User in $Users)
|
||||||
|
{
|
||||||
|
# For each user returned, count their Registered Devices
|
||||||
|
$Device = ($Devices | Where-Object {
|
||||||
|
$_.UserPrincipalName -eq $User.UserPrincipalName
|
||||||
|
} | Measure-Object)
|
||||||
|
|
||||||
|
# If the number of registered devices measured is high, create a new PSObject
|
||||||
|
if ($Device.Count -ge $HighDeviceCount)
|
||||||
|
{
|
||||||
|
# Create a new PSObject
|
||||||
|
$DeviceCountMember = @()
|
||||||
|
|
||||||
|
# Fill the values
|
||||||
|
$DeviceCountMember = (New-Object -TypeName PSObject)
|
||||||
|
$DeviceCountMember | Add-Member -MemberType NoteProperty -Name 'UserPrincipalName' -Value $User.UserPrincipalName
|
||||||
|
$DeviceCountMember | Add-Member -MemberType NoteProperty -Name 'DeviceCount' -Value $Device.Count
|
||||||
|
|
||||||
|
# Add to the PSObject
|
||||||
|
$DeviceCountHigh += $DeviceCountMember
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
end
|
||||||
|
{
|
||||||
|
# Display Users with high number of devices
|
||||||
|
$DeviceCountHigh | Sort-Object -Property DeviceCount -Descending
|
||||||
|
}
|
||||||
|
|
||||||
|
#region LICENSE
|
||||||
|
<#
|
||||||
|
BSD 3-Clause License
|
||||||
|
|
||||||
|
Copyright (c) 2021, enabling Technology
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||||
|
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
#>
|
||||||
|
#endregion LICENSE
|
||||||
|
|
||||||
|
#region DISCLAIMER
|
||||||
|
<#
|
||||||
|
DISCLAIMER:
|
||||||
|
- Use at your own risk, etc.
|
||||||
|
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
|
||||||
|
- This is a third-party Software
|
||||||
|
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
|
||||||
|
- The Software is not supported by Microsoft Corp (MSFT)
|
||||||
|
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
|
||||||
|
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
|
||||||
|
#>
|
||||||
|
#endregion DISCLAIMER
|
||||||
29
Powershell/PowerShell-collection/AzureAD/LICENSE
Normal file
29
Powershell/PowerShell-collection/AzureAD/LICENSE
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
BSD 3-Clause License
|
||||||
|
|
||||||
|
Copyright (c) 2021, enabling Technology <http://enatec.io>
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
* Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
* Neither the name of the copyright holder nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
#requires -Version 1.0
|
||||||
|
|
||||||
|
function Get-DsRegStatusInfo
|
||||||
|
{
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Wrapper function for the dsregcmd command
|
||||||
|
|
||||||
|
.DESCRIPTION
|
||||||
|
Wrapper function for the dsregcmd command
|
||||||
|
Nothing fancy, but it should convert the plain text output of dsregcmd to a PSObject
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
PS C:\> Get-DsRegStatusInfo
|
||||||
|
|
||||||
|
Returns a PSObject with the values of dsregcmd
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
PS C:\> $AADInfo = (Get-DsRegStatusInfo | Select-Object -Property AzureAdJoined,WorkplaceJoined)
|
||||||
|
PS C:\> if ( ($AADInfo.AzureAdJoined -ne 'YES') -and ($AADInfo.WorkplaceJoined -ne 'YES') ) {throw 'Not AzureAD bound'}
|
||||||
|
|
||||||
|
Check if the system is joined to the AzureAD (fully or just WorkplaceJoined)
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
PS C:\> $AADInfo = (Get-DsRegStatusInfo | Select-Object -Property AzureAdJoined, WorkplaceJoined)
|
||||||
|
PS C:\> if ($AADInfo.AzureAdJoined -eq 'YES') {'AzureAd Joined'} elseif ($AADInfo.WorkplaceJoined -eq 'YES') {'Workplace Joined'} else {'Unknown'}
|
||||||
|
|
||||||
|
Check if the system is joined to the AzureAD (fully or just WorkplaceJoined)
|
||||||
|
|
||||||
|
.NOTES
|
||||||
|
Replaced my old ConvertFrom-String based wrapper implementation, this is more flexible
|
||||||
|
|
||||||
|
.LINK
|
||||||
|
http://hochwald.net
|
||||||
|
#>
|
||||||
|
[CmdletBinding(ConfirmImpact = 'None')]
|
||||||
|
[OutputType([psobject])]
|
||||||
|
param ()
|
||||||
|
|
||||||
|
begin
|
||||||
|
{
|
||||||
|
$DsRegCmdPlain = (& "$env:windir\system32\dsregcmd.exe" /status)
|
||||||
|
$DsRegStatusInfo = (New-Object -TypeName PSObject)
|
||||||
|
}
|
||||||
|
|
||||||
|
process
|
||||||
|
{
|
||||||
|
$DsRegCmdPlain | Select-String -Pattern ' *[A-z]+ : [A-z]+ *' | ForEach-Object -Process {
|
||||||
|
$null = (Add-Member -InputObject $DsRegStatusInfo -MemberType NoteProperty -Name (([String]$_).Trim() -split ' : ')[0] -Value (([String]$_).Trim() -split ' : ')[1] -ErrorAction SilentlyContinue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
end
|
||||||
|
{
|
||||||
|
$DsRegStatusInfo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#region LICENSE
|
||||||
|
<#
|
||||||
|
BSD 3-Clause License
|
||||||
|
|
||||||
|
Copyright (c) 2021, enabling Technology
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||||
|
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
#>
|
||||||
|
#endregion LICENSE
|
||||||
|
|
||||||
|
#region DISCLAIMER
|
||||||
|
<#
|
||||||
|
DISCLAIMER:
|
||||||
|
- Use at your own risk, etc.
|
||||||
|
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
|
||||||
|
- This is a third-party Software
|
||||||
|
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
|
||||||
|
- The Software is not supported by Microsoft Corp (MSFT)
|
||||||
|
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
|
||||||
|
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
|
||||||
|
#>
|
||||||
|
#endregion DISCLAIMER
|
||||||
@@ -0,0 +1,152 @@
|
|||||||
|
#requires -Version 2.0 -Modules BitLocker
|
||||||
|
#requires -RunAsAdministrator
|
||||||
|
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Backup the BitLocker Recovery Information to the Azure Active Directory
|
||||||
|
|
||||||
|
.DESCRIPTION
|
||||||
|
Backup the BitLocker Recovery Information to the Azure Active Directory
|
||||||
|
If the Boot Drive is not encrypted, the Script will try to enable the quick protection
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
PS C:\> .\Invoke-BackupBitlockerRecoveryKey.ps1
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
PS C:\> $AADInfo = (Get-DsRegStatusInfo | Select-Object -Property AzureAdJoined,WorkplaceJoined)
|
||||||
|
PS C:\> if ( ($AADInfo.AzureAdJoined -ne 'YES') -and ($AADInfo.WorkplaceJoined -ne 'YES') ) {throw 'Not AzureAD bound'} else {.\Invoke-BackupBitlockerRecoveryKey.ps1}
|
||||||
|
|
||||||
|
You may want to check if the device is AzureAD joined with Get-DsRegStatusInfo first
|
||||||
|
|
||||||
|
.NOTES
|
||||||
|
Quick and relative dirty solution for a challenge I had in the last couple of days
|
||||||
|
|
||||||
|
.LINK
|
||||||
|
Get-DsRegStatusInfo
|
||||||
|
|
||||||
|
.LINK
|
||||||
|
http://hochwald.net
|
||||||
|
#>
|
||||||
|
[CmdletBinding(ConfirmImpact = 'Low')]
|
||||||
|
param ()
|
||||||
|
|
||||||
|
begin
|
||||||
|
{
|
||||||
|
# Defaults
|
||||||
|
$LogName = 'Application'
|
||||||
|
$STP = 'Stop'
|
||||||
|
$SCT = 'SilentlyContinue'
|
||||||
|
$LogSource = 'enAutomate'
|
||||||
|
|
||||||
|
# Register the event log source
|
||||||
|
$null = (New-EventLog -LogName $LogName -Source $LogSource -ErrorAction $SCT)
|
||||||
|
}
|
||||||
|
|
||||||
|
process
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
# Get BitLocker Volume info
|
||||||
|
$BitLockerVolumeInfo = (Get-BitLockerVolume -ErrorAction $STP | Where-Object -FilterScript {
|
||||||
|
$_.VolumeType -eq 'OperatingSystem'
|
||||||
|
})
|
||||||
|
|
||||||
|
# Get the Mount Point
|
||||||
|
$BootDrive = $BitLockerVolumeInfo.MountPoint
|
||||||
|
|
||||||
|
# Check if the drive is encrypted
|
||||||
|
if ($BitLockerVolumeInfo.ProtectionStatus -ne 'On')
|
||||||
|
{
|
||||||
|
$InfoMessage = ('Enable BitLocker for ' + $BootDrive)
|
||||||
|
Write-Verbose -Message $InfoMessage
|
||||||
|
$null = (Write-EventLog -LogName $LogName -Source $LogSource -EntryType Information -EventId 1000 -Message $InfoMessage -ErrorAction $SCT)
|
||||||
|
|
||||||
|
# Now we try to activate BitLocker (-UsedSpaceOnly is not perfect, but much faster in this case
|
||||||
|
$null = (Enable-BitLocker -MountPoint $BootDrive -EncryptionMethod XtsAes128 -UsedSpaceOnly -SkipHardwareTest -RecoveryPasswordProtector -Confirm:$false -ErrorAction $STP)
|
||||||
|
}
|
||||||
|
|
||||||
|
# Get the correct ID (The one from the RecoveryPassword)
|
||||||
|
$BitLockerKeyProtectorId = ($BitLockerVolumeInfo.KeyProtector | Where-Object -FilterScript {
|
||||||
|
$_.KeyProtectorType -eq 'RecoveryPassword'
|
||||||
|
} | Select-Object -ExpandProperty KeyProtectorId)
|
||||||
|
|
||||||
|
# Check if we have a recovery password/id
|
||||||
|
if ($BitLockerKeyProtectorId)
|
||||||
|
{
|
||||||
|
# Do the backup towards AzureAD
|
||||||
|
$null = (BackupToAAD-BitLockerKeyProtector -MountPoint $BootDrive -KeyProtectorId $BitLockerKeyProtectorId -Confirm:$false -ErrorAction $STP)
|
||||||
|
|
||||||
|
$InfoMessage = ('The Recovery Infor for ' + $BootDrive + ' was saved to the Azure Active Directory')
|
||||||
|
Write-Verbose -Message $InfoMessage
|
||||||
|
$null = (Write-EventLog -LogName $LogName -Source $LogSource -EntryType Information -EventId 1000 -Message $InfoMessage -ErrorAction $SCT)
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$WarningMessage = ('No Recorvery Information for ' + $BootDrive + ' found...')
|
||||||
|
Write-Warning -Message $WarningMessage
|
||||||
|
$null = (Write-EventLog -LogName $LogName -Source $LogSource -EntryType Warning -EventId 1001 -Message $WarningMessage -ErrorAction $SCT)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
#region ErrorHandler
|
||||||
|
# Get error record
|
||||||
|
[Management.Automation.ErrorRecord]$e = $_
|
||||||
|
|
||||||
|
# retrieve information about runtime error
|
||||||
|
$info = @{
|
||||||
|
Exception = $e.Exception.Message
|
||||||
|
Reason = $e.CategoryInfo.Reason
|
||||||
|
Target = $e.CategoryInfo.TargetName
|
||||||
|
Script = $e.InvocationInfo.ScriptName
|
||||||
|
Line = $e.InvocationInfo.ScriptLineNumber
|
||||||
|
Column = $e.InvocationInfo.OffsetInLine
|
||||||
|
}
|
||||||
|
|
||||||
|
# Error Stack
|
||||||
|
$info | Out-String | Write-Verbose
|
||||||
|
|
||||||
|
# Save to the Event Log
|
||||||
|
$null = (Write-EventLog -LogName $LogName -Source $LogSource -EntryType Error -EventId 1001 -Message ($info.Exception) -ErrorAction $SCT)
|
||||||
|
|
||||||
|
# Just display the info on continue with the rest of the list
|
||||||
|
$paramWriteError = @{
|
||||||
|
Message = ($info.Exception)
|
||||||
|
Exception = $info.Exception
|
||||||
|
TargetObject = $info.Target
|
||||||
|
ErrorAction = 'Stop'
|
||||||
|
WarningAction = 'Continue'
|
||||||
|
}
|
||||||
|
Write-Error @paramWriteError
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#region LICENSE
|
||||||
|
<#
|
||||||
|
BSD 3-Clause License
|
||||||
|
|
||||||
|
Copyright (c) 2021, enabling Technology
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||||
|
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
#>
|
||||||
|
#endregion LICENSE
|
||||||
|
|
||||||
|
#region DISCLAIMER
|
||||||
|
<#
|
||||||
|
DISCLAIMER:
|
||||||
|
- Use at your own risk, etc.
|
||||||
|
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
|
||||||
|
- This is a third-party Software
|
||||||
|
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
|
||||||
|
- The Software is not supported by Microsoft Corp (MSFT)
|
||||||
|
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
|
||||||
|
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
|
||||||
|
#>
|
||||||
|
#endregion DISCLAIMER
|
||||||
29
Powershell/PowerShell-collection/BitLocker/LICENSE
Normal file
29
Powershell/PowerShell-collection/BitLocker/LICENSE
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
BSD 3-Clause License
|
||||||
|
|
||||||
|
Copyright (c) 2021, enabling Technology <http://enatec.io>
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
* Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
* Neither the name of the copyright holder nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
@@ -0,0 +1,144 @@
|
|||||||
|
#requires -Version 2.0 -Modules BitLocker
|
||||||
|
#requires -RunAsAdministrator
|
||||||
|
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Create a new BitLocker Recovery Key
|
||||||
|
|
||||||
|
.DESCRIPTION
|
||||||
|
Create a new BitLocker Recovery Key
|
||||||
|
We will just create a new one, but we will not show it.
|
||||||
|
You should store it into the AzureAD, or a least in the Active Directory
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
PS C:\> .\New-BitlockerRecoveryKey.ps1
|
||||||
|
|
||||||
|
.NOTES
|
||||||
|
Quick and relative dirty solution for a challange I had in the last couple of days
|
||||||
|
By the way: Only the Boot Drive is supported by default.
|
||||||
|
|
||||||
|
.LINK
|
||||||
|
Add-BitLockerKeyProtector
|
||||||
|
|
||||||
|
.LINK
|
||||||
|
http://hochwald.net
|
||||||
|
#>
|
||||||
|
[CmdletBinding(ConfirmImpact = 'Low')]
|
||||||
|
param ()
|
||||||
|
|
||||||
|
begin
|
||||||
|
{
|
||||||
|
# Defaults
|
||||||
|
$LogName = 'Application'
|
||||||
|
$STP = 'Stop'
|
||||||
|
$SCT = 'SilentlyContinue'
|
||||||
|
$LogSource = 'enAutomate'
|
||||||
|
|
||||||
|
# Register the event log source
|
||||||
|
$null = (New-EventLog -LogName $LogName -Source $LogSource -ErrorAction $SCT)
|
||||||
|
}
|
||||||
|
|
||||||
|
process
|
||||||
|
{
|
||||||
|
# Get BitLocker Volume info
|
||||||
|
$BitLockerVolumeInfo = (Get-BitLockerVolume | Where-Object -FilterScript {
|
||||||
|
$_.VolumeType -eq 'OperatingSystem'
|
||||||
|
})
|
||||||
|
|
||||||
|
# Get the Mount Point
|
||||||
|
$BootDrive = $BitLockerVolumeInfo.MountPoint
|
||||||
|
|
||||||
|
# Get the Key
|
||||||
|
$KeyProtectors = $BitLockerVolumeInfo.KeyProtector
|
||||||
|
|
||||||
|
# Check if the Boot Drive is encrypted
|
||||||
|
if (($BitLockerVolumeInfo.VolumeStatus -eq 'FullyDecrypted') -or ($BitLockerVolumeInfo.ProtectionStatus -eq 'Off') -or (-not ($KeyProtectors)))
|
||||||
|
{
|
||||||
|
Write-Warning -Message ('Please Exceute: "Enable-BitLocker -MountPoint {0}"' -f $BootDrive)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
foreach ($KeyProtector in $KeyProtectors)
|
||||||
|
{
|
||||||
|
if ($KeyProtector.KeyProtectorType -eq 'RecoveryPassword')
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
# Remove the existing Recovery Password
|
||||||
|
$null = (Remove-BitLockerKeyProtector -MountPoint $BootDrive -KeyProtectorId $KeyProtector.KeyProtectorId -ErrorAction $STP)
|
||||||
|
|
||||||
|
# Just add a new Recovery Password without showing it here. We store than in the AzureAD anyway!
|
||||||
|
$null = (Add-BitLockerKeyProtector -MountPoint $BootDrive -RecoveryPasswordProtector -WarningAction SilentlyContinue)
|
||||||
|
|
||||||
|
# If we get this far, eveything has worked, write a success to the event log
|
||||||
|
$InfoText = 'Changed the BitLocker Recovery Password for ' + $BootDrive + ' successfully'
|
||||||
|
Write-EventLog -LogName $LogName -Source $LogSource -EntryType Information -EventId 1000 -Message $InfoText
|
||||||
|
Write-Output -InputObject $InfoText
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
#region ErrorHandler
|
||||||
|
# Get error record
|
||||||
|
[Management.Automation.ErrorRecord]$e = $_
|
||||||
|
|
||||||
|
# retrieve information about runtime error
|
||||||
|
$info = @{
|
||||||
|
Exception = $e.Exception.Message
|
||||||
|
Reason = $e.CategoryInfo.Reason
|
||||||
|
Target = $e.CategoryInfo.TargetName
|
||||||
|
Script = $e.InvocationInfo.ScriptName
|
||||||
|
Line = $e.InvocationInfo.ScriptLineNumber
|
||||||
|
Column = $e.InvocationInfo.OffsetInLine
|
||||||
|
}
|
||||||
|
|
||||||
|
# Error Stack
|
||||||
|
$info | Out-String | Write-Verbose
|
||||||
|
|
||||||
|
# Save to the Event Log
|
||||||
|
$null = (Write-EventLog -LogName $LogName -Source $LogSource -EntryType Error -EventId 1001 -Message ($info.Exception) -ErrorAction $SCT)
|
||||||
|
|
||||||
|
# Just display the info on continue with the rest of the list
|
||||||
|
$paramWriteError = @{
|
||||||
|
Message = ($info.Exception)
|
||||||
|
Exception = $info.Exception
|
||||||
|
TargetObject = $info.Target
|
||||||
|
ErrorAction = $STP
|
||||||
|
WarningAction = 'Continue'
|
||||||
|
}
|
||||||
|
Write-Error @paramWriteError
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#region LICENSE
|
||||||
|
<#
|
||||||
|
BSD 3-Clause License
|
||||||
|
|
||||||
|
Copyright (c) 2021, enabling Technology
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||||
|
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
#>
|
||||||
|
#endregion LICENSE
|
||||||
|
|
||||||
|
#region DISCLAIMER
|
||||||
|
<#
|
||||||
|
DISCLAIMER:
|
||||||
|
- Use at your own risk, etc.
|
||||||
|
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
|
||||||
|
- This is a third-party Software
|
||||||
|
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
|
||||||
|
- The Software is not supported by Microsoft Corp (MSFT)
|
||||||
|
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
|
||||||
|
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
|
||||||
|
#>
|
||||||
|
#endregion DISCLAIMER
|
||||||
@@ -0,0 +1,420 @@
|
|||||||
|
#requires -Version 2.0
|
||||||
|
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Exchange Server Logs Cleanup
|
||||||
|
|
||||||
|
.DESCRIPTION
|
||||||
|
Cleanup some Exchange Server logs.
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
PS C:\> .\CleanupExchangeLogs.ps1
|
||||||
|
|
||||||
|
.NOTES
|
||||||
|
Releasenotes:
|
||||||
|
1.0.1 2019-07-08: Move the delete process to the dedicated Invoke-CleanupOldFiles function
|
||||||
|
1.0.0 2019-02-04: Internal Release
|
||||||
|
|
||||||
|
THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER.
|
||||||
|
|
||||||
|
.LINK
|
||||||
|
Invoke-CleanupOldFiles
|
||||||
|
#>
|
||||||
|
[CmdletBinding(ConfirmImpact = 'Low')]
|
||||||
|
param ()
|
||||||
|
|
||||||
|
begin
|
||||||
|
{
|
||||||
|
# You can change the number of days here
|
||||||
|
$days = 30
|
||||||
|
|
||||||
|
#region PowerShell2WorkArounds
|
||||||
|
<#
|
||||||
|
The following stuff is a workaround to make everything compatible to PowerShell 2.0
|
||||||
|
Old, but some still have the old crap on the Exchange server running, sorry!
|
||||||
|
#>
|
||||||
|
#region RequiredModuleWorkAround
|
||||||
|
if (Get-Module -Name webadministration -ListAvailable -ErrorAction SilentlyContinue)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$null = (Import-Module -Name webadministration -Force -ErrorAction Stop)
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
#region ErrorHandler
|
||||||
|
# get error record
|
||||||
|
[Management.Automation.ErrorRecord]$e = $_
|
||||||
|
|
||||||
|
# retrieve information about runtime error
|
||||||
|
$info = @{
|
||||||
|
Exception = $e.Exception.Message
|
||||||
|
Reason = $e.CategoryInfo.Reason
|
||||||
|
Target = $e.CategoryInfo.TargetName
|
||||||
|
Script = $e.InvocationInfo.ScriptName
|
||||||
|
Line = $e.InvocationInfo.ScriptLineNumber
|
||||||
|
Column = $e.InvocationInfo.OffsetInLine
|
||||||
|
}
|
||||||
|
|
||||||
|
$info | Out-String | Write-Verbose
|
||||||
|
|
||||||
|
Write-Error -Message ($info.Exception) -ErrorAction Stop
|
||||||
|
|
||||||
|
# Only here to catch a global ErrorAction overwrite
|
||||||
|
break
|
||||||
|
#endregion ErrorHandler
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
#region ErrorHandler
|
||||||
|
$paramWriteError = @{
|
||||||
|
Message = 'The required Module (webadministration) is missing!'
|
||||||
|
Category = 'ObjectNotFound'
|
||||||
|
ErrorAction = 'Stop'
|
||||||
|
}
|
||||||
|
Write-Error @paramWriteError
|
||||||
|
|
||||||
|
# Only here to catch a global ErrorAction overwrite
|
||||||
|
break
|
||||||
|
#endregion ErrorHandler
|
||||||
|
}
|
||||||
|
#endregion RequiredModuleWorkAround
|
||||||
|
|
||||||
|
#region RunAsAdministrator
|
||||||
|
function Test-Administrator
|
||||||
|
{
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Check if this is an elevated shell
|
||||||
|
|
||||||
|
.DESCRIPTION
|
||||||
|
Check if this is an elevated shell.
|
||||||
|
|
||||||
|
In Powershell 4.0 it can be replaced with:
|
||||||
|
#Requires -RunAsAdministrator
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
PS C:\> Test-Administrator
|
||||||
|
|
||||||
|
.NOTES
|
||||||
|
In Powershell 4.0 it can be replaced with: Requires -RunAsAdministrator
|
||||||
|
|
||||||
|
License: Public Domain
|
||||||
|
#>
|
||||||
|
[CmdletBinding(ConfirmImpact = 'None')]
|
||||||
|
[OutputType([bool])]
|
||||||
|
param ()
|
||||||
|
|
||||||
|
process
|
||||||
|
{
|
||||||
|
$user = [Security.Principal.WindowsIdentity]::GetCurrent()
|
||||||
|
(New-Object -TypeName Security.Principal.WindowsPrincipal -ArgumentList $user).IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((Test-Administrator) -ne $true)
|
||||||
|
{
|
||||||
|
#region ErrorHandler
|
||||||
|
Write-Error -Message 'The current Windows PowerShell session is not running as Administrator. Start Windows PowerShell by using the Run as Administrator option, and then try running the script again.' -Category NotEnabled -ErrorAction Stop
|
||||||
|
|
||||||
|
# Only here to catch a global ErrorAction overwrite
|
||||||
|
break
|
||||||
|
#endregion ErrorHandler
|
||||||
|
}
|
||||||
|
#endregion RunAsAdministrator
|
||||||
|
#endregion PowerShell2WorkArounds
|
||||||
|
|
||||||
|
# Cleanup
|
||||||
|
$LogDirList = $null
|
||||||
|
|
||||||
|
# Create a new List
|
||||||
|
$LogDirList = (New-Object -TypeName System.Collections.Generic.List[System.Object])
|
||||||
|
|
||||||
|
# Add static Directories to the new list
|
||||||
|
if ($env:ExchangeInstallPath)
|
||||||
|
{
|
||||||
|
$LogDirList.Add($env:ExchangeInstallPath + 'Logging\')
|
||||||
|
|
||||||
|
# Another possible Directory
|
||||||
|
#$LogDirList.Add($env:ExchangeInstallPath + 'Bin\Search\Ceres\Diagnostics\Logs\')
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Write-Warning -Message 'This is not a Exchange Server!'
|
||||||
|
}
|
||||||
|
|
||||||
|
# Get a list of all IIS Websites and add to the new list
|
||||||
|
$AllIISSites = (Get-Website)
|
||||||
|
|
||||||
|
if ($AllIISSites)
|
||||||
|
{
|
||||||
|
# Loop over the IIS Site list
|
||||||
|
foreach ($SingleWebSite in $AllIISSites)
|
||||||
|
{
|
||||||
|
# Cleanup
|
||||||
|
$IISLogDirectory = $null
|
||||||
|
|
||||||
|
# Get the Log-Directory from the IIS Info
|
||||||
|
$IISLogDirectory = ($SingleWebSite.logfile.directory)
|
||||||
|
|
||||||
|
<#
|
||||||
|
Replace the returned %SystemDrive% with your system drive.
|
||||||
|
This is your BOOT Drive!!! Usually it is C:
|
||||||
|
#>
|
||||||
|
if ($IISLogDirectory -match '%SystemDrive%')
|
||||||
|
{
|
||||||
|
Write-Verbose -Message 'Mangle the SystemDrive within the variable...'
|
||||||
|
|
||||||
|
$IISLogDirectory = ($IISLogDirectory -replace '%SystemDrive%', 'C:')
|
||||||
|
}
|
||||||
|
|
||||||
|
# Add the log Directory to the List
|
||||||
|
$LogDirList.Add($IISLogDirectory)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Write-Warning -Message 'No IIS Log-Directory found!'
|
||||||
|
}
|
||||||
|
|
||||||
|
# Make all entries in the List unique
|
||||||
|
$LogDirList = ($LogDirList | Sort-Object | Get-Unique)
|
||||||
|
|
||||||
|
#region Invoke-CleanupOldFiles
|
||||||
|
function Invoke-CleanupOldFiles
|
||||||
|
{
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Remove files older then a given number of days
|
||||||
|
|
||||||
|
.DESCRIPTION
|
||||||
|
Remove files older then a given number of days.
|
||||||
|
Mostly used within cleanup Tasks.
|
||||||
|
|
||||||
|
.PARAMETER Path
|
||||||
|
Path to search.
|
||||||
|
|
||||||
|
.PARAMETER Age
|
||||||
|
Age of files to remove, in days.
|
||||||
|
Defaults to 30
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
PS C:\> Invoke-CleanupoldFiles -Path 'C:\inetpub\logs\LogFiles'
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
PS C:\> Invoke-CleanupoldFiles -Path 'C:\inetpub\logs\LogFiles' -Age 14
|
||||||
|
|
||||||
|
.NOTES
|
||||||
|
Releasenotes:
|
||||||
|
1.0.1 2019-07-08: Rework and splatting
|
||||||
|
1.0.0 2019-02-04: Internal Release
|
||||||
|
|
||||||
|
THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER.
|
||||||
|
|
||||||
|
.LINK
|
||||||
|
Get-ChildItem
|
||||||
|
|
||||||
|
.LINK
|
||||||
|
Test-Path
|
||||||
|
|
||||||
|
.LINK
|
||||||
|
Get-Date
|
||||||
|
#>
|
||||||
|
[CmdletBinding(ConfirmImpact = 'Low')]
|
||||||
|
param
|
||||||
|
(
|
||||||
|
[Parameter(Mandatory = $true,
|
||||||
|
ValueFromPipeline = $true,
|
||||||
|
ValueFromPipelineByPropertyName = $true,
|
||||||
|
Position = 0,
|
||||||
|
HelpMessage = 'Path to search.')]
|
||||||
|
[ValidateNotNullOrEmpty()]
|
||||||
|
[Alias('TargetFolder')]
|
||||||
|
[string]
|
||||||
|
$Path,
|
||||||
|
[Parameter(ValueFromPipeline = $true,
|
||||||
|
ValueFromPipelineByPropertyName = $true,
|
||||||
|
Position = 1)]
|
||||||
|
[ValidateNotNullOrEmpty()]
|
||||||
|
[Alias('Days')]
|
||||||
|
[int]
|
||||||
|
$Age = 30
|
||||||
|
)
|
||||||
|
|
||||||
|
process
|
||||||
|
{
|
||||||
|
if (Test-Path -Path $Path)
|
||||||
|
{
|
||||||
|
# Save the date to use it for the compare
|
||||||
|
$Now = (Get-Date)
|
||||||
|
|
||||||
|
# Today minus given days
|
||||||
|
$LastWrite = $Now.AddDays(-$days)
|
||||||
|
|
||||||
|
# Splatting the parameters
|
||||||
|
$paramGetChildItem = @{
|
||||||
|
Path = $Path
|
||||||
|
Include = '*.log', '*.blg'
|
||||||
|
Recurse = $true
|
||||||
|
}
|
||||||
|
|
||||||
|
# Find all Files to Delete (e.g. older then the given value)
|
||||||
|
$Files = (Get-ChildItem @paramGetChildItem | Where-Object -FilterScript {
|
||||||
|
(-not ($_.PSIsContainer)) -and ($_.LastWriteTime -le $LastWrite)
|
||||||
|
} | Select-Object -ExpandProperty fullname)
|
||||||
|
|
||||||
|
# Loop over the list of Files
|
||||||
|
foreach ($File in $Files)
|
||||||
|
{
|
||||||
|
# Support for WhatIf and Verbose
|
||||||
|
if ($pscmdlet.ShouldProcess($File, 'Remove'))
|
||||||
|
{
|
||||||
|
# Splatting the parameters
|
||||||
|
$paramRemoveItem = @{
|
||||||
|
Path = $File
|
||||||
|
ErrorAction = 'SilentlyContinue'
|
||||||
|
Force = $true
|
||||||
|
WhatIf = $false
|
||||||
|
}
|
||||||
|
# Remove the files that we found
|
||||||
|
$null = (Remove-Item @paramRemoveItem)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
#region ErrorHandler
|
||||||
|
# get error record
|
||||||
|
[Management.Automation.ErrorRecord]$e = $_
|
||||||
|
|
||||||
|
# retrieve information about runtime error
|
||||||
|
$info = @{
|
||||||
|
Exception = $e.Exception.Message
|
||||||
|
Reason = $e.CategoryInfo.Reason
|
||||||
|
Target = $e.CategoryInfo.TargetName
|
||||||
|
Script = $e.InvocationInfo.ScriptName
|
||||||
|
Line = $e.InvocationInfo.ScriptLineNumber
|
||||||
|
Column = $e.InvocationInfo.OffsetInLine
|
||||||
|
}
|
||||||
|
|
||||||
|
$info | Out-String | Write-Verbose
|
||||||
|
|
||||||
|
Write-Error -Message ($info.Exception) -ErrorAction Stop
|
||||||
|
|
||||||
|
# Only here to catch a global ErrorAction overwrite
|
||||||
|
break
|
||||||
|
#endregion ErrorHandler
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion Invoke-CleanupOldFiles
|
||||||
|
}
|
||||||
|
|
||||||
|
process
|
||||||
|
{
|
||||||
|
# Do we have a lif of directories?
|
||||||
|
if ($LogDirList)
|
||||||
|
{
|
||||||
|
# Loop over the List of Directories
|
||||||
|
foreach ($LogDir in $LogDirList)
|
||||||
|
{
|
||||||
|
Write-Verbose -Message "Removing logs from $LogDir older then $days days"
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
# Do we have a DAY value
|
||||||
|
if ($days)
|
||||||
|
{
|
||||||
|
# Splatting the parameters
|
||||||
|
$paramInvokeCleanupoldFiles = @{
|
||||||
|
Path = $LogDir
|
||||||
|
Age = $days
|
||||||
|
ErrorAction = 'Stop'
|
||||||
|
verbose = $true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
# Splatting the parameters
|
||||||
|
$paramInvokeCleanupoldFiles = @{
|
||||||
|
Path = $LogDir
|
||||||
|
ErrorAction = 'Stop'
|
||||||
|
verbose = $true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Invoke the internal Fun
|
||||||
|
Invoke-CleanupOldFiles @paramInvokeCleanupoldFiles
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
#region ErrorHandler
|
||||||
|
# get error record
|
||||||
|
[Management.Automation.ErrorRecord]$e = $_
|
||||||
|
|
||||||
|
# retrieve information about runtime error
|
||||||
|
$info = @{
|
||||||
|
Exception = $e.Exception.Message
|
||||||
|
Reason = $e.CategoryInfo.Reason
|
||||||
|
Target = $e.CategoryInfo.TargetName
|
||||||
|
Script = $e.InvocationInfo.ScriptName
|
||||||
|
Line = $e.InvocationInfo.ScriptLineNumber
|
||||||
|
Column = $e.InvocationInfo.OffsetInLine
|
||||||
|
}
|
||||||
|
|
||||||
|
$info | Out-String | Write-Verbose
|
||||||
|
|
||||||
|
Write-Error -Message ($info.Exception) -ErrorAction Stop
|
||||||
|
|
||||||
|
# Only here to catch a global ErrorAction overwrite
|
||||||
|
break
|
||||||
|
#endregion ErrorHandler
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
#region ErrorHandler
|
||||||
|
$paramWriteError = @{
|
||||||
|
Message = 'No directories found to cleanup'
|
||||||
|
Category = 'ObjectNotFound'
|
||||||
|
ErrorAction = 'Stop'
|
||||||
|
}
|
||||||
|
Write-Error @paramWriteError
|
||||||
|
|
||||||
|
# Only here to catch a global ErrorAction overwrite
|
||||||
|
break
|
||||||
|
#endregion ErrorHandler
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#region LICENSE
|
||||||
|
<#
|
||||||
|
BSD 3-Clause License
|
||||||
|
|
||||||
|
Copyright (c) 2021, enabling Technology
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||||
|
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
#>
|
||||||
|
#endregion LICENSE
|
||||||
|
|
||||||
|
#region DISCLAIMER
|
||||||
|
<#
|
||||||
|
DISCLAIMER:
|
||||||
|
- Use at your own risk, etc.
|
||||||
|
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
|
||||||
|
- This is a third-party Software
|
||||||
|
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
|
||||||
|
- The Software is not supported by Microsoft Corp (MSFT)
|
||||||
|
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
|
||||||
|
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
|
||||||
|
#>
|
||||||
|
#endregion DISCLAIMER
|
||||||
@@ -0,0 +1,219 @@
|
|||||||
|
#requires -Version 3.0 -RunAsAdministrator
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Cleanup some of the Exchange Logs
|
||||||
|
|
||||||
|
.DESCRIPTION
|
||||||
|
Cleanup some of the Exchange Logs
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
PS C:\> .\Clear-LogFileDirectory.ps1
|
||||||
|
|
||||||
|
.NOTES
|
||||||
|
Wrapper for the Clear-LogFileDirectory function
|
||||||
|
Everything is hardcoded for this wrapper ;-)
|
||||||
|
|
||||||
|
.LINK
|
||||||
|
Clear-LogFileDirectory
|
||||||
|
#>
|
||||||
|
[CmdletBinding(ConfirmImpact = 'None')]
|
||||||
|
param ()
|
||||||
|
|
||||||
|
# Files older then 1 day are deleted
|
||||||
|
$Days = 1
|
||||||
|
|
||||||
|
# Exchange Base Directory
|
||||||
|
$ExchangeBaseDir = 'D:\Exchange Server'
|
||||||
|
|
||||||
|
# Exchange Version (Directory)
|
||||||
|
$ExchangeVersion = 'V15'
|
||||||
|
|
||||||
|
# Where to find the IIS stuff
|
||||||
|
$IISBaseDir = "$env:HOMEDRIVE\inetpub"
|
||||||
|
|
||||||
|
|
||||||
|
#region IIS
|
||||||
|
# Append the Log Stuff for the Call below
|
||||||
|
$IISLogPath = $IISBaseDir + '\logs\LogFiles\'
|
||||||
|
#endregion IIS
|
||||||
|
|
||||||
|
#region Exchange
|
||||||
|
# Combine the values
|
||||||
|
$ExchangeDirectoryPath = $ExchangeBaseDir + '\' + $ExchangeVersion
|
||||||
|
|
||||||
|
# Append the Log Stuff for the Call below
|
||||||
|
$ExchangeLoggingPath = $ExchangeDirectoryPath + '\Logging\'
|
||||||
|
$ExchangeETLTraces = $ExchangeDirectoryPath + '\Bin\Search\Ceres\Diagnostics\ETLTraces\'
|
||||||
|
$ExchangeETLLogs = $ExchangeDirectoryPath + '\Bin\Search\Ceres\Diagnostics\Logs'
|
||||||
|
#endregion Exchange
|
||||||
|
|
||||||
|
#region HelperFunction
|
||||||
|
function Clear-LogFileDirectory
|
||||||
|
{
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Cleanup Files in a given Directory
|
||||||
|
|
||||||
|
.DESCRIPTION
|
||||||
|
Cleanup Files in a given Directory
|
||||||
|
|
||||||
|
.PARAMETER Path
|
||||||
|
Specifies a path, multi-value or wildcards are not yet supported!
|
||||||
|
No default so far!
|
||||||
|
|
||||||
|
.PARAMETER Days
|
||||||
|
Age of the Files to Delete.
|
||||||
|
Default is 7
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
PS C:\> Clear-LogFileDirectory -Path "c:\inetpub\logs\LogFiles\"
|
||||||
|
|
||||||
|
.NOTES
|
||||||
|
Mind the Gap:
|
||||||
|
Everything within the given directory will be deleted, without any further interaction!
|
||||||
|
#>
|
||||||
|
|
||||||
|
[CmdletBinding(ConfirmImpact = 'Low',
|
||||||
|
SupportsShouldProcess)]
|
||||||
|
param
|
||||||
|
(
|
||||||
|
[Parameter(Mandatory,
|
||||||
|
ValueFromPipeline,
|
||||||
|
ValueFromPipelineByPropertyName,
|
||||||
|
HelpMessage = 'Specifies a path, multivalue or wildcards are not yet supported!')]
|
||||||
|
[ValidateNotNullOrEmpty()]
|
||||||
|
[Alias('Folder', 'TargetFolder')]
|
||||||
|
[string]
|
||||||
|
$Path,
|
||||||
|
[Parameter(ValueFromPipeline,
|
||||||
|
ValueFromPipelineByPropertyName)]
|
||||||
|
[ValidateNotNullOrEmpty()]
|
||||||
|
[Alias('Age', 'FileAge')]
|
||||||
|
[int]
|
||||||
|
$Days = 7
|
||||||
|
)
|
||||||
|
|
||||||
|
begin
|
||||||
|
{
|
||||||
|
#region Defaults
|
||||||
|
$CNT = 'Continue'
|
||||||
|
$SCT = 'SilentlyContinue'
|
||||||
|
#endregion Defaults
|
||||||
|
|
||||||
|
Write-Verbose -Message ('START: Processing of {0}' -f $Path)
|
||||||
|
}
|
||||||
|
|
||||||
|
process
|
||||||
|
{
|
||||||
|
if (Test-Path -Path $Path -ErrorAction $SCT)
|
||||||
|
{
|
||||||
|
$Now = (Get-Date)
|
||||||
|
$LastWrite = $Now.AddDays(-$Days)
|
||||||
|
|
||||||
|
#region FindAndFilterFiles
|
||||||
|
# Splat the Parameters
|
||||||
|
$paramFindAndFilterFiles = @{
|
||||||
|
Path = $Path
|
||||||
|
Recurse = $true
|
||||||
|
ErrorAction = $SCT
|
||||||
|
}
|
||||||
|
$Files = (Get-ChildItem @paramFindAndFilterFiles | Where-Object -FilterScript {
|
||||||
|
($_.Name -like '*.log') -or ($_.Name -like '*.blg') -or ($_.Name -like '*.etl')
|
||||||
|
} | Where-Object -FilterScript {
|
||||||
|
$_.lastWriteTime -le $LastWrite
|
||||||
|
} | Select-Object -ExpandProperty FullName)
|
||||||
|
#endregion FindAndFilterFiles
|
||||||
|
|
||||||
|
#region FileLooper
|
||||||
|
foreach ($File in $Files)
|
||||||
|
{
|
||||||
|
Write-Verbose -Message ('Deleting file {0}' -f $File)
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if ($pscmdlet.ShouldProcess($File, 'Delete'))
|
||||||
|
{
|
||||||
|
#region DeleteFilesFound
|
||||||
|
# Splat the Parameters
|
||||||
|
$paramDeleteFilesFound = @{
|
||||||
|
Path = $File
|
||||||
|
Force = $true
|
||||||
|
Confirm = $false
|
||||||
|
ErrorAction = 'Stop'
|
||||||
|
}
|
||||||
|
$null = (Remove-Item @paramDeleteFilesFound)
|
||||||
|
#endregion DeleteFilesFound
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
#region ErrorHandler
|
||||||
|
# get error record
|
||||||
|
[Management.Automation.ErrorRecord]$e = $_
|
||||||
|
|
||||||
|
# retrieve information about runtime error
|
||||||
|
$info = [PSCustomObject]@{
|
||||||
|
Exception = $e.Exception.Message
|
||||||
|
Reason = $e.CategoryInfo.Reason
|
||||||
|
Target = $e.CategoryInfo.TargetName
|
||||||
|
Script = $e.InvocationInfo.ScriptName
|
||||||
|
Line = $e.InvocationInfo.ScriptLineNumber
|
||||||
|
Column = $e.InvocationInfo.OffsetInLine
|
||||||
|
}
|
||||||
|
|
||||||
|
$info | Out-String | Write-Verbose
|
||||||
|
|
||||||
|
Write-Warning -Message ($info.Exception) -ErrorAction $CNT -WarningAction $CNT
|
||||||
|
#endregion ErrorHandler
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion FileLooper
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Write-Error -Message ("The folder {0} doesn't exist! Check the folder path!" -f $Path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
end
|
||||||
|
{
|
||||||
|
Write-Verbose -Message ('DONE: Processed {0}' -f $Path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion HelperFunction
|
||||||
|
|
||||||
|
#region FunctionWrapper
|
||||||
|
Clear-LogFileDirectory -Path $IISLogPath -Days $Days
|
||||||
|
Clear-LogFileDirectory -Path $ExchangeLoggingPath -Days $Days
|
||||||
|
Clear-LogFileDirectory -Path $ExchangeETLTraces -Days $Days
|
||||||
|
Clear-LogFileDirectory -Path $ExchangeETLLogs -Days $Days
|
||||||
|
#endregion FunctionWrapper
|
||||||
|
|
||||||
|
#region LICENSE
|
||||||
|
<#
|
||||||
|
BSD 3-Clause License
|
||||||
|
|
||||||
|
Copyright (c) 2021, enabling Technology
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||||
|
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
#>
|
||||||
|
#endregion LICENSE
|
||||||
|
|
||||||
|
#region DISCLAIMER
|
||||||
|
<#
|
||||||
|
DISCLAIMER:
|
||||||
|
- Use at your own risk, etc.
|
||||||
|
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
|
||||||
|
- This is a third-party Software
|
||||||
|
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
|
||||||
|
- The Software is not supported by Microsoft Corp (MSFT)
|
||||||
|
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
|
||||||
|
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
|
||||||
|
#>
|
||||||
|
#endregion DISCLAIMER
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
#requires -Version 2.0
|
||||||
|
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Enabling Modern Authentication for Exchange Online
|
||||||
|
|
||||||
|
.DESCRIPTION
|
||||||
|
Enabling Modern Authentication for Exchange Online (Office 365)
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
PS C:\> .\Enable-ModernAuth-Exchange.ps1
|
||||||
|
|
||||||
|
.NOTES
|
||||||
|
Works fine with Office 2013 and Office 2016 on Windows. Tested with Office 2016 on the Mac.
|
||||||
|
You must enable it on your computers (Windows and Mac) as well! It is disabled by default.
|
||||||
|
|
||||||
|
.LINK
|
||||||
|
https://blogs.technet.microsoft.com/canitpro/2015/09/11/step-by-step-setting-up-ad-fs-and-enabling-single-sign-on-to-office-365/
|
||||||
|
#>
|
||||||
|
[CmdletBinding()]
|
||||||
|
param ()
|
||||||
|
|
||||||
|
begin
|
||||||
|
{
|
||||||
|
# The Exchange Online URL
|
||||||
|
$ExoURL = 'https://outlook.office365.com/powershell-liveid/'
|
||||||
|
|
||||||
|
# Same as above, but for the German Office 365 (MCD)
|
||||||
|
#$ExoURL = 'https://outlook.office.de/powershell-liveid/'
|
||||||
|
|
||||||
|
# The Exchange Online Authentication method
|
||||||
|
$ExoAuth = 'Basic'
|
||||||
|
}
|
||||||
|
|
||||||
|
process
|
||||||
|
{
|
||||||
|
# Get the Credentials (Could also be imported if you have dem saved)
|
||||||
|
$credentials = (Get-Credential)
|
||||||
|
|
||||||
|
# Create the new session
|
||||||
|
$paramNewPSSession = @{
|
||||||
|
ConfigurationName = 'Microsoft.Exchange'
|
||||||
|
ConnectionUri = $ExoURL
|
||||||
|
Credential = $credentials
|
||||||
|
Authentication = $ExoAuth
|
||||||
|
AllowRedirection = $true
|
||||||
|
}
|
||||||
|
$ExoSession = (New-PSSession @paramNewPSSession)
|
||||||
|
|
||||||
|
# Start the Session by importing it to the PowerShell Session
|
||||||
|
$null = (Import-PSSession -Session $ExoSession)
|
||||||
|
|
||||||
|
# Enable Modern Authentication, use $false to disable it
|
||||||
|
$null = (Set-OrganizationConfig -OAuth2ClientProfileEnabled $true)
|
||||||
|
}
|
||||||
|
|
||||||
|
end
|
||||||
|
{
|
||||||
|
# Cleanup
|
||||||
|
$ExoSession = $null
|
||||||
|
}
|
||||||
|
|
||||||
|
#region LICENSE
|
||||||
|
<#
|
||||||
|
BSD 3-Clause License
|
||||||
|
|
||||||
|
Copyright (c) 2021, enabling Technology
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||||
|
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
#>
|
||||||
|
#endregion LICENSE
|
||||||
|
|
||||||
|
#region DISCLAIMER
|
||||||
|
<#
|
||||||
|
DISCLAIMER:
|
||||||
|
- Use at your own risk, etc.
|
||||||
|
- This is open-source software, if you find an issue try to fix it yourself. There is no support and/or warranty in any kind
|
||||||
|
- This is a third-party Software
|
||||||
|
- The developer of this Software is NOT sponsored by or affiliated with Microsoft Corp (MSFT) or any of its subsidiaries in any way
|
||||||
|
- The Software is not supported by Microsoft Corp (MSFT)
|
||||||
|
- By using the Software, you agree to the License, Terms, and any Conditions declared and described above
|
||||||
|
- If you disagree with any of the terms, and any conditions declared: Just delete it and build your own solution
|
||||||
|
#>
|
||||||
|
#endregion DISCLAIMER
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user