Category Archives: PowerShell

PowerShell Reporting Scripts

We all have projects that involve gathering a list of basic information about the computer and user objects in Active Directory. The PowerShell scripts can make these inventory activities less time consuming, and help us to collect information about computers’ models, BIOS versions, processors and registry settings, documenting the existence of the needed registry keys (for example key for Meltdown& Spectre mitigation).

The PowerShell [PSCustomObject] can without difficulty combine information from different WMI classes, gather the computers’ registry settings, and produce one inventory object that can then be passed / worked on or exported to CSV file.

If we add a progress bar to these reporting scripts, and use the Activity, Status, and CurrentOperation parameters, we will achieve the granularity and deeper level of detail into the scripts’ processing and the status of running commands.
I hope that the example presented here will show you the advantages of using PSCustomObject and encourage you to use it in your reporting scripts.

# ================================================================
# Filename: Report-ComputerHardwareInfo.ps1
# Date:     Ferbruary, 2018
# Author:   Alex Dujakovic
# Description: reporting script to collect info about computers' 
# hardware and Meltdown/Spectre registry key
# ================================================================

# VARIABLES - adjust values to your environment
# ---------------------------------------------------------------

# CSV file to collect / store info about computers
$csvFilePath = "C:\PSScripts\Hardware\PC_HardwareAndRegInfo.csv"

# Collect info for this Registry path 
$MeltdownSpectreRegistryPath = "SOFTWARE\Microsoft\Windows\CurrentVersion\QualityCompat"

# Collection of the computers for inventory
#
# Use an OU container for computers' names
# ---------------------------------------------------------------
$myPcCollection = [ADSI]"LDAP://OU=Computers,OU=Accounts,DC=AlexTest,DC=Local"

# Log file to log errors
$logFile="C:\PSScripts\Hardware\HardwareAndReg.log"

# ================================================================

# Check if .CSV file exist, and create empty one with column names
if (!(Test-Path -path $csvFilePath)) { 
""|Select Name,PcManufacturer,PcModel,UserName,SerialNumber,BIOSName,Version,
SMBIOSBIOSVersion,BIOSManufacturer,Processor,ProcessorModel,ProcessorManufacturer,
Key,KeyValue,RegPath | 
Export-Csv -Path $csvFilePath -NoTypeInformation}

$PCcollection = ($myPcCollection.psbase.Children| Where -FilterScript{$_.ObjectCategory -Like "*Computer*"} | 
Select-Object -ExpandProperty Name)

# Computers contained in CSV File will not be pinged again
$reachedPCs = (Import-Csv -Path $csvFilePath | Select-Object -ExpandProperty Name)

# Creating progress bar properties
$Activity = "OBJECTIVE: Collecting Info - Creating Report"
$CurFolderText = '"Item: $($CurNumber.ToString().PadLeft($collectionItem.Count.ToString().Length)) of $($collectionItem) "'
$CurFolderBlock = [ScriptBlock]::Create($CurFolderText)

# ---------------------|START OF FUNCTIONS|----------------------

