Quantcast
Channel: admin | Alex Computer Bubble Blog
Viewing all 40 articles
Browse latest View live

PowerShell Reporting Scripts

$
0
0

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.



WinRE and ReCustomization.xml File

$
0
0

If you would like to read the other parts in this blog series please go to:
1.Wireless Support WinRE 10 – Part One
2.Wireless Support WinRE 10 – Part Two
3.PowerShell – Create USB Recovery Drive

Microsoft provides plenty of information regarding Windows Recovery Environment (Windows RE) as well as documents describing the process of adding a custom troubleshooting or diagnostic tool to your Windows RE image.
Unfortunately, there is no information about a ReCustomization.xml file, which is the product of the customization process where you add a custom tool and make sure that the custom tool appears in the Boot Options menu when launched from Windows.

The REAgentC.exe is a tool to configure a Windows RE boot image and to administer recovery options and customizations. It could be used on an offline Windows image or on a running Windows operating system.

For example, you can run command ReAgentc.exe /info to find out the current status of Windows RE, and check if it is installed and where, see the picture 1 below.

Picture 1: display result of ReAgentc.exe /info command

In the case that Windows RE is being disabled on your system, the command will display different info, see the picture 2.

Picture 2: display the result of two commands: ReAgentc.exe and Bcdedit on a system where Windows RE is being disabled.

On the assumption that you want to have Windows RE enabled, and a custom tool added to the boot options menu, you would run the following command:

Reagentc /setbootshelllink /configfile C:\SWSetup\AddDiagnosticsToolToBootMenu.xml

where C:\SWSetup is the folder holding the AddDiagnosticsToolToBootMenu.xml file. This command, when successful, will produce the ReCustomiztion.xml file and place it in the Windows\System32\Recovery folder (see the picture 3).

Notice the syntax for this command:
/setbootshelllink [/configfile <path_to_BootShellXML>] [/target <path_to_offline_image>]

Picture 3: the creation of Windows RE ReCustomization.xml file

Use the /target option to specify the location of the offline Windows image.
If this argument is not used, the running operating system is used as shown in the picture above.

The content of ReCustomization.xml file is as follows:

<?xml version=”1.0″ encoding=”utf-8″?>
<!– AddDiagnosticsToolToBootMenu.xml –>
<BootShell>
<WinRETool locale=”en-us”>
<Name>Alex Computer Bubble</Name>
<Description>Troubleshoot Computer Settings</Description>
</WinRETool>
</BootShell>

If you had a chance to create a WinRE USB Recovery drive, as described in my previous blogs, place this file into the USB source folder where a boot.wim is located.



Please note: Although the author has made every reasonable attempt to achieve complete accuracy of the content, he assumes no responsibility for errors or omissions. Also, you should use this information as you see fit, and at your own risk.

DiskPart GUI – Windows 10 (USB Stick multi partition support)

$
0
0

If you would like to read the other parts in this blog series please go to:
1. DiskPart GUI PowerShell – Part One
2. DiskPart GUI – Update – Part Two

In this follow-on post, I’ll provide the script you can use to automatically format the USB and Hard Drives. It is important to note that Microsoft has added a new feature to Windows 10 Creators Update with ability to create multiple partitions on a USB drive, allowing you to have a single USB key with a combination of FAT32 and NTFS partitions.
Therefore, to work with USB drives that have multiple partitions, your technician PC has to have Windows 10, version 1703 installed, with the most recent version of the Windows ADK; see the table below.

Table 1: List of Windows ADK versions

In addition, I wanted to provide a script that automatically partitions the disk in accordance with Microsoft latest recommendation: the recovery partition to be located after the Windows partition in the last place.Figure 1: The layout of the updated DiskPart tool for Windows 10

Figure 2: The process of creating multiple partitions on a single USB drive

And if you want to make the selected USB flash drive bootable with WinPE, just use ‘Select Folder’ button to browse to the location of the WinPE bootable files and click ‘Copy’ button to copy files into the Boot partition.
This script could be found in the downloaded section – folder Application.
 
Sponsored Links:
 



Dynamic Distribution Groups – DDL

$
0
0

The Dynamic Distribution group or list (DDL) could be created in Exchange using the following two native tools:
• Exchange Administration Console (HTML based)
• Exchange Management Shell (Command line based)

The Shell has an advantage over EAC due to the ability to change the properties that aren’t available in the EAC.

The DDL is a type of list with the memberships produced by a query on the other mail-enabled objects, and therefore it does not hold a defined set of members, but the membership list (for a dynamic distribution group) is calculated each time a message is sent to a DDL, based on the filters and conditions defined at time a DDL is created.

The easiest way to create, view and change properties of a DDL is to use the native Exchange Management Shell, and the following three cmdlets:
• New-DynamicDistributionGroup
• Get-DynamicDistributionGroup
• Set-DynamicDistributionGroup

Creating a new DDL

To create a DDL you can use the following Script:

# Creating a new DDL
# To create Dynamic Distribution group or list (DDL):
$DDLName = “Marketing Team – GAD”
$DDLAlias = “GAD-Mktg”
$ouName = "OU=Marketing,OU=Accounts,DC=GAD,DC=com"

# This cmdlet will create a DDL in the OU with UserMailboxes who match the specified Title and Company name 
New-DynamicDistributionGroup -Name $DDLName -Alias $Alias -OrganizationalUnit $ouName `
-RecipientFilter {(RecipientType -eq 'UserMailbox') `
-and (Title -like 'Actuary' -or Title -like 'Adjuster' -or Title -like 'Analyst') `
-and (company -eq 'XYZ Insurance')}

Sometimes, just before running a PowerShell script, it is a good practice to use another cmdlet: Get-Recipient and test the filter; it should produce the list of the recipients in the case that you wanted to preview the membership in the shell.
First start with this:

# Just before running a PowerShell script, to priview the Recipients and test the filter, do this:
Get-Recipient -RecipientPreviewFilter{(RecipientType -eq 'UserMailbox') `
-and (Title -like 'Actuary' -or Title -like 'Adjuster' -or Title -like 'Analyst') `
-and (company -eq 'XYZ Insurance')}

And then run this cmdlet to produce the number of users in the DDL:

# preview the number of the Recipients
Get-Recipient -RecipientPreviewFilter{(RecipientType -eq 'UserMailbox') `
-and (Title -like 'Actuary' -or Title -like 'Adjuster' -or Title -like 'Analyst') `
-and (company -eq 'XYZ Insurance')}| Measure-Object

View and change DDL properties

Use the Get-DynamicDistributionGroup and Set-DynamicDistributionGroup cmdlets to view and change properties for dynamic distribution groups.

View the list of recipients:

# To get list of DDL recipients based on the filter, do this: 
Get-Recipient -RecipientPreviewFilter `
(Get-DynamicDistributionGroup “Marketing Team – GAD”).RecipientFilter `
-RecipientType UserMailbox -SortBy Name | 
FT -Auto Name, DisplayName, Title, Company, Department

# or this:
Get-DynamicDistributionGroup “Marketing Team – GAD” | 
ForEach {Get-Recipient -RecipientPreviewFilter $_.RecipientFilter } |
Select Name, DisplayName, Title, Company, Department | 
Sort Name | Format-Table

# The result could be something like this:
<# 
Name DisplayName Title Company Department 
---- ----------- ----- ------- ---------- 
Blue, Kirk Blue.K@MyInsurance.com Actuary XYZ Insurance GAD 
Smith, Joseph Smith.J@MyInsurance.com Adjuster XYZ Insurance Sales 
King, Robert King.R@MyInsurance.com Adjuster XYZ Insurance Sales 
Gallant, Wade Gallant.W@MyInsurance.com Analyst XYZ Insurance IT 
Willow, Daniel Willow.D@MyInsurance.com Actuary XYZ Insurance GAD 
Bond, Rayan Bond.R@MyInsurance.com Analyst XYZ Insurance IT
#>

View the number of recipients:

Get-DynamicDistributionGroup “Marketing Team – GAD” | 
ForEach {Get-Recipient -RecipientPreviewFilter $_.RecipientFilter } | 
Select Name, Title, Company, DisplayName | Measure-Object

# The result is 21 as shown below:
Count    : 21
Average  :
Sum      :
Maximum  :
Minimum  :
Property :

To change the properties of DDL:

# You can edit the dynamic distribution list 

Set-DynamicDistributionGroup -identity “Marketing Team – GAD” 
-RecipientFilter {(RecipientType -eq 'UserMailbox') `
-and (Title -like 'Actuary' -or Title -like 'Adjuster' -or Title -like 'Analyst' `
-or Title -like 'Examiner' -or Title -like 'Underwriter' -or Title -like 'Broker' `
-or Title -like 'Consultant' -or Title -like 'Appraiser') -and (company -eq 'XYZ Insurance')}

If you want to view the filter only:

# You changed the DDL and you want to see the newly created filter.
# To view the newly created DDL's filter, run the following:
$DDL = Get-DynamicDistributionGroup “Marketing Team – GAD”

$DDL.RecipientFilter

