Added Files

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

View File

@@ -0,0 +1,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

View File

@@ -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

View File

@@ -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

View File

@@ -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
#}

View File

@@ -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.')
}

View File

@@ -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.')

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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 + '.')
}
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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.

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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.

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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())

View File

@@ -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
}

View File

@@ -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.')
}
}
}

View File

@@ -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 @()
}

View File

@@ -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
}

View File

@@ -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 @()

View File

@@ -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

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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

View File

@@ -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.'
}

View File

@@ -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)

View File

@@ -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

View File

@@ -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 + '.')
}
}

View File

@@ -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.')
}
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

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

View File

@@ -0,0 +1,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.

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

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

View File

@@ -0,0 +1,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

View File

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

View File

@@ -0,0 +1,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

View File

@@ -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

View File

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

View File

@@ -0,0 +1,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

View File

@@ -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

View File

@@ -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

View File

@@ -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