Function GetRemoteReg($computerName,$state){
Try{
$svcState = (Get-WmiObject -Class Win32_Service -ComputerName $computerName `
-Filter "Name = 'RemoteRegistry'" -ErrorAction Stop).State 
Write-Host "$($ComputerName) - State: $svcState" -ForegroundColor DarkGray
Switch($state){
"START"{
   Switch($svcState){
    "Stopped"{(Get-WmiObject -ComputerName $computerName Win32_Service `
    -Filter "Name = 'RemoteRegistry'").StartService()
    Write-Host "$($ComputerName) - Started RemoteRegisty Service" -ForegroundColor DarkGray
    Break
   }
    "Running"{Write-Host "$($ComputerName) - RemoteRegisty Service Running" `
    -ForegroundColor DarkGray; Break}
   } Break
      }
"STOP"{
   Switch($svcState){
    "Stopped"{Write-Host "$($ComputerName) - RemoteRegisty Service Stopped" `
    -ForegroundColor DarkGray; Break}
    "Running"{(Get-WmiObject -ComputerName $computerName Win32_Service `
    -Filter "Name = 'RemoteRegistry'").StopService()
    Write-Host "$($ComputerName) - Stopped RemoteRegisty Service" -ForegroundColor DarkGray
    Break
    }    
   } Break
     }
 }
}
Catch{
        Write-Host "$($ComputerName) - RemoteRegisty Service Error: `
        $($_.Exception.GetType().FullName) - $($_.Exception.Message)" `
        -ForegroundColor DarkGray
    }
}

Function Get-RegistryPath {

Param([Management.ManagementObject]$Reg,[int64]$Hive,[string]$regpath)

Function Get-RegistryValue {
    
Param([Management.ManagementObject]$Reg,
    [int64]$Hive,
    [string]$regitem,
    [string]$value,
    [int32]$iType)
    
$obj=New-Object PSObject
       
switch ($iType) {
    1 {$data=($reg.GetStringValue($Hive,$regitem,$value)).sValue}
    2 {$data=($reg.GetExpandedStringValue($Hive,$regitem,$value)).sValue}
    3 {$data="Binary Data"}
    4 {$data=($reg.GetDWordValue($Hive,$regitem,$value)).uValue}
    7 {$data=($reg.GetMultiStringValue($Hive,$regitem,$value)).sValue}
    default {$data="Unable to retrieve value"}
}
           
Add-Member -inputobject $obj -membertype "NoteProperty" -name Key -value $value
Add-Member -inputobject $obj -membertype "NoteProperty" -name KeyValue -value $data 
Add-Member -inputobject $obj -membertype "NoteProperty" -name RegPath -value $regitem
Add-Member -inputobject $obj -membertype "NoteProperty" -name Hive -value $hive
Add-Member -inputobject $obj -membertype "NoteProperty" -name KeyType -value $iType 
write $obj
           
} # end of Get-RegistryValue Subfunction

# obtain values of current registry key
 $values=$Reg.enumValues($Hive,$regpath)
 if ($values.snames.count -gt 0) {
    for ($i=0;$i -lt $values.snames.count;$i++) {
       $iType = $values.types[$i]
       $value = $values.snames[$i]
       Get-RegistryValue $Reg $Hive $regpath $value $iType
    }
 }  
 
$keys=$Reg.EnumKey($Hive,$regpath)

# enumerate any sub keys
if ($keys.snames.count -gt 0) {
   foreach ($item in $keys.snames) {
    #recursively call this function
    Get-RegistryPath $Reg $hive "$regpath\$item"
   }
 }
} # end of Get-RegistryPath function

Function Get-HardwareInfo($ComputerName){

# define reg for remote computer
[WMIClass]$Reg = "\\$($computerName)\root\default:StdRegProv"
# define registry hive as a numeric constant
$HKLM=2147483650
# define registry path
$registryPath = "$($MeltdownSpectreRegistryPath)"

#Get computer system Information
$compSystem = Get-WmiObject -Class Win32_ComputerSystem -ComputerName $ComputerName
#Get CPU Information
$CPUInfo = Get-WmiObject -Class Win32_Processor -ComputerName $ComputerName
#Get BIOS Information
$BIOS = Get-WmiObject -Class Win32_BIOS -ComputerName $ComputerName 
# start remote computer's registry
GetRemoteReg -computerName $ComputerName -state "START" | Out-Null
# obtain infor from remote registry
$regQuery = (Get-RegistryPath -Reg $Reg -Hive $HKLM -regpath "$($registryPath)" | 
Select Key,Keyvalue,RegPath)
# stop remote computer's registry
GetRemoteReg -computerName $ComputerName -state "STOP" | Out-Null

# create PowerShell custom object
$OS = [PSCustomObject]@{
    # Date = Get-Date
    Name = $ComputerName
    PcManufacturer = $compSystem.Manufacturer
    PcModel = $compSystem.Model
    UserName = $compSystem.UserName
    #
    SerialNumber = $BIOS.SerialNumber
    BIOSName = $BIOS.Name
    Version = $BIOS.Version 
    SMBIOSBIOSVersion = $BIOS.SMBIOSBIOSVersion
    BIOSManufacturer = $BIOS.Manufacturer
    #
	Processor =  $CPUInfo.Name
	ProcessorModel =  $CPUInfo.Description
	ProcessorManufacturer =  $CPUInfo.Manufacturer
    #
    Key = $regQuery.Key
    KeyValue = $regQuery.KeyValue
    RegPath = $regQuery.RegPath
} 

$OS | Export-Csv -Path $csvFilePath -NoTypeInformation -Append

}

# -------------|END OF FUNCTIONS|-----------------

$newQueryCollection = (Compare-Object -ReferenceObject `
$PCcollection -DifferenceObject $reachedPCs |
Where-Object -FilterScript {$_.SideIndicator -eq "<="} |
Select-Object -ExpandProperty InputObject | 
Sort InputObject)

    $pcCount = 1
    $CurNumber = 0
    $collectionItem = $newQueryCollection.Count

foreach ($Computer In $newQueryCollection){

    $CurNumber++
    $CurFolderpPercent = $CurNumber / $collectionItem * 100
    $Task = "Current Task: processing $($Computer)"
    Write-Progress -Id 1 -Activity $Activity -Status (& $CurFolderBlock) `
    -CurrentOperation $Task -PercentComplete $CurFolderpPercent

Write-Host "$($Computer) - Try Ping" -ForegroundColor White

    If (Test-Connection -ComputerName $Computer -ea silentlycontinue -Count 1) {
        Write-Host "$($Computer) - Ping Successful" -ForegroundColor Green
        try{
            $ErrorActionPrefference = "STOP"
            Get-HardwareInfo -ComputerName $Computer | Out-Null
        }
        catch [System.Management.Automation.RuntimeException] {
        # Any WMI related service is disabled/not responding.
        Write-Host "$($Computer) - No RPC Server" -ForegroundColor Yellow
        }
        catch{
            "$($Computer) - Error: $($_.Exception.GetType().FullName) - $($_.Exception.Message)" |
            Out-file -FilePath $logFile -Encoding ascii -Append 
        }
    }
    else{
        Write-Host "$($Computer) - Ping Failed" -ForegroundColor Red
    }    
 $pcCount ++    
}
Write-Host "Script finished execution" -ForegroundColor Yellow

To reiterate the advantages of PSCustomeObject:
• combine information from different sources (WMI classes, Registry settings, functions)
• preserve order in display
• quicker processing

The script with name Report-ComputerHardwareInfo.ps1 could be downloaded from the download/PowerShell section of this site.


Exchange – PowerShell AD User Properties Script

If you would like to read the other parts in this blog series please go to:

  1. Logon Script and PowerShell ADAssist Tool (Part 1)
  2. PowerShell ADAssist Tool and List-ADUsers Script (Part 2)
  3. PowerShell AD User Properties Script (Part 3)
  4. Exchange – PowerShell AD User Properties Script (Part 4)

This is the final blog in this series about the user accounts’ administration, and here I will present this Windows Form with two additional tabs: Exchange General and Mailbox Settings. This is the updated Display-ADUserProperties.ps1 script.

From the previous blog (part 3) in this series, you’ve learned that this script gives you the ability to:

  • Display and edit a user’s attributes in AD
  • Change a user’s password settings
  • Unlock / disable / enable a user’s account and manage account’s expiration
  • Delete / move an account and clear the selected user’s account attributes
  • View / export / import a user’s settings from an XML file
  • Manage a user’s groups’ membership in AD
  • Apply template settings
  • Copy and create a new user account in AD

In addition to the above list, this script being updated with the Exchange cmdlets enables you to do the following:

  • Create a new user and its mailbox or create a mailbox for the existing user
  • Hide, disable and remove a mailbox
  • Clear, add/edit, remove the custom attributes
  • Manage the mailbox storage limits and item retention settings
  • View / change the delivery options and delivery restrictions

Please note that Display-ADUserProperties.ps1 script is launched by both the AD-Assist.ps1 or List-ADuser.ps1 script, and it is placed in the same folder – ADAssist Application (in my example it is extracted inside C:\PSScript\AD-Assist folder).

If an account you want to administer is not mailbox enabled, you will see the ‘Enable User Mailbox’ button. To enable this account, click the ‘Enable User Mailbox’ button and in the form that pops up, select the checkbox to make the form’s controls enabled. From the drop down list, select the Exchange database and click the ‘Create New User Mailbox’ button as shown in the picture 1.

Picture 1: Enable mailbox for a user account

Note: you will see this button for every user that is mailbox enabled, but hasn’t logged on to mailbox yet, so there is no data to return. After the user logs on, this button will no longer appear.

The Exchange General tab, displays info about the user’s mailbox, such as the number of total items, its size, the database name, and the dates it is being created/modified. All mailbox’s custom attributes presented in the textboxes are editable and to commit their change (clear, add/edit, remove attributes in their corresponding textboxes) click the ‘Update Exchange Info’ button. To hide/unhide the mailbox or to disable/remove it, use the buttons provided on the form.

Picture 2: Exchange General tab shows info about the mailbox’s properties and custom attributes.

The Mailbox Settings tab, displays different settings, like the mailbox storage limits, its items’ retention settings, delivery options and delivery restrictions. All these settings could be changed (clear, add/edit, remove) by the controls and buttons provided on the form.

Picture 3: Mailbox Settings tab

Important: I’ve updated the Display-ADUserProperties.ps1 script and the download file (ADAssist Tool) just before publishing this post and you can download this compressed file from the download/application section of this site.