# The Filter:
((((((RecipientType -eq 'UserMailbox') -and (((((((((((((((Title -like 'Actuary') `
  -or (Title -like 'Adjuster'))) -or (Title -like 'Analyst'))) -or (Title -like 'Examiner'))) `
  -or (Title -like 'Underwriter'))) -or (Title -like 'Broker'))) -or (Title-like 'Consultant'))) `
  -or (Title -like 'Appraiser'))))) -and (Company -eq 'XYZ Insurance'))) -and `
  (-not(Name -like 'SystemMailbox{*')) -and (-not(Name -like 'CAS_{*')) -and `
  (-not(RecipientTypeDetailsValue -eq 'MailboxPlan')) -and `
  (-not(RecipientTypeDetailsValue -eq 'DiscoveryMailbox')) -and `
  (-not(RecipientTypeDetailsValue -eq 'ArbitrationMailbox')))

$DDL.RecipientFilter | Measure-Object

# The result is 30 as shown below:
Count    : 30
Average  :
Sum      :
Maximum  :
Minimum  :
Property :

I hope that this little post and the scripts presented above will deepen your interest in PowerShell as it clearly has an advantage over EAC due to the ability to change the properties that aren’t available in the EAC.


Backup and Restore Printers with PowerShell

$
0
0

The Windows 10 operating system includes apps or special wizards that enables a computer users to export and import printers’ setting to a special compressed file, with a default name MyPrinters.printerExport, and therefore provide the backup and restore operations for clients’ printers, including their queues, configured ports, and drivers.

Note: The location of these special apps/wizards:
•          Printer Migration Wizard: PrintBRMUi.exe (%systemroot\system32)
•          Command Line Tool: PrintBrm.exe (%systemroot\system32\spool\tools)
The PrintBRM tool can be run over the network to remote servers, but the Print$ share must exist on both the source and target server and the Remote Registry Service must be running.

The PowerShell script presented here is for a computer users who do not have elevated rights, and want a simple and efficient way to backup and restore their respective network printers. The script produces a log file to document its backup and restore operation and creates the shortcut files for all client’s network printers, see the figure 1 below.

Figure 1: Log file documenting backup and restore operations

To use this script, you could type the following to back up all the network printers:

MyNetworkPrinters.ps1 -backUpTo H:\MyHomeFolder\MyNetPrinters

And to restore them, you could type:

MyNetworkPrinters.ps1 -restoreFrom H:\MyHomeFolder\MyNetPrinters

The backup and/or restore location (in my example: H:\MyHomeFolder\MyNetPrinters) could be any network folder or a USB drive.

Here is the PowerShell script:

# Name:   MyNetworkPrinterr.ps1
# Author: Alex Dujakovic
# Date:   January 2019
# Description: Back Ups Network Printers to specified folder
# Restores Network Printers from specified folder
# Examples:
# MyNetworkPrinters.ps1 -backUpTo Q:\NetHomeFolder\MyNetPrinters
# MyNetworkPrinters.ps1 -restoreFrom Q:\NetHomeFolder\MyNetPrinters</code></pre>
[CmdletBinding(SupportsShouldProcess=$True,DefaultParametersetName="BackUp")]
param(
[Parameter(ParameterSetName="BackUp", Mandatory=$True, Position=0)]
[string]$backUpTo,

[Parameter(ParameterSetName="Restore", Mandatory=$True, Position=0)]
[string]$restoreFrom
)
#---------------------------|Start Of Functions|----------------------------
Function msgbox {
param (
[string]$Message,
[string]$Title = 'Message box title',
[string]$buttons = 'OKCancel'
)
#Load the assembly
Add-Type -AssemblyName System.Windows.Forms | Out-Null
switch ($buttons) {
'ok' {$btn = [System.Windows.Forms.MessageBoxButtons]::OK; break}
'okcancel' {$btn = [System.Windows.Forms.MessageBoxButtons]::OKCancel; break}
'AbortRetryIgnore' {$btn = [System.Windows.Forms.MessageBoxButtons]::AbortRetryIgnore; break}
'YesNoCancel' {$btn = [System.Windows.Forms.MessageBoxButtons]::YesNoCancel; break}
'YesNo' {$btn = [System.Windows.Forms.MessageBoxButtons]::yesno; break}
'RetryCancel'{$btn = [System.Windows.Forms.MessageBoxButtons]::RetryCancel; break}
default {$btn = [System.Windows.Forms.MessageBoxButtons]::RetryCancel; break}
}

# Display the message box
$Return=[System.Windows.Forms.MessageBox]::Show($Message,$Title,$btn)
$Return
}

Function do_Restore-NetPrinters($restoreFrom){

$Log = "$($env:COMPUTERNAME)_BackUpAndRestoreLog.log"
"RESTORE NETWORK PRINTERS" |
Out-file -FilePath "$($restoreFrom)\$($Log)" -Encoding ascii -Append |
Out-Null
<h1>Add Printers</h1>
Function Add-MyNetPrinter($pathToFolder){
Get-ChildItem -Path "$($pathToFolder)" |
ForEach-Object{
If($_.Extension -eq ".lnk"){
Try{
$ErrorActionPreference = "STOP"
Start-Process "$($_.FullName)"
"Installing printer: $($_.FullName)"|
Out-file -FilePath "$($restoreFrom)\$($Log)" -Encoding ascii -Append
}
Catch{
"Error restoring printer: $($_.FullName)" |
Out-file -FilePath "$($restoreFrom)\$($Log)" -Encoding ascii -Append
"ERROR: $($_.Exception.GetType().FullName) - $($_.Exception.Message)"|
Out-file -FilePath "$($restoreFrom)\$($Log)" -Encoding ascii -Append
}
}
}
}
#Set Default Printer
Function Set-MyDefaultPrinter($pathToCSVFile){
Import-Csv -Path "$($pathToCSVFile)" |
ForEach-Object {
If($_.Default -eq $true){
Try{
$ErrorActionPreference = "STOP"
(New-Object -ComObject WScript.Network).SetDefaultPrinter("$($_.Name)")
"Setting default printer: $($_.Name)"|
Out-file -FilePath "$($restoreFrom)\$($Log)" -Encoding ascii -Append
}
Catch{
"Error setting default printer: $($_.Name)" |
Out-file -FilePath "$($restoreFrom)\$($Log)" -Encoding ascii -Append
"ERROR: $($_.Exception.GetType().FullName) - $($_.Exception.Message)"|
Out-file -FilePath "$($restoreFrom)\$($Log)" -Encoding ascii -Append
}
}
}
}

if (Test-Path -Path "$($restoreFrom)" -PathType Container){
"Network printer restore folder found!"|
Out-file -FilePath "$($restoreFrom)\$($Log)" -Encoding ascii -Append
Add-MyNetPrinter -pathToFolder "$($restoreFrom)" | Out-Null
Start-Sleep -Seconds 1
}
Else{
msgbox -Message "Restore Network printer folder not found!" -Title `
"Restore Network Printers" -buttons OK
}

if (Test-Path -Path "$($restoreFrom)\$($env:USERNAME)_Printer.csv" -PathType Leaf){
"Setting default printer - network printer CSV file found!"|
Out-file -FilePath "$($restoreFrom)\$($Log)" -Encoding ascii -Append
Set-MyDefaultPrinter -pathToCSVFile "$($restoreFrom)\$($env:USERNAME)_Printer.csv"|
Out-Null
Start-Sleep -Seconds 1
}
Else{
"Network printer CSV File not found!"|
Out-file -FilePath "$($restoreFrom)\$($Log)"
msgbox -Message "Could not set default printer!" -Title <code>
"Restore Network Printers" -buttons OK
}
msgbox -Message "Restore of my Network Printers finished!" -Title</code>
"Restore Network Printers" -buttons OK
}

Function do_BackUp-NetPrinters($backUpTo){

$Log = "$($env:COMPUTERNAME)_BackUpAndRestoreLog.log"
"BACK-UP NETWORK PRINTERS" |
Out-file -FilePath "$($backUpTo)\$($Log)" -Encoding ascii -Append

Function CreateShortcut($shortcutName,$TargetFile){
Try{
$ErrorActionPreference = "STOP"
$ShortcutFile = "$($backUpTo)\$($shortcutName).lnk"
$WScriptShell = New-Object -ComObject WScript.Shell
$Shortcut = $WScriptShell.CreateShortcut($ShortcutFile)
$Shortcut.TargetPath = $TargetFile
$Shortcut.Save()
<pre><code>"Printer Shortcut: $($shortcutName) created successfully" | 
Out-file -FilePath "$($backUpTo)\$($Log)" -Encoding ascii -Append</code></pre>
}Catch{
"Error creating printer shortcut: $($shortcutName)" |
Out-file -FilePath "$($backUpTo)\$($Log)" -Encoding ascii -Append
"ERROR: $($_.Exception.GetType().FullName) - $($_.Exception.Message)"|
Out-file -FilePath "$($backUpTo)\$($Log)" -Encoding ascii -Append
}
}
Try{
$ErrorActionPreference = "STOP"
$printerCollection = (Get-WmiObject win32_printer -ComputerName $env:COMPUTERNAME|
Select-Object -Property Name, Network, Default, Location, ShareName, ServerName)
$printerCollection |
Export-csv -Path "$($backUpTo)\$($env:USERNAME)_Printer.csv" -NoTypeInformation -Append
"Network Printer log file created successfully named: $($env:USERNAME)_Printer.csv" |
Out-file -FilePath "$($backUpTo)\$($Log)" -Encoding ascii -Append
Start-Sleep -Seconds 1
$printerCollection |
    ForEach{
        If($_.Network -eq $True) {CreateShortcut -shortcutName "$($_.ShareName)" `
        -TargetFile "$($_.Name)" | Out-Null}
}
}Catch{
msgbox -Message "Error creating log file!" -Title "Back-Up Network Printers" -buttons OK
"Error creating Network Printer log file named: $($env:USERNAME)<em>Printer.csv"|
Out-file -FilePath "$($backUpTo)\$($Log)" -Encoding ascii -Append
"ERROR: $($</em>.Exception.GetType().FullName) - $($_.Exception.Message)"|
Out-file -FilePath "$($backUpTo)\$($Log)" -Encoding ascii -Append
}
msgbox -Message "Back-up of Network Printers finished!" -Title `
"Back-Up Network Printers" -buttons OK
}
#---------------------------|End Of Functions|----------------------------------
switch ($PsCmdlet.ParameterSetName) 
{ 
"Restore" {do_Restore-NetPrinters $restoreFrom} 
"BackUp"  {do_BackUp-NetPrinters $backUpTo} 
}

This script could be found in the downloaded section – folder PowerShell.

Sponsored links:




Dynamic Distribution Groups – DDL

$
0
0

The Dynamic Distribution group or list (DDL) could be created in Exchange using the following two native tools:

  • Exchange Administration Console (HTML based)
  • Exchange Management Shell (Command line based)

The Shell has an advantage over EAC due to the ability to change the properties that aren’t available in the EAC.

The DDL is a type of list with the memberships produced by a query on the other mail-enabled objects, and therefore it does not hold a defined set of members, but the membership list (for a dynamic distribution group) is calculated each time a message is sent to a DDL, based on the filters and conditions defined at time a DDL is created.

The easiest way to create, view and change properties of a DDL is to use the native Exchange Management Shell, and the following three cmdlets:

  • New-DynamicDistributionGroup
  • Get-DynamicDistributionGroup
  • Set-DynamicDistributionGroup

Creating a new DDL

To create a DDL you can use the following Script:

# Creating a new DDL
# To create Dynamic Distribution group or list (DDL):
$DDLName = “Marketing Team – GAD”
$DDLAlias = “GAD-Mktg”
$ouName = "OU=Marketing,OU=Accounts,DC=GAD,DC=com"

# This cmdlet will create a DDL in the OU with UserMailboxes who match the specified Title and Company name 
New-DynamicDistributionGroup -Name $DDLName -Alias $Alias -OrganizationalUnit $ouName `
-RecipientFilter {(RecipientType -eq 'UserMailbox') `
-and (Title -like 'Actuary' -or Title -like 'Adjuster' -or Title -like 'Analyst') `
-and (company -eq 'XYZ Insurance')}

Sometimes, just before running a PowerShell script, it is a good practice to use another cmdlet: Get-Recipient and test the filter; it should produce the list of the recipients in the case that you wanted to preview the membership in the shell.

First start with this:

# Just before running a PowerShell script, to priview the Recipients and test the filter, do this:
Get-Recipient -RecipientPreviewFilter{(RecipientType -eq 'UserMailbox') `
-and (Title -like 'Actuary' -or Title -like 'Adjuster' -or Title -like 'Analyst') `
-and (company -eq 'XYZ Insurance')}

And then run this cmdlet to produce the number of users in the DDL:

# preview the number of the Recipients
Get-Recipient -RecipientPreviewFilter{(RecipientType -eq 'UserMailbox') `
-and (Title -like 'Actuary' -or Title -like 'Adjuster' -or Title -like 'Analyst') `
-and (company -eq 'XYZ Insurance')}| Measure-Object

View and change properties

Use the Get-DynamicDistributionGroup and Set-DynamicDistributionGroup cmdlets to view and change properties for dynamic distribution groups. View the list of recipients:

# To get list of DDL recipients based on the filter, do this: 
Get-Recipient -RecipientPreviewFilter `
(Get-DynamicDistributionGroup “Marketing Team – GAD”).RecipientFilter `
-RecipientType UserMailbox -SortBy Name | 
FT -Auto Name, DisplayName, Title, Company, Department
#
# or this:
#
Get-DynamicDistributionGroup “Marketing Team – GAD” | 
ForEach {Get-Recipient -RecipientPreviewFilter $_.RecipientFilter } |
Select Name, DisplayName, Title, Company, Department | 
Sort Name | Format-Table
#
# The result could be something like this:
<#

Name                DisplayName					  Title     Company        Department
----                -----------                   -----     -------        ----------
Blue, Kirk		    Blue.K@MyInsurance.com		  Actuary   XYZ Insurance  GAD
Smith, Joseph		Smith.J@MyInsurance.com       Adjuster  XYZ Insurance  Sales
King, Robert		King.R@MyInsurance.com        Adjuster  XYZ Insurance  Sales
Gallant, Wade		Gallant.W@MyInsurance.com     Analyst   XYZ Insurance  IT
Willow, Daniel		Willow.D@MyInsurance.com      Actuary   XYZ Insurance  GAD
Bond, Rayan 		Bond.R@MyInsurance.com        Analyst   XYZ Insurance  IT

#>

View the number of recipients:

Get-DynamicDistributionGroup “Marketing Team – GAD” | 
ForEach {Get-Recipient -RecipientPreviewFilter $_.RecipientFilter } | 
Select Name, Title, Company, DisplayName | Measure-Object
#
# The result is 121 as shown below:
Count    : 21
Average  :
Sum      :
Maximum  :
Minimum  :
Property :

To change the properties of DDL:

# You can edit the dynamic distribution list 
#
Set-DynamicDistributionGroup -identity “Marketing Team – GAD” 
-RecipientFilter {(RecipientType -eq 'UserMailbox') `
-and (Title -like 'Actuary' -or Title -like 'Adjuster' -or Title -like 'Analyst' `
-or Title -like 'Examiner' -or Title -like 'Underwriter' -or Title -like 'Broker' `
-or Title -like 'Consultant' -or Title -like 'Appraiser') -and (company -eq 'XYZ Insurance')}

If you want to view the filter only:

# You changed the DDL and you want to see the newly created filter.
# To view the newly created DDL's filter, run the following:
$DDL = Get-DynamicDistributionGroup “Marketing Team – GAD”

$DDL.RecipientFilter

# The Filter:
((((((RecipientType -eq 'UserMailbox') -and (((((((((((((((Title -like 'Actuary') `
  -or (Title -like 'Adjuster'))) -or (Title -like 'Analyst'))) -or (Title -like 'Examiner'))) `
  -or (Title -like 'Underwriter'))) -or (Title -like 'Broker'))) -or (Title-like 'Consultant'))) `
  -or (Title -like 'Appraiser'))))) -and (Company -eq 'XYZ Insurance'))) -and `
  (-not(Name -like 'SystemMailbox{*')) -and (-not(Name -like 'CAS_{*')) -and `
  (-not(RecipientTypeDetailsValue -eq 'MailboxPlan')) -and `
  (-not(RecipientTypeDetailsValue -eq 'DiscoveryMailbox')) -and `
  (-not(RecipientTypeDetailsValue -eq 'ArbitrationMailbox')))

$DDL.RecipientFilter | Measure-Object

# The result is 121 as shown below:
Count    : 30
Average  :
Sum      :
Maximum  :
Minimum  :
Property :

The samples of the scripts in this blog could be found in the download section – folder PowerShell.




Search and Install Network Printers with PowerShell

$
0
0

In my blog – Backup and Restore Printers with PowerShell, I presented a script for a computer users who do not have elevated rights, and want a simple and efficient way to back-up and restore their respective network printers. In this post, I will present a PowerShell script that enables users to install network printers in an easy way, using a simple Windows Presentation Foundation (WPF) form as shown below.

Search and Install Network Printers

Note: The printer image I’ve used on this WPF form is an image courtesy of ‘digitalart’ at FreeDigitalPhotos.net.

By default, the domain users do not have permissions to install the printer drivers on the domain computers; their installation requires the local Administrator rights. This default setting could be exceedingly inconvenient because it requires IT Support team involvement anytime a user tries to install a new printer driver.

In my example, the domain users do not need to have the local Administrator rights; they simply search for network printers by selecting a print server from a drop down list, optionally type a printer’s name or just part of its name and click the ‘Find’ button. The search result is then displayed in the form’s grid view, presenting a list of printers with their shared names, locations, model and a comment (see figure 1). The presented data content can be sorted when a user clicks on a column header. These settings allow users to easily find printers based on their physical location and their name.

By selecting the check box and clicking the ‘Install’ button, users can observe the process of downloading and installing the drivers for the select network printer.

Figure 1: WPF form’s grid view displays a list of printers with their shared names, locations, model and a comment.

Note the way I’ve built the ComboBox (drop down list containing the list of print server names).

This could be done differently, for example by building a list with the print servers’ names obtained from a CSV file.

In addition to installing network printers, this app could be used to configure one new Window 10 OS feature: Windows Default Print Management. By default, Windows 10 OS quietly configures the default printer, but if users find this setting to be undesirable, a click on the ‘Change’ button will change this option to Off.

To achieve this, the PowerShell script uses the following registry setting to turn on/off Windows Default Print Management:

Hive: HKEY_CURRENT_USER

Key Path: Software\Microsoft\Windows NT\CurrentVersion\Windows

Value name: LegacyDefaultPrinterMode

Value type: REG_DWORD

Value data: 1

Base: Decimal

This script presented in this blog could be found in the download section – folder Application.



WPF ComboBox – One or Two Columns (PowerShell)

$
0
0

In my previous post – Search and Install Network Printers with PowerShell, you could read about a PowerShell script that enables users to install network printers using a simple Windows Presentation Foundation (WPF) form.

The ComboBox in the above mentioned form has a drop down list containing the list of all the print server’s names, and the code in the script looks like the following lines:

$XAML = @"
<Window

<ComboBox Name="ComboBoxUnitTXTFile" HorizontalAlignment="Left" Margin="210,75,0,0"
<ComboBoxItem Name="cbSelectFirst" IsSelected="True">ABC-PrintServer</ComboBoxItem>
<ComboBoxItem Name="cbSelectSecond">CBA-PrintServer</ComboBoxItem>
<ComboBoxItem Name="cbSelectThird">AAA-PrintServer</ComboBoxItem>
<ComboBoxItem Name="cbSelectFourth">CCC-PrintServer</ComboBoxItem>
</ComboBox>

</Window>
"@

This could be done differently, for example by building a list with the print servers’ names obtained from a CSV file.

WPF Form with One ComboBox Column

The ComboBox with one column and CSV file as the source of the list with servers’ names could be created in WPF form as shown below:

Name,Location
ACB-PRINT001,Halifax (First)
ACB-PRINT002,Halifax (Second)
BED-PRINT003,Bedford
DAR-PRINT004,Dartmouth (ABC)

Add-Type -AssemblyName PresentationFramework
# =========================|Functions|=========================
Function PopulateComboBox{

Import-Csv -Path "Blog\WPF-ComboBox\PrintServers.csv"|
Select-Object -Property Name | Sort-Object -Property Name |
ForEach-object {
$PrinterNames.AddChild($_.Name)
}
}

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

$XAML = @"
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="PowerShell WPF – One Column ComboBox" WindowStartupLocation = "CenterScreen" Height="290" Width="450">
<Grid>
<Label Content="PowerShell WPF – One Column ComboBox" Height="30" HorizontalAlignment="Left" Margin="30,5,0,0" Name="LabelTitle" VerticalAlignment="Top" Width="380" FontSize="18" FontWeight="Bold"/>
<Label Content="Select Print Server:" Height="28" HorizontalAlignment="Left" Margin="50,70,0,0" Name="LabelUnit" VerticalAlignment="Top" />
<ComboBox Name="ComboBoxUnitTXTFile" HorizontalContentAlignment="Left" Margin="210,75,0,0" VerticalAlignment="Top" Width="140" ToolTip="Select one Print Server form the list"/>
<Button Content="RUN" Height="23" HorizontalAlignment="Left" Margin="180,200,0,0" Name="BtnSearchForFile" VerticalAlignment="Top" Width="100" ToolTip="Click this button to find Print Server"/>
<Button Content="CLOSE" Height="23" HorizontalAlignment="Left" Margin="300,200,0,0" Name="BtnClose" VerticalAlignment="Top" Width="100" ToolTip="Click this button to close this form"/>
</Grid>
</Window>
"@

# Read XAML
$reader=(New-Object System.Xml.XmlNodeReader $xaml)
$Form=[Windows.Markup.XamlReader]::Load( $reader )

#Controls
$Close = $Form.FindName(‘BtnClose’)

$TxtTextMultiLine = $Form.FindName(‘TxtDisplayTextMultiLine’)
$PrinterNames = $Form.FindName(‘ComboBoxUnitTXTFile’)
$FindPrintServer = $Form.FindName(‘BtnSearchForFile’)

$FindPrintServer.Add_Click({

If ([string]::IsNullOrEmpty($PrinterNames.Text)){
$MsgTemplate = "Drop Down List – Select Print Server is empty. Please try again and select Print Server."
[System.Windows.Forms.MessageBox]::Show($MsgTemplate,"Search Network Printers",[System.Windows.Forms.MessageBoxButtons]::Ok) | Out-Null
}
Else{
$strPrintServerName = "$($PrinterNames.Text)"
$MsgTemplate = "Selected server: $($strPrintServerName)"
[System.Windows.Forms.MessageBox]::Show($MsgTemplate,"Search Network Printers") | Out-Null
}
})
$Close.Add_Click({$Form.Close()})
$Form.Add_ContentRendered({PopulateComboBox})
$Form.ShowDialog() | Out-Null

WPF Form with Two ComboBox Columns

The ComboBox with two columns and CSV file as the source of the list with servers’ locations and names could be created in WPF form as shown below:

Add-Type -AssemblyName PresentationFramework

# =========================|Functions|===================================

Function PopulateComboBox{

$arrayPrinters = @()
Import-Csv -Path "E:\4Alex\Blog\WPF-ComboBox\PrintServers.csv"|
Select-Object -Property Location, Name | Sort-Object -Property Location |
ForEach-object {
$printServer=[PSCustomObject]@{
Name = "$($_.Name)"
Location = "$($_.Location)"
}
$arrayPrinters += $printServer
}

$arrayPrinters
$PrinterNames.DataContext = $arrayPrinters
}

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

$XAML = @"
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="PowerShell WPF – Two Column ComboBox" WindowStartupLocation = "CenterScreen" Height="290" Width="450">
<Grid>
<Label Content="PowerShell WPF – Two Column ComboBox" Height="30" HorizontalAlignment="Left" Margin="30,5,0,0" Name="LabelTitle" VerticalAlignment="Top" Width="380" FontSize="18" FontWeight="Bold"/>
<Label Content="Select Print Server:" Height="28" HorizontalAlignment="Left" Margin="50,70,0,0" Name="LabelUnit" VerticalAlignment="Top" />
<ComboBox Name="ComboBoxUnitTXTFile" HorizontalContentAlignment="Left" HorizontalAlignment="Stretch" Margin="210,75,0,0" VerticalAlignment="Top" Width="140" ToolTip="Select one Print Server form the list" ItemsSource="{Binding}">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Margin="2" Text="{Binding Name}"/>
<TextBlock Margin="2" Text="{Binding State}"/>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
<ComboBox.ItemContainerStyle>
<Style TargetType="{x:Type ComboBoxItem}">
<Setter Property="SnapsToDevicePixels" Value="True"/>
<Setter Property="OverridesDefaultStyle" Value="True"/>
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ComboBoxItem}">
<Border Name="templateBorder" Padding="0" SnapsToDevicePixels="true">
<ContentPresenter>
<ContentPresenter.Content>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Margin="4" TextAlignment="Left" Grid.Column="0" Text="{Binding Location}"/>
<TextBlock Margin="4" TextAlignment="Left" Grid.Column="1" Text="{Binding Name}"/>
</Grid>
</ContentPresenter.Content>
</ContentPresenter>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsHighlighted" Value="True">
<Setter Property="Foreground" Value="{x:Static SystemColors.HighlightTextBrush}"/>
<Setter TargetName="templateBorder" Property="Background" Value="{x:Static SystemColors.HighlightBrush}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
<Button Content="RUN" Height="23" HorizontalAlignment="Left" Margin="180,200,0,0" Name="BtnSearchForFile" VerticalAlignment="Top" Width="100" ToolTip="Click this button to find Print Server"/>
<Button Content="CLOSE" Height="23" HorizontalAlignment="Left" Margin="300,200,0,0" Name="BtnClose" VerticalAlignment="Top" Width="100" ToolTip="Click this button to close this form"/>
</Grid>
</Window>
"@

# Read XAML
$reader=(New-Object System.Xml.XmlNodeReader $xaml)
$Form=[Windows.Markup.XamlReader]::Load( $reader )

#Controls
$Close = $Form.FindName(‘BtnClose’)

$TxtTextMultiLine = $Form.FindName(‘TxtDisplayTextMultiLine’)
$PrinterNames = $Form.FindName(‘ComboBoxUnitTXTFile’)
$FindPrintServer = $Form.FindName(‘BtnSearchForFile’)

$FindPrintServer.Add_Click({

If ([string]::IsNullOrEmpty($PrinterNames.Text)){
$MsgTemplate = "Drop Down List – Select Print Server is empty. Please try again and select Print Server."
[System.Windows.Forms.MessageBox]::Show($MsgTemplate,"Search Network Printers",[System.Windows.Forms.MessageBoxButtons]::Ok) | Out-Null
}
Else{
$strPrintServerName = "$($PrinterNames.SelectedItem.Name)"
$MsgTemplate = "Selected server: $($strPrintServerName)."
[System.Windows.Forms.MessageBox]::Show($MsgTemplate,"Search Network Printers") | Out-Null
}
})
$Close.Add_Click({$Form.Close()})
$Form.Add_ContentRendered({PopulateComboBox})
$Form.ShowDialog() | out-null

This script presented in this blog could be found in the download section – folder PowerShell.




Launching Windows Special Folders

$
0
0

In my PowerShell script – Search and Install Network Printers, I used the following code to open Devices and Printers special folder:

#View Devices and Printers in Control Panel
$PreviewFile.Add_Click({
&{explorer.exe 'shell:::{A8A91A66-3A7D-4424-8D24-04E180695C7A}'}
})

Windows has many “special folders” like “Application Data”, “System” or “Start Menu”; that are either a reference to a physical file on a computer system, or a reference to a “virtual” folder, which does not actually exist on a computer’s file system, and it is simply presented through Windows Explorer as a tree of folders.

Special folders enable any application installed on the operating system to be ‘aware’ of the appropriate locations of the files needed for processing; independently of the OS version or user language being used.

These folders can be accessed by using the Windows Shell: command and the CLSID identifier codes that Windows assigns to these folders in the Windows Registry (note: a CLSID is a globally unique identifier that identifies a COM class object).

Therefore, I can open the Printers folder in the Control Panel by using the notation of two colons followed by a CLSID in curly braces. For instance,
explorer ::{21ec2020-3aea-1069-a2dd-08002b30309d}\::{2227a280-3aea-1069-a2de-08002b30309d}

Or I can use named Shell: commands as follows:
explorer shell:PrintersFolder

PowerShell and Launching Special Folders

To open special printers’ folder (Devices and printers) with PowerShell, use one of the following:

# Open printers folder
Start-Process shell:PrintersFolder

# Open Devices and printers
&{explorer.exe 'shell:::{A8A91A66-3A7D-4424-8D24-04E180695C7A}'}

&{explorer.exe 'shell:::{21ec2020-3aea-1069-a2dd-08002b30309d}\::{2227a280-3aea-1069-a2de-08002b30309d}'}

# Open Devices and printers
cmd.exe --% /c Explorer shell:::{A8A91A66-3A7D-4424-8D24-04E180695C7A} 

In PowerShell, we can use the System.Environment .NET Framework class in square brackets, like this: [System.Environment] to find the special folders. To simplify using the System namespace, PowerShell lets you use [Environment] for brevity.
Therefore, to find the paths to the special folders, I need to determine what special folders are out there and enumerate their names.

# To enumarate Special Folders
[Environment+SpecialFolder]::GetNames([Environment+SpecialFolder]) 

# To find the number of Special Folders
[System.enum]::GetNames([System.Environment+SpecialFolder]) | Measure-Object

Suppose you want to access a specific folder, such as the one named Desktop. In PowerShell syntax, the ID of this specific folder is [Environment+SpecialFolder]::Desktop. Now, you just need to provide this ID as an argument to the Environment class’s static GetFolderPath method.

# To return the exact path to the current user's Desktop folder.
[Environment]::GetFolderPath(&amp;amp;amp;amp;quot;Desktop&amp;amp;amp;amp;quot;) 

Please find the script named: Launch-SpecialFolders in the download section – folder PowerShell.

WinPE GUI – Windows 10

$
0
0

My intention is to not create a long blog post and therefore I’ve split it into three parts as follows:

  1. WinPE GUI-Windows 10 (WIM and FFU) – Part 1
  2. WinPE GUI-Windows 10 – Create and Apply WIM Images – Part 2
  3. WinPE GUI-Windows 10 – Create and Apply FFU Images – Part 3

In the end you will be able to create an application that I hope will be a nice addition to your existing PowerShell scripts and tools, particularly to your Bootable Windows PE USB Drives. The few images shown below present the WinPE GUI – Capture and Apply Windows Images (WIM & FFU).

Figure 1: WinPE GUI – Capture and Apply Windows Images (WIM & FFU)
Figure 2: WinPE GUI – Apply Windows Images (WIM & FFU)
Figure 3: WinPE GUI – Capture and create FFU Images (WIM & FFU)

This tool (named: WinPE10-UEFI-BootExternalUSBDrive) could be found in the Download section of this site – folder Application.

I appreciate very much the free image I use in this application; it is provided by PIRO4D from Pixabay.


WinPE GUI – Windows 10 (WIM and FFU) – Part 1

$
0
0

This blog has other parts:
WinPE GUI-Windows 10 – Introduction
WinPE GUI-Windows 10 – Create and Apply WIM Images – Part 2
WinPE GUI-Windows 10 – Create and Apply FFU Images – Part 3

Microsoft ADK and UEFI
Since my last blog about UEFI bootable USB drives, Microsoft kept on updating ADKs (Assessment and Deployment Kit) and the last one available for download, as of this writing is the Windows ADK for Windows 10, version 1809.
Starting with the above mentioned ADK version, Windows Preinstallation Environment (PE) is released separately from the ADK. To add Windows PE to the ADK installation, you need to download the Windows PE Addon and run the included installer after installing the ADK.

Microsoft’s ADKs are not the only software being updated. Many new Windows PCs do not include the “traditional” BIOS firmware; they use UEFI (Unified Extensible Firmware Interface) firmware instead. This “firmware” is a type of software stored on system board chips (NVRAM or ROMs) and shipped with computers and other devices. Some computer’s manufacturers still produce UEFI PCs that include BIOS-mimicking software which is officially called a Compatibility Support Module or CSM. These days almost all UEFI systems have a CSM, but the days of producing on-board chips with CSM in order to achieve backward BIOS capability software are numbered.

Why this preamble talk about UEFI is important in the process of deploying Windows 10 OS to a UEFI-based device?
Firstly, it is essential to format the hard drive that includes the Windows partition by using a GUID partition table (GPT) file system.
The default partition layout for UEFI-based PCs is as follows:

  1. System partition,
  2. MSR partition,
  3. Windows partition, and
  4. Recovery tools partition.

Secondly, if you install Windows using the wrong mode (legacy BIOS-compatibility mode over UEFI mode) you won’t be able to use all the features and benefits of UEFI firmware mode without reformatting the drive.

Thirdly, the default Windows Preinstallation Environment (WinPE) drive format, FAT32, is used to boot UEFI-based PCs, and it is too small to store most Windows images:
• FAT32 has a maximum file size of 4GB and it is not enough for the most customized Windows images that are over 4GB.
• FAT32 has a maximum partition size of 32GB, but still not enough because some customized Windows images are larger than 32GB.

Here’s the list of a few ways around these limitations used by IT professionals:

  1. Store the image on a separate USB drive
  2. Store the image on a network location
  3. Split the image
  4. Create a multiple partition USB drive

The last listed option (4) is my way to go around the above described limitations.
Create multiple partition USB drive
The first step is to obtain Windows ADK and have it installed on your computer. Once you have Windows ADK installed, please download WinPE10-UEFI-BootExternalUSBDrive.zip package from the download section, under Application folder.
After you download the file, please do the following:
⦁ Unzip the file (in my example C:\ WinPE10-UEFI-BootExternalUSBDrive).
⦁ If you want to add additional applications or drivers, please add them to the provided subfolders (Apps and Drivers). For example, if you need to add a driver for a new Solid State drive of your 64-Bit system, just add drivers into Drivers\amd64 folder (in my example C:\WinPE10-UEFI-BootExternalUSBDrive\Drivers\amd64 folder).
⦁ Connect your external USB Drive to your computer.
⦁ Find the Create-Win10BootabletExternalUSBDrive.ps1 script and run it as administrator in order to create Media files that are necessary to make the external USB drive bootable.
Again, this is a two steps process, as shown in the pictures below:

Figure 01: Find and run as admin the Create-Win10BootabletExternalUSBDrive.ps1 script

During the first step (shown in figure 2), please select the 32-bit or 64-bit version and then the folder named C:\ WinPE10-UEFI-BootExternalUSBDrive by clicking ‘Browse Source’ button. Once this folder is selected, please click ‘Start’ button to start creation of the Media files (i.e. the files and applications essential for booting USB drive).

Figure 02: The first step, select your version, the source folder and click the ‘Start’ button.

Note: Please do not close PowerShell Command window, as shown in the figure 03 below.

Figure 03: PowerShell script starts creation of the Media files

After all Media files are created, please proceed to the second step in order to make External USB Drive bootable. First click on the ‘View Drive(s)’ button to display all available drives. In my example, I selected the USB drive listed as Disk Number 1 to finalize this action, and click the ‘Run’ button.

Figure 04: Select USB drive; in this example drive with Disk Number -1 and Drive Letter – D.

The files inside the ‘Media’ folder will be copied on the active partition of a newly created bootable USB drive, as shown below.

Figure 05: The files from ‘Media’ folder being copied onto the active partition of a newly created bootable USB drive.

Finally, at the end of the second step, you will have a bootable USB drive.

Talk about Resolution

To set screen resolution in WinPE I simply put an answer file named unattend.xml in the Apps folder; the PowerShell script I use to create a bootable USB drive places it into the root of the WinPE image.
In the unattend.xml file provided in the Apps\amd64 folders, the resolution is set up as follows:
1920 Horizontal Resolution
1080 Vertical Resolution

Note: you might need to change the resolution depending on your hardware. In addition, if your Windows PE is a 32-bit Windows environment, remember to change the processor architecture from amd64 to x86. Also, note that since version 4.0, Windows PE doesn’t like 16-bit color depth, and changing color depth to 32-bit is necessary.
If you encounter any problems with resolution, you should check the entries written to the wpeinit.log file. You can read more on this topic here: https://deploymentresearch.com/Research/Post/256/Setting-screen-resolution-in-WinPE-4-0


WinPE GUI-Windows 10 – Create and Apply WIM Images – Part 2

$
0
0

This blog has other parts:
WinPE GUI-Windows 10 – Introduction
WinPE GUI-Windows 10 (WIM and FFU) – Part 1
WinPE GUI-Windows 10 – Create and Apply FFU Images – Part 3

Note, newer DISM features don’t always work when servicing images of previous versions of Windows.

Create WIM Image
To create a Wim image, go to the second tab named ‘Create Image’ and click on the DISM –WIM Create Image tab.

Figure 01: DISM –WIM Create Image tab

Type the name of the image file in the text box as well as the description (optional). I would strongly suggest that you always provide a description for an image in a form of an answer to the 5Ws. Once an image is created, it becomes available on the list of images you can apply.

Apply WIM Image
Before you apply an image, you need to select the way the computer’s hard drive will be partitioned, as shown in the figure 02.

Figure 02: select the partition scheme for the computer’s hard drive

There are three options provided: Windows 10 BIOS-MBR, Windows 10 UEFI and a special option for the situations where you want to create an image to be applied on the larger hard drives. Once you finish selecting your partition option, click on the ‘DISM Apply Image’ tab to choose an image to be applied to the computer’s hard drive, see the figure 03.

Figure 03: select an image form the drop-down list and click ‘Apply’ button to start the imaging process

Click on the drop-down arrow to select an image (from the list of all the previously created images) that you can use with this WinPE. For example, you would first select an image file and click on ‘Apply’ button to start the process of applying image on the computer’s hard drive as shown in the figure 03.
Important: when you apply an image, the computer’s hard drive will be formatted; the scripted process creates partitions (from the partition option you previously selected) prior to applying an image. At the end the script makes Windows partition bootable. Once the apply image process is successfully processed, you can click on ‘Restart’ button to restart your computer into newly applied image.


WinPE GUI-Windows 10 – Create and Apply FFU Images – Part 3

$
0
0

This blog has other parts:
WinPE GUI-Windows 10 – Introduction
WinPE GUI-Windows 10 (WIM and FFU) – Part 1
WinPE GUI-Windows 10 – Create and Apply WIM Images – Part 2

Compared to the file-based WIM image, the FFU image is a sector-based file container that stores all hard drive partitions. The sector-based imaging process means that FFU images take less time to deploy, but have larger files sizes than WIMs.
Starting with Windows 10, version 1709, DISM has the ability to capture, deploy, and service FFUs, with the following limitations:
⦁ The drive that an FFU is applied to has to be the same or larger than the drive it is captured from
⦁ FFU captures of encrypted disks are not supported
⦁ Captures of disks that have Volume Shadow Copy Service (VSS) enabled are not supported
⦁ Splitting compressed FFUs is not supported

Create FFU Image

To create a FFU image, go to ‘Create Image’ tab and click on ‘DISM – FFU Create Image’ tab, as shown in the figure 1.

Figure 1: capturing computer’s drive (drive 0) and creating FFU image file

Form the drop-down list, select the drive (in my example drive 0 is the computer’s hard drive and drive 1 is the external USB drive). Type the name for the new FFU image and description. Click the ‘Create’ button to start the process of capturing the computer’s hard drive.

Apply FFU Image

As I stated at the beginning of this post, the FFU image is a sector-based file container that stores all hard drive partitions. This application, prior to applying a FFU image, needs to know the partition scheme to be used. The FFU image may only be applied to a disk which is the same size or larger than the captured disk; therefore if your reference/source computer has the same size hard drive as your destination computer, you will select the first option.

The second option should be chosen only in the case that you captured a FFU image from a reference computer, where there is no recovery partition created, and the Windows partition has been set up as the final partition on the hard drive (see Appendix for more info).

Figure 2: select partition scheme before applying FFU image

Once you finish selecting the partition scheme, click on the FFU Apply Image tab. On this tab, select the disk drive and FFU image from drop-down lists and click the ‘Apply’ button, see the figure 3. Once the apply image process is successfully processed, you can click on ‘Restart’ button to restart your computer into newly applied image.

Figure 3: select the destination drive and the FFU image from the drop-down list

Appendix – reference PC and destination PC hard drives may be different sizes

Note that the FFU format captures the partition layout. Therefore, if the reference PC and destination PC hard drives are different, and the destination PC has larger hard drive, use this strategy to make the Windows partition as large as it can be:

⦁ On the reference/source PC, install Windows with no recovery partition, and set up the Windows partition as the final partition on the drive. To set this up, use this tool as follows: Click on ‘Create WIM Partition’ tab and select the third option.

Figure 4: Select WIM partition option, note there is no recovery partition

⦁ Click on ‘DISM Apply Image’ tab, select WIM image from the drop-down list and click ‘Apply’ button to install Windows on the last partition on the drive; click on ‘Restart’ button to boot into newly installed operating system and Customize/Sysprep, etc … your reference computer.

Figure 5: Apply WIM image

⦁ Once your reference PC is ready, boot PC with this bootable USB drive and click on ‘DISM – FFU Create Image’ tab to capture computer’s hard drive. Select the drive, type a name for FFU image and description; click ‘Create’ button.

Figure 6: Capture FFU image

Now you have a FFU image file ready to be deployed to the computers that have larger hard drives.

⦁ On the destination PC, deploy the FFU image you capture in step # 3. Go to ‘Create FFU Partition’ tab and select the second option as shown in figure 7.

Figure 7: Select FFU partition option

The above partition option will use scripts to first expand the Windows partition to fill the empty space. Then, shrink the Windows partition to make room for the recovery partition. And finally, configure the recovery partition.

⦁ Once you finish selecting partition option, go to ‘FFU Apply Image’ tab, select the drive and from the drop-down list select the FFU image you captured in step # 3. Click the ‘Apply’ button to finalize FFU imaging process.

Figure 8: Apply the FFU image you captured in step # 3

⦁ After clicking ‘Apply’ button and finalizing FFU imaging process, you can click ‘Restart’ button.


Find and back up PST files with PowerShell

$
0
0

The PowerShell script named ‘BackUp-PSTs.ps1’ is created to find all PST files (either still attached to Microsoft Outlook application as data files, stored in some network folder or in a folder on a client’s computer). All found PST files are displayed in a grid view or a tabular format, as shown in the figure 1.

The script takes just a few seconds to search for PST files. It exploits the following registry key: KEY_CURRENT_USER\Software\Microsoft\Office\15.0\Outlook\search\Catalog, which holds (catalogs) information of all the PST files attached to Microsoft Outlook application.
To clarify, this registry key contains info about all the files that are either currently connected as data files or have been connected and removed as data files from Outlook application (a history of attached archives). Therefore, it is necessary for the script to use the Test-Path cmdlet to discover only those PST files that still exist, and displays them in different color, depending on their folder locations (example: home folder – green, network folder – blue and a folder on a local hard drive –red).

Figure 1: List of found PST files

How you can utilize this script?

Once you have your client’s PST files displayed in a grid view, you can copy them to a back-up location, export them to a CSV file (log PST) and use this log to restore / attached them to a client’s Outlook application as data files.

There are other way to discover PST files

By means of WMI; you could utilize the following code that automatically searches all drives, and it could be run on remote computers:

Get-WmiObject -Query “Select * from CIM_DataFile Where Extension = ‘pst'” | Select-Object ‘Name’

By means of a batch file:

for %%a in (C: D: E:) do dir /b /s /a %%a*.pst

Or you could use a VBS script and RoboCopy (see the figure 2):

Const HKEY_CURRENT_USER = &H80000001
strComputer = "."
strKeyPath = "Software\Microsoft\Office\15.0\Outlook\search\Catalog"

Set objNet = WScript.CreateObject("WScript.Network")
Set oShell=WScript.CreateObject("WScript.Shell")
Set fso = CreateObject("Scripting.FileSystemObject")

strParms = " *.pst /s /W:1 /R:1 /LOG+:""\\ServerName\Apps\UserData\PSTFiles\RoboCopyPST.log"""
strTarget = "\\ServerName\Apps\UserData\PSTFiles"
Set oReg=GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & strComputer & "\root\default:StdRegProv")
oReg.EnumValues HKEY_CURRENT_USER, strKeyPath, arrValueNames, arrValueTypes, strValue

On Error Resume Next
For i=0 To UBound(arrValueNames)
	If IsNull(arrValueNames(i)) = True Then
		MsgBox "No PSTs"
	Else
    		If InStr(LCase(arrValueNames(i)),".pst")Then 
    			If InStr(UCase(arrValueNames(i)),"C:\")Then
    			Set f = fso.GetFile(arrValueNames(i))
    				oShell.Run "robocopy " & fso.GetParentFolderName(arrValueNames(i)) & " " & strTarget & " " & f.Name & " " & strParms
    			End If
    		End If
	End If
Next
Figure 2: Finding PST files in Mail-BackUp folder and copying them to destination folder.

The above mentioned (PowerShell and VBS) scripts could be found in their respective download section of this site.


PowerShell: Export Active Directory Groups and their Members

$
0
0

IT service desks use groups in Active Directory as collections of user and computer accounts, and other groups in order to efficiently control access to shared resources, enumerate permissions to these resources and therefore make cost effective network maintenance and administration.

The department managers and supervisors need to know how network resources are being accessed and if a least-privilege model has being enforced. They frequently submit requests to IT service desks to obtain data from Active Directory, and the one most prevalent request is getting a list of (security and distribution) groups and its members. In addition, they need these groups’ lists converted to a comprehensible format, and in my opinion, the Microsoft Excel format allows an easy transformation of data to any other formats.

In this post, I will show you how PowerShell could be used to create a simple tool that enables everyone with read access to Active Directory, to select groups, and have both groups and their members exported into a Microsoft Excel spreadsheet.

Active Directory Groups

Before I present this tool, I would like to share with you the following info about Microsoft Active Directory groups.

There are two types of groups in Active Directory:

⦁ Distribution groups; used to create email distribution lists.
⦁ Security groups; used to assign permissions to shared resources.

In addition to type, groups are categorized by a scope that defines where the group can be granted permissions. The following three group scopes are defined by Active Directory:

⦁ Universal
⦁ Global
⦁ Domain Local

PowerShell tool to view and export Groups and their members

The figure 1 shows the form to be used with this tool.

Figure 1: PowerShell tool – Export Groups and Members to Excel File

The first step is to select Active Directory container or organizational unit (OU). Please click on ‘Select OU’ button as shown in figure 2 and from a domain tree expand the containers and select one that holds the groups you want to view and export into an excel file. Once you select a container, please click on ‘OK’ button in ‘Choose Active Directory OU’ form.

Figure 2: click on ‘Select OU’ button, expand the containers and select the one that holds the groups

The path to the selected Active Directory container is displayed in the ‘Select Groups’ Container (OU) box, as shown in figure 3.

The second step is to type or select the name and the path of an excel file where all this info will be exported. The easiest way is to click on ‘Save As’ button, select a folder and type a name for an excel file (see the figure 3 below) to export data into.

Figure 3: selecting a folder location and typing a file name of an excel file to export data into

The third step is to decide what to search for in the selected Active Directory container. You have an option to either search for all groups’ objects or to filter your search by using a radio button and specifying the groups’ scope and type.

In addition, you have option to further filter your search by specifying a part of a groups’ name.
Important, this is an optional step and the text typed in is a case sensitive.

In my example, shown in figure 4, I decided to search for the global security groups in the specified OU (ABC-Groups); by clicking on the ‘Find Groups’ the search will start and the result will be displayed in the grid view.

Figure 4: Searching for global security groups in the selected OU; clicking the ‘Find Groups’ button will start the search process.

Once the search process is finished, the label on the bottom of the form (figure 5) will display a message ‘Processing Completed’, and the result will be shown in the grid view. You can use the columns in the grid view to sort the result by group name, group type or description.

Important!!! Before you want to export the groups and their members into the specified excel file, you have to select the group in the grid view. Only information for the selected groups will be exported (as shown in figure 5).

You can use ‘Select All’ and ‘Deselect All’ buttons to select and de-select the entire list of groups in the grid view.

Figure 5: select the groups you want to export to an excel file

If you want to refine you search by a part of the groups’ name in specified OU, please use the text box provided and type in just a part of a name.
In my example, shown in figure 6, I wanted to collect info about the number of the global security groups that have ‘Auctions’ in their name. After I clicked ‘Find Groups’ button, the result displayed only two groups.
I selected both groups and click ‘Export Groups’ button to export their names and members to the specified excel file.

Figure 6: Using a part of a group’s name as an additional search filter.

The above mentioned (PowerShell) scripts could be found in the download section (Applications) of this site.


PowerShell login script – map network drives based on Group Membership

$
0
0

The PowerShell logon scripts seems to be an important topic to me, and in this blog I include another logon script example that deals with mapping network shares based on a user’s security group membership and logs a user’s activity in two log files.

This blog series has other parts:
Logon Script is here to stay …
PowerShell Logon Script Example
Setting up a Logon Script through GPOs
Logon Script and PowerShell ADAssist Tool

This login script creates two log files; one log file is created in a user’s Home Directory (i.e. using Windows Environment variable HOMESHARE) and the second log file is created on the user’s computer’s C drive (i.e. in a folder C:\scripts).

To produce these log files, this script uses two functions, as follows:

function Write-LogonInfoToHomeDrive
{
	param
	(
		$homeDrive
	)   
    [string]$LoginDateTime = Get-Date -Format "MM:dd:yyyy HH:mm:ss"
    # Writing login info to the "LoginScriptInfo.log" file located in user's Home Directory
	"$($env:COMPUTERNAME),$($env:USERNAME),$($LoginDateTime)" | 
    Out-File -FilePath "$($homeDrive)\$($userHomeFolderLogInName)" -Force -ErrorAction SilentlyContinue
}

function Write-LogonInfoToComputerDrive
{
	param
	(
		[hashtable]$MapNetworkDriveSuccess,
		[hashtable]$MapNetworkDriveFailure,
		[array]$LogInError = $null
	)
	
    [string]$LoginDateTime = Get-Date -Format "MM:dd:yyyy HH:mm:ss"
	$errCreateDir = $false

	if (!(Test-Path -Path "$($localLogInFolder)")){
		try{
			New-Item -Path "$($localLogInFolder)" -ItemType Directory -ErrorAction Stop | Out-Null
		}
		catch{
			$errCreateDir = $true
		}
	}
	else{
		Remove-Item -Path $userLogInfo -Force -ErrorAction SilentlyContinue
	}
	
	if (-not $errCreateDir){

		[array]$Info = @()
		
		$Info = 'Logon info:'
		$Info += ''
		$Info += "LOGONSERVER  = $($env:LOGONSERVER.TrimStart('\\'))"
		$Info += "LOGONTIME    = $LoginDateTime"
		$Info += "USERNAME     = $env:USERNAME"
		$Info += "COMPUTERNAME = $env:COMPUTERNAME"
        $Info += ''
		
		if ($MapNetworkDriveSuccess.Count -ge 1){
			$Info += 'The following network drives were mapped successfully:'
			$Info += ($MapNetworkDriveSuccess.GetEnumerator() | Sort-Object -Property Name)
			$Info += ''
		}
		
		if ($MapNetworkDriveFailure.Count -ge 1){
			$Info += 'The following network drives were NOT mapped successfully:'
			$Info += ($MapNetworkDriveFailure.GetEnumerator() | Sort-Object -Property Name)
			$Info += ''
		}
		
		if ($LogInError){
			$Info += $LogInError
			$Info += ''
		}
        $Info | Out-File -FilePath $userLogInfo -Force -ErrorAction SilentlyContinue
	}
}

The result of these functions are the following log files:

Figure 1: two log files produced by the PowerShell script

The above mentioned (PowerShell) scripts could be found in the download section (PowerShell | Logon Script folder) of this site.

Please note: Although the author has made every reasonable attempt to achieve complete accuracy of the content, he assumes no responsibility for errors or omissions. Also, you should use this information as you see fit, and at your own risk.

PowerShell Logoff scripts – collect computers’ hardware info

$
0
0

In one of my first posts about PowerShell Logon / Logoff scripts I’ve encouraged you to implement a script that could help you tackle tracking Logon / Logoff activity in your Active Directory environment.

Please note that this blog series has other parts that might be of interest to you:
Logon Script is here to stay …
PowerShell Logon Script Example
Setting up a Logon Script through GPOs
Logon Script and PowerShell ADAssist Tool

In the example provided in this post, the attention is given to a PowerShell logout script and its creation of two monthly rolling log files; one rolling log file contains information about users’ logout activity, and the second rolling log file contains information about computer’s hardware. The figure 1 displays two log files; please notice the Year-Month-Day format present in the naming convention of these files.

Figure 1: Two monthly rolling log files

The following hardware/software info is collected in a CSV file:

To collect information shown in the above table, this script uses two functions to create a PSCustomObject, and then outputs the findings to a monthly rolling CSV file.

The following is the example of a PSCustomObject:

$OsHardware = [PSCustomObject]@{
    # Date     = Get-Date
    Name = $ComputerName
    PcManufacturer = $compSystem.Manufacturer
    PcModel = $compSystem.Model
    SystemType = $compSystem.SystemType
    UserName = $compSystem.UserName
    ReleaseDate =  $BIOS.ReleaseDate
    SerialNumber = $BIOS.SerialNumber
    SMBIOSBIOSVersion = $BIOS.SMBIOSBIOSVersion
    BIOSManufacturer = $BIOS.Manufacturer
    Version = $BIOS.Version 
    Chassi = ConvertTo-ChassisType -Type $Chassi.ChassisTypes
	Processor =  $CPUInfo.Name
	ProcessorModel =  $CPUInfo.Description
	ProcessorManufacturer =  $CPUInfo.Manufacturer
	PhysicalCores =  $CPUInfo.NumberOfCores
	CPU_L2CacheSize =  $CPUInfo.L2CacheSize
	CPU_L3CacheSize =  $CPUInfo.L3CacheSize
	Sockets =  $CPUInfo.SocketDesignation
	LogicalCores =  $CPUInfo.NumberOfLogicalProcessors
	OS_Name =  $OSInfo.Caption
	OS_Version =  $OSInfo.Version
	DataExecutionPrevention =  $OSInfo.DataExecutionPrevention_Available
	TotalPhysical_Memory_GB =  $PhysicalMemory
	TotalVirtual_Memory_MB =  $OSTotalVirtualMemory
	TotalVisable_Memory_MB =  $OSTotalVisibleMemory
    VideoName = $ComputerVideoCard.Name
    VideoVendor = $ComputerVideoCard.AdapterCompatibility
    VideoMode = $ComputerVideoCard.VideoModeDescription
} 
$OsHardware | Export-Csv 

The above mentioned (PowerShell) scripts could be found in the download section (PowerShell | Logon Script folder) of this site.

Please note: Although the author has made every reasonable attempt to achieve complete accuracy of the content, he assumes no responsibility for errors or omissions. Also, you should use this information as you see fit, and at your own risk.

Use PowerShell to send a text message (SMS)

$
0
0

This post is about sending a text message (SMS) to a cellular subscribers from an e-mail application (Microsoft Outlook) using a PowerShell script. An e-mail message will be converted into a text message, and therefore the recipients will receive a text message instead of an e-mail.

In order to be successful and send a text message from an e-mail application, a PowerShell script needs to have the following components (see table below for more info):

⦁ The recipient’s cellular number;
⦁ The name of the recipient’s cellular provider (Bell, Rogers, TELUS, etc.);
⦁ The provider’s extension

The table lists the e-mail address to be used depending on the recipient’s cellular provider

For example, we can use the following PowerShell function to send a text message to someone with a mobile number 902-444-6789 and Rogers as a cellular provider.

Function Send-SMS($cell){
    $SMSproerties= @{
    to = $cell
    from = "Alex.Computer.Bubble@Test.com"
    body = "Have a great day."
    subject = "PowerShell Text (SMS) Message"
    smtpServer = "MyServerName"
    }
    Send-MailMessage @SMSproerties
} 
Send-SMS -cell "9024446789@pcs.rogers.com" 

In addition, we could utilize a CSV file that encompasses all our mobile subscribers and their providers (an example is shown in figure 1) and have a PowerShell function to loop through this file and send the text messages either to all of them or just the ones we filter out.

Figure 1: An example of a CSV File with the mobile subscribers.

Here, the PowerShell script is used to loop through a CSV file and send a text message to a recipient filtered by Where-Object cmdlet.

$Script:SMSmessageFrom = "Alex.Computer.Bubble@Test.com"
$Script:SMSmessageBody = "Have a great day."
$Script:SMSmessageSubject = "PowerShell Text (SMS) Message"
$Script:SmtpServer = "MyServerName"

Function Send-SMS($cell){
    $SMSproerties= @{
        to = $cellPhoneArray
        from = $Script:SMSmessageFrom
        body = $Script:SMSmessageBody
        subject = $Script:SMSmessageSubject
        smtpServer = $Script:SmtpServer
    }
    Send-MailMessage @SMSproerties
}

Import-Csv -Path C:\PSscripts\SMS.csv | 
Select-Object -Property FirstName, LastName, RecipientNumber, ProviderExtension |
Where-Object -FilterScript {$_.FirstName -eq "John" -and $_.LastName -eq "Smith"} |
ForEach-Object {
    $cellNumber = $($_.RecipientNumber) -replace "-", ""
    $sendSMS = "$($cellNumber)$($_.ProviderExtension)"
    Send-SMS -cell $sendSMS
} 

The list of US providers:
⦁ 3 River Wireless: @sms.3rivers.net
⦁ Alltel: @message.alltel.com
⦁ AT&T: @txt.att.net
⦁ ACS Wireless: @paging.acswireless.com
⦁ Blue Sky Frog: @blueskyfrog.com
⦁ Bluegrass Cellular: @sms.bluecell.com
⦁ Boost Mobile: @myboostmobile.com
⦁ BPL Mobile: @bplmobile.com
⦁ Carolina West Wireless: @cwwsms.com
⦁ Cellular One: @mobile.celloneusa.com
⦁ Cellular South: @csouth1.com
⦁ Centennial Wireless: @cwemail.com
⦁ CenturyTel: @messaging.centurytel.net
⦁ Cingular: @txt.att.net
⦁ Clearnet: @msg.clearnet.com
⦁ Comcast: @comcastpcs.textmsg.com
⦁ Corr Wireless Communications: @corrwireless.net
⦁ Dobson: @mobile.dobson.net
⦁ Edge Wireless: @sms.edgewireless.com
⦁ Golden Telecom: @sms.goldentele.com
⦁ Helio: @messaging.sprintpcs.com
⦁ Houston Cellular: @text.houstoncellular.net
⦁ Illinois Valley Cellular: @ivctext.com
⦁ Inland Cellular Telephone: @inlandlink.com
⦁ Idea Cellular: @ideacellular.net
⦁ MCI: @pagemci.com
⦁ Metrocall: @page.metrocall.com
⦁ Metrocall 2-way: @my2way.com
⦁ Metro PCS: @mymetropcs.com
⦁ Microcell: @fido.ca
⦁ Midwest Wireless: @clearlydigital.com
⦁ Mobilcomm: @mobilecomm.net
⦁ MTS: @text.mtsmobility.com
⦁ Nextel: @messaging.nextel.com
⦁ OnlineBeep: @onlinebeep.net
⦁ Public Service Cellular: @sms.pscel.com
⦁ PCS One: @pcsone.net
⦁ Qwest: @qwestmp.com
⦁ Rogers AT&T Wireless: @pcs.rogers.com
⦁ Satellink .pageme@satellink.net
⦁ Southwestern Bell: @email.swbw.com
⦁ Sprint: @messaging.sprintpcs.com
⦁ Sumcom: @tms.suncom.com
⦁ Surewest Communicaitons: @mobile.surewest.com
⦁ Sumcom: @tms.suncom.com
⦁ Sprint: @messaging.sprintpcs.com
⦁ Surewest Communicaitons: @mobile.surewest.com
⦁ T-Mobile: @tmomail.net
⦁ Tracfone: @txt.att.net
⦁ Triton: @tms.suncom.com
⦁ Unicel: @utext.com
⦁ US Cellular: @email.uscc.net
⦁ US West: @uswestdatamail.com
⦁ Virgin Mobile: @vmobl.com
⦁ Virgin Mobile Canada: @vmobile.ca
⦁ Verizon: @vtext.com
⦁ Western Wireless: @cellularonewest.com
⦁ West Central Wireless: @sms.wcc.net

Note: the Synametrics Technologies has site that aims to be the most complete and up to date list of email addresses that can be used to send text messages to phones.
https://web.synametrics.com/email2sms.htm

The PowerShell scripts (Send-SMSMessageToPhone.ps1) and a CSV file could be found in the download section of this site.

The post Use PowerShell to send a text message (SMS) first appeared on Alex Computer Bubble Blog.

Use PowerShell to Troubleshoot Client DNS

$
0
0

Recently at the client’s site we had to replace a server with ailing hardware; this server housed thousands of users’ home folders and a problem was snowballing into a crisis very rapidly. The biggest concern was a rising number of client machines experiencing connectivity issues, and slow start-up. The ‘Folder Redirection’ policy pushed through GPOs was not working. The issues was twofold: the computers’ local registry entries associated with ‘Folder Redirection’ failed to be updated and therefore still populated with values linked to the ailing server; the client computers’ with filed registry settings kept flooding network with valid DNS requests.

Use PowerShell to check the DNS cache

I wanted to view the DNS cache on the clients’ computers, and the well-known command: ipconfig /DisplayDNS has delivered the information I need, but only offered it as text on the console of a local computer.
I needed the DNS cache info from numerous remote computers; therefore my best choice was to run the PowerShell Get-DNSClientCache cmdlet on a remote computer.

Here is an example of a function created to use a session object as output of a New-CimSession or Get-CimSession cmdlet.

# Collection of computer names; could be an array, TXT or CSV file
$pcArray = @("ACB-HFX-WP76543","ACB-DAR-LP12345","ACB-BED-WP12123")

# Path to the CSV File
$CsvFilePath = "C:\DNS\DNScach.csv"

Function Get-ComputerDNSClientCache{
    
    Param(
    [Parameter(Mandatory=$True, Position=0)] 
    [String]$computer,
    [Parameter(Mandatory=$False, Position=1)]
    [String]$FilterEntry
    )
 
 $result = @()

 Function ConvertTo-DNSRecordType($Type) {
    switch ($Type)
	{
	 1    {"A"}
	 2    {"NS"}
	 5    {"CNAME"}
	 6    {"SOA"}
	 12   {"PTR"}
	 15   {"MX"}
	 28   {"AAAA"}
	 33   {"SRV"}
	}
 }
 
 Function ConvertTo-DNSRecordStatus($value) {
    switch ($value)
	{
	 0    {"Success"}
	 9003 {"NotExist"}
	 9701 {"NoRecords"}
	}
 }
   
  $cimSession = New-CimSessionOption -Protocol Dcom
  New-CimSession -ComputerName $computer -SessionOption $cimSession | Out-Null
  $session = Get-CimSession

  if ($FilterEntry){
    $DnsCache = (Get-DnsClientCache -CimSession $session |
    Select-Object -Property PSComputerName,Entry,Type,TimeToLive,Status,Data |
    Where-Object -FilterScript {$_.Entry -like "$($FilterEntry)"})
  }
  Else{
    $DnsCache = (Get-DnsClientCache -CimSession $session |
    Select-Object -Property PSComputerName,Entry,Type,TimeToLive,Status,Data)
  }

     $DnsCache | ForEach-Object{
     $result += [PSCustomObject]@{
     'ComputerName' = $_.PScomputerName
     'Entry' = $_.Entry
     'Type' = ConvertTo-DNSRecordType -type $_.Type
     'TTL' = $_.TimeToLive
     'Status' = ConvertTo-DNSRecordStatus -value $_.Status
     'Data' = $_.Data
     }
    }

$result | Format-Table -AutoSize
$result | Export-Csv -Path "$($CsvFilePath)" -Append -NoTypeInformation
 Get-CimSession | Remove-CimSession
}

foreach ($pc In $pcArray){
    Get-ComputerDNSClientCache -computer $pc -FilterEntry "Something.Entity.com" 
}

In the above function I use an array to group a few remote computers. In addition to an array, I could use a text file or a CSV file and retrieve and store hundreds of DNS cache entries. Running this script without the second ‘FilterEntry’ parameter, returns numerous records from clients’ DNS cache, as shown here in figure 1 as an example.

Figure 1: CSV file contains numerous records returned from remote computers.

To remove the numerous entries from DNS cache and retrieve just the ones I want to know about, I provided an additional ‘FilterEntry’ parameter to filter out the result (see the figure 2).

Get-ComputerDNSClientCache -computer $pc -FilterEntry "www.google.com"
Figure 2: List of remote computers with specified entry

The above mentioned (PowerShell) function could be found in the download section (PowerShell \ Get-ComputerDNSClientCashe) of this site.

Please note: Although the author has made every reasonable attempt to achieve complete accuracy of the content, he assumes no responsibility for errors or omissions. Also, you should use this information as you see fit, and at your own risk.

The post Use PowerShell to Troubleshoot Client DNS first appeared on Alex Computer Bubble Blog.

PowerShell – Redirecting Special Folders with login script

$
0
0

Redirecting Special Folders

In many large organizations, where business-computing setting includes both the Active Directory and Group Policy, the Folder Redirection is frequently used to redirect the path of local folders to a shared network location. The Group Policy is the most recommended and used method for redirecting folders. The My Documents, Favorites, and Links folders are examples of the special folders that can be redirected. Table 1 shows these special folders and their description.

Table 1: list of special folders that can be redirected.

Folder Redirection Registry Keys

With a server migration (copying or moving data from one server to another) comes challenges associated with folder redirection of user’s home folders. During migration of users’ home directories from one shared location to another, some members experience a variety of problems, such as accessing their redirected Documents folders, problems with Favorites and Links, as well as non-responsive computers during the login process.

In Window environment, some of these problems can be mitigated by PowerShell scripts modifying registry values of those registry entries that are associated with Folder Redirection, such as the following:

⦁ HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders\
⦁ HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders\

It is important to note that Folder Redirection extension does not own the User Shell Folder keys, but simply manipulates them.
In addition, please note that the same entries appear in both the Shell Folders subkey and the User Shell Folders. The entries that appear in user User Shell Folders take precedence over those in Shell Folders subkey. And the entries that appear in HKEY_CURRENT_USER take precedence over those in HKEY_LOCAL_MACHINE.

Once you change the location of the folders in User Shell Folders, Windows automatically updates values in Shell Folders subkey. Therefore we can use a login script to change the values in the User Shell Folders subkey only.

Here is an example of a PowerShell script that you could utilize to alleviate some of Folder Redirection issues; it uses a user account group membership to modify the values in the User Shell Folders subkey and redirects the Documents, Favorites, and Links folders. The name of the security group in this example is ‘Group-FolderRedirection’.

# The script uses a user's group membership 
$memberOf = ([ADSISEARCHER]"samaccountname=$($env:USERNAME)").Findone().Properties.memberof -replace '^CN=([^,]+).+$','$1'

foreach ($groupItem in $memberOf){
 switch ($groupItem){

 "Group-FolderRedirection" {
            
    If(Get-ItemProperty -Path "Registry::HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders"){
      Set-ItemProperty -Path "Registry::HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders" `
        -Name "Favorites" -Value "$($env:HOMESHARE)\Favorites" -Type ExpandString -Force
      Set-ItemProperty -Path "Registry::HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders" `
        -Name "Personal" -Value "$($env:HOMESHARE)\Documents" -Type ExpandString -Force
      Set-ItemProperty -Path "Registry::HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders" `
        -Name "{F42EE2D3-909F-4907-8871-4C22FC0BF756}" -Value "$($env:HOMESHARE)\Documents" -Type ExpandString -Force
      Set-ItemProperty -Path "Registry::HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders" `
        -Name "{BFB9D5E0-C6A9-404C-B2B2-AE6DB6AF4968}" -Value "$($env:HOMESHARE)\Links" -Type ExpandString -Force
    }
  }
} 

This is just a sample code of the PowerShell script; it could be found in the download section (PowerShell | Logon Script folder) of this site.

These are the requirements to be met to successfully implement the above PowerShell script:
⦁ Your business-computing setting includes both the Active Directory and Group Policy.
⦁ With the Group Policy Object Editor, you use Folder Redirection to redirect certain special folders to network locations.
⦁ The %HOMESHARE% variable obtain its value from each user’s ‘homeDirectory’ property in Active Directory (see figure 1).
⦁ You implement a login script that performs actions based on users’ group membership.
⦁ A user becomes a member of the designated security group (in my example ‘Group-FolderRedirection’) when needed and after a few successful logins and logoffs could be removed from this designated group membership.

Figure 1: The %HOMESHARE% variable echoes this setting in Active Directory.
The post PowerShell – Redirecting Special Folders with login script first appeared on Alex Computer Bubble Blog.
Viewing all 40 articles
Browse latest View live


Latest Images