Automation Scripts

PowerShell Scripts for IT Operations and Security

EnableBitlockerSystemDriveC.ps1

This script checks BitLocker status on the system drive, enables encryption using TPM if available, and backs up the recovery key to Active Directory.

#Check TPM version 
$TPMver = Get-CIMInstance -class Win32_Tpm -namespace root\CIMV2\Security\MicrosoftTpm | Select SpecVersion
if ($TPMver -match '2.0') {
  $DRIVE = Get-BitLockerVolume -MountPoint 'c:'
  if ($DRIVE.VolumeStatus -eq 'FullyDecrypted') {
    Add-BitLockerKeyProtector -MountPoint 'c:' -RecoveryPasswordProtector
    Enable-Bitlocker -MountPoint 'c:' -TpmProtector
  }
} else {
  $DRIVE = Get-BitLockerVolume -MountPoint 'c:'
  if ($DRIVE.VolumeStatus -eq 'FullyDecrypted') {
    Add-BitLockerKeyProtector -MountPoint 'c:' -RecoveryPasswordProtector
    Enable-Bitlocker -MountPoint 'c:' -RecoveryPasswordProtector
  }
}
      
Download Full Script

View the full project write-up →

EnableBitlockerDataDriveD.ps1

This script detects and encrypts data drives (D:, E:, F:) and enables auto-unlock if the system drive is already encrypted.

$DRIVEd = Get-BitLockerVolume -MountPoint 'd:'
$DRIVEe = Get-BitLockerVolume -MountPoint 'e:'
$DRIVEf = Get-BitLockerVolume -MountPoint 'f:'
$DRIVE = Get-BitLockerVolume -MountPoint 'c:'

if ($DRIVEd.volumeStatus -eq 'FullyDecrypted' -and $DRIVE.volumestatus -eq 'FullyEncrypted') {
  Add-BitLockerKeyProtector -MountPoint 'd:' -RecoveryPasswordProtector
  Enable-Bitlocker -MountPoint 'd:' -RecoveryPasswordProtector
  Enable-BitLockerAutoUnlock -MountPoint 'd:'
}
# Repeat for E: and F:
      
Download Full Script

View the full project write-up →

Remove-EntraStaleDevices.ps1

Automates cleanup of stale Microsoft Entra devices using certificate-based Graph authentication, with exclusion group protection, dry-run mode, and a persistent audit ledger.

# Certificate-based app-only auth (unattended, no stored secret)
Connect-MgGraph -TenantId $TenantId -ClientId $ClientId `
                -CertificateThumbprint $CertThumbprint -NoWelcome

$cutoffIso = (Get-Date).AddMonths(-$StaleMonths).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ")

# Pass 1: Graph-side filter on last sign-in
$staleBySignIn = Get-MgDevice -Filter "approximateLastSignInDateTime lt $cutoffIso" `
                              -ConsistencyLevel 'eventual' -All

# Pass 2: devices that never signed in report null, so fall back to creation date
$neverSignedIn = Get-MgDevice -All | Where-Object {
  -not $_.ApproximateLastSignInDateTime -and $_.CreatedDateTime -lt $cutoffUtc
}

# Fail-closed: skip devices in the protection group; stop the run if it can't be read
$staleAll = $staleAll | Where-Object { -not $excludedDeviceIds.Contains($_.Id) }
# Full script includes ledger tracking, WhatIf mode, and HTML email reporting
      
Download Full Script

View the full project write-up →

triggeradobesync.ps1

Triggers an on-demand Entra-to-Adobe SCIM provisioning sync via Microsoft Graph, so time-sensitive access requests don't wait on the scheduled window.

Connect-MgGraph -AppId $AppId -TenantID $TenantId -CertificateThumbprint $Thumbprint

# Resolve the Adobe enterprise app's service principal
$sp = Get-MgServicePrincipal -Filter "displayName eq 'Adobe Identity Management (OIDC)'"

# Retrieve its synchronization job and capture the job Id
$jobs  = Get-MgServicePrincipalSynchronizationJob -ServicePrincipalId $sp.Id
$jobId = ($jobs | Select-Object -First 1 -ExpandProperty Id)

# Start the provisioning job immediately
Start-MgServicePrincipalSynchronizationJob -ServicePrincipalId $sp.Id -SynchronizationJobId $jobId
# Full script includes job status reporting (status code, last successful execution time)
      
Download Full Script

View the full project write-up →

BiweeklyMailboxPermissionReport.ps1

