Open Source Tools

Scripts &
Automation

Practical PowerShell scripts and tools for IT pros & sysadmins. Free, open-source, and battle-tested.

Browse Scripts About the Project
// repositories

Available Scripts

Open-source tools built for real-world IT administration. Expand any card to view the script directly.

PowerShell
Find-Automapping-M365
Instantly identify which accounts have auto-mapping enabled on a shared mailbox by reading the DelegateListLink attribute directly from the domain controller — no iteration required.
PowerShell
Find-Automapping-M365.ps1
(Get-MailboxPermission "SharedMailbox" -ReadFromDomainController)[0].DelegateListLink
#Above will give Output as only the accounts which are automapped
PowerShell
Connect-MgGraph-AppSecret
Connect to Microsoft Graph as an App using a Client Secret. Authenticates non-interactively using a credential object — ideal for automation and unattended scripts.
PowerShell
Connect-MgGraph-AppSecret.ps1
$clientID = ""
$Tenantid = ""
$clientsecret = ""
#Convert Secret to secure string
$clientsecretpass = ConvertTo-SecureString -String $clientsecret -AsPlainText -Force
#Create a credential object
$clientSecretCred = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $clientID, $clientsecretpass
#Connect to Microsoft Graph
connect-MgGraph -TenantId $Tenantid -ClientSecretCredential $clientSecretCred
PowerShell
Search-UnifiedAuditLog
Search and export Microsoft 365 Unified Audit Log records to CSV. Supports filtering by record type, operations, and free text across configurable time intervals.
PowerShell
Search-UnifiedAuditLog.ps1
#Modify the values for the following variables to configure the audit log search.
$timestamp = Get-Date -Format "yyyy-MM-dd_HH-mm-ss"
$logFile = "c:\Temp\AuditLogSearchLog_$timestamp.txt"
$outputFile = "c:\Temp\AuditLogRecords_$timestamp.csv"
[DateTime]$start = [DateTime]::UtcNow.AddDays(-1)
[DateTime]$end = [DateTime]::UtcNow
#$record = "ExchangeAdmin"
#$Operations = "Remove-DistributionGroup"
#$FreeText = "PSDL1"
$resultSize = 5000
$intervalMinutes = 60
#Start script
[DateTime]$currentStart = $start
[DateTime]$currentEnd = $end
Function Write-LogFile ([String]$Message)
{
    $final = [DateTime]::Now.ToUniversalTime().ToString("s") + ":" + $Message
    $final | Out-File $logFile -Append
}
Write-LogFile "BEGIN: Retrieving audit records between $($start) and $($end), RecordType=$record, PageSize=$resultSize."
Write-Host "Retrieving audit records for the date range between $($start) and $($end), RecordType=$record, ResultsSize=$resultSize"
$totalCount = 0
while ($true)
{
    $currentEnd = $currentStart.AddMinutes($intervalMinutes)
    if ($currentEnd -gt $end)
    {
        $currentEnd = $end
    }
if ($currentStart -eq $currentEnd)
    {
        break
    }
$sessionID = [Guid]::NewGuid().ToString() + "_" +  "ExtractLogs" + (Get-Date).ToString("yyyyMMddHHmmssfff")
    Write-LogFile "INFO: Retrieving audit records for activities performed between $($currentStart) and $($currentEnd)"
    Write-Host "Retrieving audit records for activities performed between $($currentStart) and $($currentEnd)"
    $currentCount = 0
$sw = [Diagnostics.StopWatch]::StartNew()
    do
    {
        $results = Search-UnifiedAuditLog -StartDate $currentStart -EndDate $currentEnd -RecordType $record -SessionId $sessionID -Operations $Operations -FreeText $FreeText -SessionCommand ReturnLargeSet -ResultSize $resultSize
if (($results | Measure-Object).Count -ne 0)
        {
            $results | export-csv -Path $outputFile -Append -NoTypeInformation
$currentTotal = $results[0].ResultCount
            $totalCount += $results.Count
            $currentCount += $results.Count
            Write-LogFile "INFO: Retrieved $($currentCount) audit records out of the total $($currentTotal)"
if ($currentTotal -eq $results[$results.Count - 1].ResultIndex)
            {
                $message = "INFO: Successfully retrieved $($currentTotal) audit records for the current time range. Moving on!"
                Write-LogFile $message
                Write-Host "Successfully retrieved $($currentTotal) audit records for the current time range. Moving on to the next interval." -foregroundColor Yellow
                ""
                break
            }
        }
    }
    while (($results | Measure-Object).Count -ne 0)
$currentStart = $currentEnd
}
Write-LogFile "END: Retrieving audit records between $($start) and $($end), RecordType=$record, PageSize=$resultSize, total count: $totalCount."
Write-Host "Script complete! Finished retrieving audit records for the date range between $($start) and $($end). Total count: $totalCount" -foregroundColor Green
Coming Soon
More Scripts on the Way
New scripts are actively being developed. Check back soon for new tools and automation scripts.
// about

Built for IT Pros

PowerShell — ehlo.co.in
PS> $profile
ehlo.co.in

PS> Get-Author
Focus : M365 & Exchange Online
Domain : ehlo.co.in

PS> Get-Mission
# Making IT automation accessible
Open-source scripts for every admin.

ehlo.co.in is a home for open-source scripts and automation tools focused on Microsoft 365, Exchange Online, and enterprise IT administration.

Every script here solves a real problem, is free to use, and is published with clear documentation. The goal: save fellow admins hours of troubleshooting.

1+
Repos
M365
Focus
Free
Always
// contact

Get in Touch

Found a bug, have a suggestion, or want to contribute? Reach out directly.

admin@ehlo.co.in Browse Scripts