Maintenance.psm1

# IMPORTANT: THIS SCRIPT IS A CONCEPT AND IS INCOMPLETE!

# Aim: Periodically run script and automatically report any concerns to helpdesk via email.

# Bot: Intergrate with SlackBot for self-help where possible. Could use multilpe functions.

# Tips: If you remove-module, then try to install-module and it doesnt appear, remove remains of old module from Program Files(86)

# Actions: User prompt | Self-elevates PowerShell | Gathers admin credentials | Starts reporting | Updates modules | Creates restore point | Backups .pst files | Queiries packages | Queiries domain | Sets active hours | Disable fast boot | Remove office activiation key | Clear system caches | Delete old files | Empty recycle bin | Gathers CPU usage | Gathers available RAM | Checks defrag percentage | Queries device serial | Queries asset tag | Checks OS build number | Checks total disk space | Checks system temperature | Queries dhcp | Checks bitlocker encryption | Queries Anti-Virus | Queries Trusted Platform Module | Queries Access Control Level | Checks Secure Boot | Runs DISM | Queries software | Checks timezone | Checks system language | Tests gateway | Queries DNS | Queries open ports | Queries mapped drives | Queires system users | Queries drivers | Checks battery status | Checks for critical logs | Runs System File Checker | Updates Group Policy | Queries default printer | Checks system uptime | Enables maximum performance for wireless adapters | Disables monitor timeout | Disables disk timeout | Disables standby timeout | Measure script speed | Sends report to helpdesk if issues detected | Restart system.

