I have a need to find the size of user's home directories. However the user's sub directories are restricted to the SYSTEM and specific user. So any PowerShell script I've tried to run has so far failed to gain permission. Even when run as admin. I was directed to this script which supposedly enables some backup privileges that may allow my script access enough to find the size of the directory. However incorporating this function with the primary script appears to have failed.
The program TreeSize has no issue pulling this information and providing me a report. And at this point I'm very close to just buying a license and using it instead of trying to recreate the functionality in PowerShell. But before I throw in the towel I figured I'd post my as-is script here and see what the community thought of it. Currently the script will error out and say access is denied when trying to access the user's home directories.
Perhaps someone here can make this work?
# Define the Privileges enum
enum Privileges {
SeBackupPrivilege
# Add other privileges as needed
}
# Define the structures required for privilege enabling
Add-Type @"
using System;
using System.Runtime.InteropServices;
public struct TokPriv1Luid {
public int Count;
public long Luid;
public int Attr;
}
public class PoshPrivilege {
[DllImport("kernel32.dll", ExactSpelling = true)]
public static extern IntPtr GetCurrentProcess();
[DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)]
public static extern bool OpenProcessToken(
IntPtr h,
int acc,
ref IntPtr phtok
);
[DllImport("advapi32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool AdjustTokenPrivileges(
IntPtr htok,
bool disall,
ref TokPriv1Luid newst,
int len,
IntPtr prev,
IntPtr relen
);
[DllImport("advapi32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool LookupPrivilegeValue(
string host,
string name,
ref long pluid
);
}
"@
Function Enable-Privilege {
[cmdletbinding(
SupportsShouldProcess = $True
)]
Param (
[parameter(Mandatory = $True)]
[Privileges[]]$Privilege
)
If ($PSCmdlet.ShouldProcess("Process ID: $PID", "Enable Privilege(s): $($Privilege -join ', ')")) {
#region Constants
$SE_PRIVILEGE_ENABLED = 0x00000002
$SE_PRIVILEGE_DISABLED = 0x00000000
$TOKEN_QUERY = 0x00000008
$TOKEN_ADJUST_PRIVILEGES = 0x00000020
#endregion Constants
$TokenPriv = New-Object TokPriv1Luid
$HandleToken = [intptr]::Zero
$TokenPriv.Count = 1
$TokenPriv.Attr = $SE_PRIVILEGE_ENABLED
#Open the process token
$Return = [PoshPrivilege]::OpenProcessToken(
[PoshPrivilege]::GetCurrentProcess(),
($TOKEN_QUERY -BOR $TOKEN_ADJUST_PRIVILEGES),
[ref]$HandleToken
)
If (-NOT $Return) {
Write-Warning "Unable to open process token! Aborting!"
Break
}
ForEach ($Priv in $Privilege) {
$PrivValue = $Null
$TokenPriv.Luid = 0
#Lookup privilege value
$Return = [PoshPrivilege]::LookupPrivilegeValue($Null, $Priv, [ref]$PrivValue)
If ($Return) {
$TokenPriv.Luid = $PrivValue
#Adjust the process privilege value
$return = [PoshPrivilege]::AdjustTokenPrivileges(
$HandleToken,
$False,
[ref]$TokenPriv,
[System.Runtime.InteropServices.Marshal]::SizeOf($TokenPriv),
[IntPtr]::Zero,
[IntPtr]::Zero
)
If (-NOT $Return) {
Write-Warning "Unable to enable privilege <$priv>! "
}
}
}
}
}
# Check if the Active Directory module is loaded
if (-not (Get-Module -Name ActiveDirectory -ErrorAction SilentlyContinue)) {
# Try to load the Active Directory module
try {
Import-Module ActiveDirectory -ErrorAction Stop
} catch {
Write-Host "Failed to load the Active Directory module. Please ensure the Active Directory RSAT tools are installed."
exit 1
}
}
$recipients = "Admin@domain.local"
$sender = "Users-Group@domain.local"
$subject = "Group User Backup Report"
# Get all members of the "users-Group" group recursively
$members = Get-ADGroupMember -Identity "users-Group" -Recursive | Get-ADUser -Properties DisplayName, Mail, HomeDirectory, Department
# Create an array to store the results
$results = @()
foreach ($member in $members) {
$displayName = $member.DisplayName
$mail = $member.Mail
$homeDirectory = $member.HomeDirectory
$department = $member.Department
# Use a local path for calculating the size
#$localHomeDirectory = $homeDirectory -replace '\\domain.local\dfs\users\', 'E:\folder\Users\'
$localHomeDirectory = $homeDirectory -replace [regex]::Escape('\\domain.local\dfs\users\'), 'E:\folder\Users\'
# Check if $localHomeDirectory is not null before testing its path
if ($localHomeDirectory -ne $null -and (Test-Path $localHomeDirectory)) {
# Get the size of the local home directory
$homeDirectorySize = (Get-ChildItem -Path $localHomeDirectory -Recurse | Measure-Object -Property Length).Sum
} else {
# Set $homeDirectorySize to 0 if $localHomeDirectory is null or the path doesn't exist
$homeDirectorySize = 0
}
# Convert the size to a human-readable format
$homeDirectorySizeFormatted = "{0:N2} MB" -f ($homeDirectorySize / 1MB
)
# Create a custom object with the user details and home directory size
$userDetails = [PSCustomObject]@{
DisplayName = $displayName
Mail = $mail
HomeDirectory = $homeDirectory # Display UNC path in the email
Department = $department
HomeDirectorySize = $homeDirectorySizeFormatted
}
# Add the user details to the results array
$results += $userDetails
}
# Sort the results by display name
$results = $results | Sort-Object DisplayName
# Convert the results to HTML
$htmlBody = $results | ConvertTo-Html | Out-String
# Send the email
Send-MailMessage -To $recipients -From $sender -Subject $subject -Body $htmlBody -BodyAsHtml -SmtpServer "smtpgw.domain.local"