Pulls mailbox permission changes from the Microsoft 365 unified audit log on a biweekly cadence, resolves GUIDs to readable identities, and emails a timestamped CSV report.

# Pull mailbox permission changes for the last 14 days
$AuditData = Search-UnifiedAuditLog -StartDate $StartDate -EndDate $EndDate `
    -Operations Add-MailboxPermission, Remove-MailboxPermission, Add-RecipientPermission, Remove-RecipientPermission `
    -ResultSize 5000

# Two-tier identity resolution: Exchange Online lookup first, then Graph fallback
function Get-ResolvedUPN {
    param([string]$Identity)
    try {
        $exo = Get-Recipient -Identity $Identity -ErrorAction Stop
        if ($exo.PrimarySmtpAddress) { return $exo.PrimarySmtpAddress.ToString() }
    } catch { }
    try { $mgUser = Get-MgUser -UserId $guid -ErrorAction Stop; if ($mgUser.UserPrincipalName) { return $mgUser.UserPrincipalName } } catch { }
    return $Identity
}
# Full script includes caching, CSV export, and Graph email delivery
      
Download Full Script

View the full project write-up →

PhishCleanup_AllMailboxes.ps1

Interactive incident response tool for tenant-wide search and soft-delete of phishing email, with PIM elevation checks, mandatory preview, typed confirmation, and audit logging.

# Confirm Global Administrator is actively elevated via PIM before doing anything
$gaActive = $instances | Where-Object {
    $_.RoleDefinition.DisplayName -in @("Global Administrator","Company Administrator")
}
if (-not $gaActive) {
    Write-ErrorMsg "Global Administrator is NOT ACTIVE for $UserPrincipalName."
    exit 1
}

# Typed confirmation required before the tenant-wide purge executes
do {
    Write-Warn "Type exactly: PURGE-ALL, anything else will do nothing."
    $confirm = Read-Host "Confirm"
} until ($confirm -eq "PURGE-ALL")

New-ComplianceSearchAction -SearchName $searchName -Purge -PurgeType SoftDelete
# Full script includes input validation, compliance search creation and polling, preview, and CSV/JSON audit logging
      
Download Full Script

View the full project write-up →

MonthlyMailboxQuotaEnforcement.ps1

Monthly Exchange Online quota enforcement that compares each mailbox against policy and corrects only what has drifted, with tiered exception groups and email reporting.

# Quota policy definitions: IssueWarning / ProhibitSend / ProhibitSendReceive
$DefaultQuotas = @{ Warn = "23GB"; Send = "25GB"; SendReceive = "50GB"  }
$Tier1Quotas   = @{ Warn = "48GB"; Send = "50GB"; SendReceive = "100GB" }
$Tier2Quotas   = @{ Warn = "72GB"; Send = "75GB"; SendReceive = "100GB" }
$SharedQuotas  = @{ Warn = "48GB"; Send = "50GB"; SendReceive = "50GB"  }

# Compare current quotas against policy; only act if something is different
function Set-MailboxQuotaIfNeeded {
    param ($Identity,$Category,$Warn,$Send,$SendReceive)
    $mb = Get-Mailbox -Identity $Identity
    if ($oldWarn -ne $newWarn -or $oldSend -ne $newSend -or $oldSR -ne $newSR) {
        Set-Mailbox -Identity $Identity -IssueWarningQuota $Warn `
            -ProhibitSendQuota $Send -ProhibitSendReceiveQuota $SendReceive
    }
}
# Full script includes value normalization, per-mailbox error isolation, CSV audit export, and HTML email reporting
      
Download Full Script

View the full project write-up →

FileShareToSharePointSync.ps1

One-way sync from a network file share to a SharePoint library, with signature-based change detection, locked-file handling, persistent state, and failure escalation.

# Skip locked/in-use files: attempt an exclusive read, retry on the next run
function Test-FileLocked {
    param([string]$Path)
    try {
        $stream = [System.IO.File]::Open($Path, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read, [System.IO.FileShare]::None)
        $stream.Close()
        return $false
    } catch { return $true }
}

# Signature = path + last-write time + size; skip if unchanged since last upload
$Signature = "$($File.FullName)|$($File.LastWriteTimeUtc.Ticks)|$($File.Length)"
if ($State.Files.ContainsKey($File.FullName) -and $State.Files[$File.FullName] -eq $Signature) {
    continue
}
# Full script includes state persistence, folder structure mirroring, temp file filtering, and Graph email escalation
      
Download Full Script

View the full project write-up →