function Run_Maintenance
{

# User Prompt.
$continue = (Read-Host -p "WARNING: IF YOU'RE UNSURE WHAT THIS SCRIPT DOES, THEN REFRAIN FROM USING IT! Press any button to execute or X to cancel")
if ($continue -eq 'X')
{
 return
}

# Self-elevate.
if (-Not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] 'Administrator')) {
 if ([int](Get-CimInstance -Class Win32_OperatingSystem | Select-Object -ExpandProperty BuildNumber) -ge 6000) {
  $CommandLine = "-File `"" + $MyInvocation.MyCommand.Path + "`" " + $MyInvocation.UnboundArguments
  Start-Process -FilePath PowerShell.exe -Verb Runas -ArgumentList $CommandLine
  Exit
 }
}

# Gets credentials.
$credentials = (Get-Credential)
if ($credentials -eq $null)
{exit}

# Start Report.
#$report = @()

# Update Modules.
$modules = (Get-Module | Select-Object 'Name')
foreach ($module in $modules)
{
Update-Module -Name $module
}

# Create Restore Point.
$date = (Get-Date)
$LastPoint = (Get-ComputerRestorePoint -LastStatus)
if ($LastPoint.CreationTime -le $date)
{
Checkpoint-Computer -Description $date -RestorePointType MODIFY_SETTINGS
}
else
{continue}

# Outlook PST Backup. (Need file server address)
#Copy-Item "C:\Outlook_Files\*.pst" "\\fileserver\temp-backups\pst\"

# Query Packages. (Need file server address) (NuGet + PowerShellGet)
#$CurrentPackages = (get-packageprovider)
#$packages = (\\fileserver\csv\packages.csv) #Need CSV file for package names
#foreach ($package in $packages)
#{
#if ($package.Name -eq $CurrentPackages)
#{continue}
#else
#{
# Install-PackageProvider -Name $package.Name -Force
#}
#}

# Query Domain. (Need domain name & domain controllers name)
#$domain = 'domain.net'
#$DomainChecker = (Get-CimInstance -ClassName Win32_ComputerSystem | select domain)
#if ($DomainChecker -eq $domain)
#{continue}
#else
#{Add-Computer -DomainName $domain -Server 'domain.net\DC01'}

# Set Active Hours.
$start = '9'
$finish = '9'
$HoursStart = (Get-ItemProperty -Path HKLM:\SOFTWARE\Microsoft\WindowsUpdate\UX\Settings -Name ActiveHoursStart)
$HoursEnd = (Get-ItemProperty -Path HKLM:\SOFTWARE\Microsoft\WindowsUpdate\UX\Settings -Name ActiveHoursEnd)
if (($HoursStart -eq '9') -and ($HoursEnd -eq '9'))
{continue}
else
{
New-ItemProperty -Path HKLM:\SOFTWARE\Microsoft\WindowsUpdate\UX\Settings -Name ActiveHoursStart -Value $start -PassThru
New-ItemProperty -Path HKLM:\SOFTWARE\Microsoft\WindowsUpdate\UX\Settings -Name ActiveHoursEnd -Value $finish -PassThru
}

#Disable Fast Boot (Add query)
REG ADD "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Power" /v HiberbootEnabled /t REG_DWORD /d "0" /f
#New-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Power -Name HiberbootEnabled -Value '0' -PassThru

#Office Activiation Fix (Add query or all users will need to login to one of the Microsoft Office applications.)
Remove-Item HKLM:SOFTWARE\Microsoft\Office\10.0, HKLM:SOFTWARE\Microsoft\Office\12.0, HKLM:SOFTWARE\Microsoft\Office\15.0, HKLM:SOFTWARE\Microsoft\Office\16.0 -Confirm:$false -Force

# Clear Caches. (Add statement to skip showing errors that relate to files being used by another process)
$user= ((Get-WMIObject -ClassName Win32_ComputerSystem).PrimaryOwnerName)
Remove-Item -Path "C:\Users\$user\AppData\Local\Temp\*" -Force -Recurse
Remove-Item -Path "C:\Users\$user\AppData\Local\Microsoft\Windows\Explorer\*" -Force -Recurse
Remove-Item -Path "C:\Users\$user\AppData\Roaming\Microsoft\Windows\Recent\*" -Force -Recurse
Remove-Item -Path "C:\Windows\Temp\*" -Force -Recurse
Remove-Item -Path "C:\Windows\prefetch\*" -Force -Recurse
Remove-Item -Path "C:\Windows\SoftwareDistribution\*" -Force -Recurse
Clear-DnsClientCache

# Deletes files and folders within the downloads folder, older than 30 days.
$age = (Get-Date).AddDays(-30)
Get-ChildItem 'C:\Users\$user\Downloads\*' -Recurse -File | ForEach-Object{
    if ($_.CreationTime -le $age){
        Remove-Item $_.fullname -Force -Verbose
    }else{
        continue
    }
}

#Empty Recycle Bin
Clear-RecycleBin -Force

#CPU Usage
$pro = ((Get-Counter '\Processor(_Total)\% Processor Time').CounterSamples.CookedValue)

#Available RAM
$mem = ((Get-Counter '\Memory\Available MBytes').CounterSamples.CookedValue)

#Check Drive (GETTING ERROR: Line 63) (Need it to check post defrag report and gather results from that. -replace 'Toalfrgmentdspac', '%')
#$frag = Optimize-Volume -DriveLetter C -Analyze -Verbose 4>&1
#$frag | select-string 'Total fragmented space'
#if ($frag -ge '10')
#{
#Optimize-Volume -DriveLetter C -Defrag -Verbose
#}
#elseif ($frag -le '10')
#{
#continue
#}

# Check Serials. (Need file server address)
#$Serials = Import-Csv -Path '\\fileserver\csv\serials.csv'
$SerialNo = (Get-CimInstance -ClassName CIM_BIOSElement | select 'SerialNumber')

# Check Asset TAGs. (Need file server address)
#$SysDetails = (\\fileserver\csv\)
$name = ($env:computername)
$tag = (Get-CimInstance -ClassName CIM_Chassis | select 'SMBIOSAssetTag')
$descritpion = (Get-WmiObject -Class Win32_OperatingSystem | Select 'Description')

#Check Build (How to read specific data from json)
#$build = (Get-CimInstance -ClassName CIM_OperatingSystem | select 'BuildNumber', 'Version')
#$Response = (Invoke-WebRequest -URI https://learn.microsoft.com/en-us/windows/release-health/windows11-release-informa)

# Disk Space.
$DiskSpace = (Get-WmiObject -Class Win32_logicalDisk | Where-Object {$_.DeviceID -eq 'C:'})
$FreeSpace = ($DiskSpace.FreeSpace / $DiskSpace.Size)

#Get temperature (INCOMPLETE!)
#$t = (Get-WmiObject MSAcpi_ThermalZoneTemperature -Namespace "root/wmi")
#$returntemp = @()
#foreach ($temp in $t.CurrentTemperature)
#{
# $currentTempKelvin = $temp / 10
# $currentTempCelsius = $currentTempKelvin - 273.15
# $returntemp += $currentTempCelsius.ToString()
#}
#return $returntemp
#$temp = Get-Temperature

#Query if DHCP enabled.
$LinkStatus = (Get-NetAdapter | Where-Object -FilterScript {$_.Status -Eq "Up"})
$adapters = (Get-NetIPConfiguration -Detailed | Select-Object InterfaceAlias, @{N="DHCP";E={$_.NetIPv4Interface.DHCP}})
foreach ($adapter in $adapters)
{
if ($dhcp.DHCP -eq 'Enabled')
{continue}
elseif ($dhcp.DHCP -eq 'Disabled')
{
Set-NetIPInterface -dhcp --Enabled $LinkStatus.ifIndex #Might need a ForEach statement for each interface number
}
}

#Check drive encryption status
$lock = (Get-BitLockerVolume -MountPoint "C:" |  Select-Object volumetype, protectionstatus -Verbose)

#Query AV
#Updating Antimalware Definitions
Update-MpSignature -UpdateSource MicrosoftUpdateServer
#Setting configuration to check for definition updates daily.
Set-MpPreference -SignatureScheduleDay Everyday
#Getting status of Antimalware Protection software
$AV = (Get-MpComputerStatus)
#Starting quick scan
Start-MpScan -ScanType QuickScan
#Removing any active threats
Remove-MpThreat

#Query TPM
$tpm = (get-tpm | Select-Object 'TpmEnabled')
if ($tpm -eq $true)
{continue}
elseif ($tpm -eq $false)
{Enable-TpmAutoProvisioning #Dont think this is the right command to enable TPM}
}

#Query ACL (Need file server address)
$acls = (get-acl)
#$OwnerAccess = (#csv list)

#Check if Secure Boot is active
$secboot = (Confirm-SecureBootUefi)

#DISM will connect to Windows Update online to download and replace damaged files if necessary.
Repair-WindowsImage -Online -RestoreHealth
$repair = (Repair-Volume -DriveLetter C -Scan)

# Software
#$AppX = (Get-AppxPackage)
#if ($AppX.Name -eq 'Microsoft.DesktopAppInstaller')
#{continue}
#else
#{Add-AppxPackage -RegisterByFamilyName -MainPackage Microsoft.DesktopAppInstaller_8wekyb3d8bbwe}
##Update source repos
winget source update
##Search for software and install if not present.
#$SoftwarePacks = 'Microsoft Update Health Tools', 'Microsoft Edge'
#$searches = (winget search -q $SoftwarePacks)
#foreach ($search in $searches)
#{
# if ($searches -eq $search)
# {continue}
# else
# {
# $install = $SoftwarePacks
# winget install -q $install
# }
#}
##Upgrade all packages to latest version.
winget upgrade --all --include-unknown --verbose-logs

# Check timezone/language
$lang = 'en-GB'
$country = 'United Kingdom'
$timezone = 'GMT Standard Time'
$timezoneId = (get-timezone | Select-Object Id)
$WinSysLocale = (get-WinSystemLocale | Select-Object Name)
$WinUserLangList = (get-WinUserLanguageList | Select-Object LanguageTag)
$HomeLocation = (get-WinHomeLocation | Select-Object 'HomeLocation')
if ($timezoneId -eq $timezone)
{continue}
else
{
#Set the timezone
Set-TimeZone "$timezone"
get-winsystemlocale
get-winhomelocation
}
if ($WinSysLocale -eq $lang)
{continue}
else
{
 #Set system local
 Set-WinSystemLocale $lang
}
if ($WinUserLangList -eq $lang)
{continue}
else
{#Override language list with just English GB
$1 = New-WinUserLanguageList $lang
$1[0].Handwriting = 1
Set-WinUserLanguageList $1 -force
Set-WinUILanguageOverride -Language $lang
}
if ($HomeLocation -eq $country)
{continue}
else
{
#Set home location to United Kingdom
Set-WinHomeLocation 0xf2
}

#Test gateway connection
$DefaultGateway = get-netipconfiguration -Detailed
$Addresses = $DefaultGateway.IPv4DefaultGateway | Select-Object NextHop
$Ping = (Test-Connection $Addresses -Count 1 )

#Check DNS settings
$DNS1 = '9.9.9.11'
$DNS2 = '142.112.112.11'
$DNS = Get-DnsClientServerAddress
if ($DNS -eq $DNS1, $DNS2)
{continue}
else
{Set-DnsClientServerAddress -InterfaceAlias WiFi -ServerAddresses ($DNS1,$DNS2)}

#Query open ports (Need file server address)
#$ports = (Get-NetTCPConnection)
#$ActivePorts = ('80') #Create csv of active ports

#Query mapped drives (Need file server address)
#$drives = (Get-PSDrive)
#$MappedDrives = ('\\serveraddress\lists\.csv') #Create csv of active ports
#foreach ($drive in $drives)
#{
#if ($drive.Provider -eq 'FileSystem' -and $drive.Root -eq $MappedDrives.Root)
#{continue}
#elseif ($drive.Provider -eq 'FileSystem' -and $drive.Root -ne $MappedDrives.Root)
#{
#New-PSDrive -Persist -Name "$MappedDrives.Name" -PSProvider "FileSystem" -Root "$MappedDrives.Root"
#}
#}

#Query system users (Need file server address) (INCOMPLETE!)
#$SysUsers = (net user)
#$System = (hostname)
#$SysUserList = (#csv)

#Check and display outdated drivers
$date = (Get-Date)
$drivers = (Get-WindowsDriver -Online -All | select 'Driver', 'Date', 'Version')

# Check battery status and remaining charge
$battery = (Get-CimInstance -ClassName CIM_Battery | Select-Object 'Status')

#Check for Critical events
$LogDate = (get-date) - (New-TimeSpan -Day 1)
$logs = (get-winevent | Where-Object {$_.LevelDisplayName -eq $LogDate, 'Critical'})

#SFC
sfc /scannow

#Force Group Policy Update
GPUpdate /Force

#Find default printer
$printers = Get-WmiObject -Query " SELECT * FROM Win32_Printer WHERE Default=$true"
foreach ($printer in $printers)
{
 #Restarting spooler
 Restart-Service -Name Spooler
 #Testing default printer
 {$printers.PrintTestPage()}
}

#Check Uptime.
$uptime = Get-Uptime
if ($uptime -ge 1)
{
 #Hibernate timeout
 powercfg /hibernate off
}
#Wireless Adapter Power Mode: On battery - Maximum Performance
powercfg /SETDCVALUEINDEX SCHEME_CURRENT 19cbb8fa-5279-450e-9fac-8a3d5fedd0c1 12bbebe6-58d6-4636-95bb-3217ef867c1a 0 -Verbose
#Wireless Adapter Power Mode: Plugged in - Maximum Performance
powercfg /SETACVALUEINDEX SCHEME_CURRENT 19cbb8fa-5279-450e-9fac-8a3d5fedd0c1 12bbebe6-58d6-4636-95bb-3217ef867c1a 0 -Verbose
#Monitor timout
powercfg /change monitor-timeout-ac 0
powercfg /change monitor-timeout-dc 0
#Disk timeout
powercfg /change disk-timeout-ac 0
powercfg /change disk-timeout-dc 0
#Standby timeout
powercfg /change standby-timeout-ac 0
powercfg /change standby-timeout-dc 0

#Measure how long it takes script to execute
#$script = ('\\fileserver\Maintenance.ps1')
#$scripts = (Get-Content $script)
#$speed = ((Measure-Command {$scripts}).TotalSeconds)


#Send email to admin if any issues occur. ()
if ($report -eq ($frag -ge '10') -xor (($tag -ne '') -and ($name -ne '') -and ($description -ne '')) -xor ($SerialNo -ne $serial) -xor ($acls.Owner -ne '$OwnerAccess.Owner' -xor $acls.Access -ne '$OwnerAccess.Access') -xor ($ports.State -eq 'Established Internet' -and $ports.LocalPort -ne $ActivePorts) -xor ($SysUsers -and $System -ne $SysUserList.User -and $SysUserList.System) -xor ($speed -ge '1') -xor ($SerialNo -ne '$serial') -xor ($drivers.Date -le $date) -xor ($LastPoint -eq 'The last attempt to restore the computer failed.') -xor ($uptime -ge 100) -xor ($battery -eq 'BAD') -xor ($DomainChecker -eq 'WORKGROUP')  -xor ($Ping.Timeout -ge '40000') -xor ($secboot -eq $false) -xor ($uptime -ge '24:00:00.0000000') -xor ($FreeSpace -le '0.100000000000000') -xor ($pro -ge '99.0') -xor ($mem -le '1000') -xor ($temp -ge '60.00') -xor ($lock.ProtectionStatus -eq 'Off') -xor ($AV.AntivirusEnabled -eq $false) -xor ($repair -eq 'ErrorsFound') -xor ($log.LevelDisplayName -eq 'Critical') -xor ($tag -ne '') -xor ($name -ne '') -xor ($description -ne ''))
{
Export-Csv -Path 'C:\maintenance_report_$date.csv' -NoTypeInformation -Append -Force
Send-MailMessage -From 'Automator <automator@domain.co.uk>'-To 'Help Desk <healpdesk@domain.co.uk>' -Subject 'MAINTENNCE ISSUE: Report for $user' -UseSsl -Credential $credentials -SmtpServer smtp.domain.co.uk -Port 587 -Priority High -DeliveryNotificationOption OnFailure -Attachments 'C:\maintenance_report_$date.csv'
}

# System Restart
#Restart-Computer

}
Export-ModuleMember -Function 'Run_Maintenance' -Alias 'runmain'