Blog view of all posts

Export a list over all disabled user accounts in Active Directory to CSV

This will export data in the following order (with scandinavian letters)
Givenname, Surname, SamAccountname, PrimarySMTPAddress

Get-QADUser -sizelimit 0 | where {$_.accountisdisabled -eq $true} | select givenname,sn,SamAccountName,PrimarySMTPAddress | Export-Csv -Encoding utf8 c:\temp\disabled_users.csv


Delete all empty subdirectories

(Get-ChildItem -recurse | Where-Object {$_.PSIsContainer -eq $True}) |
Where-Object {$_.GetFiles().Count -eq 0} | remove-item

UPDATE:
Chad Rexin has corrected this code:
(Get-ChildItem -recurse | Where-Object {$_.PSIsContainer -eq $True}) |
Where-Object {$_.GetFiles().Count -eq 0 -AND $_.GetDirectories().Count -eq 0} |
ForEach-Object {Write-Host “Deleting this empty directory $($_.FullName)”;
remove-item $_.FullName}

See below for discussion.


Import a list of users and export a list with more fields

# Make utf8 to include special characters
cat .\list.csv > .\list2.csv

# Import the file and process export as utf8
Import-Csv .\list2.csv | foreach {
Get-QADUser -lastname $_.lastname -firstname $_.firstname | select firstname,lastname,mobile,primarysmtpaddress,logonname
} | Export-Csv .\list3.csv -Encoding “UTF8″


Clean up user accounts in one OU after linked-mailbox migration to new domain

This script uses the Quest AD Cmdlets that can be downloaded free from Quest.

# Add the Quest commandlets if not added 
if(!(Get-PSSnapin | 
    Where-Object {$_.name -eq "quest.activeroles.admanagement"})) {
      ADD-PSSnapin Quest.Activeroles.ADManagement
    }
 
# Add Exchange 2010 commandlets (if not added)
if(!(Get-PSSnapin | 
    Where-Object {$_.name -eq "Microsoft.Exchange.Management.PowerShell.E2010"})) {
      ADD-PSSnapin Microsoft.Exchange.Management.PowerShell.E2010
    }
 
################## SETTINGS
# Home directory for users
$homedir = "\\contoso.com\users\"
 
# Domain
$domain = "contoso.com"
 
# Email address to keep
$keepmail = "@contoso.com"
 
# The OU we are working on
$OU = "contoso.com/Users/migrated_users"
##################
 
# Run on all users in the defined OU
Get-QADUser -SearchRoot $OU | 
foreach {
    echo "-------------------------------------------------"    
    echo "Working on $($_.displayname)"
    echo "-------------------------------------------------"
 
    # Generate username after the 3+3 rule
    $userprincipalname = ($_.firstname.substring(0,3) + $_.lastname.substring(0,3)).tolower()
    $userprincipalname = $userprincipalname.replace("ø","o")
    $userprincipalname = $userprincipalname.replace("å","a")
    $userprincipalname = $userprincipalname.replace("æ","e")
 
    # Make the changes on the user account
    Set-QADUser -Identity $_ -UserPrincipalName $($userprincipalname + "@" + $domain) -SamAccountName "$($userprincipalname)" -HomeDirectory $($homedir + $userprincipalname) -HomeDrive "H:"  #-whatif
 
    # Check to see if the users homedirectory exists
    if ( !(Test-Path -Path "$homedir\$userprincipalname" -PathType Container) ) {
 
         # Doesn't exist so create it.
         Write-Host "home directory doesn't exist. Creating home directory."
 
         # Create the directory
         New-Item -path $homedir -Name $userprincipalname -ItemType Directory
         $userDir = "$homedir\$userprincipalname"
 
         # Modify  Permissions on homedir
         $Rights= [System.Security.AccessControl.FileSystemRights]::Read -bor [System.Security.AccessControl.FileSystemRights]::Write -bor [System.Security.AccessControl.FileSystemRights]::Modify -bor [System.Security.AccessControl.FileSystemRights]::FullControl
         $Inherit=[System.Security.AccessControl.InheritanceFlags]::ContainerInherit -bor [System.Security.AccessControl.InheritanceFlags]::ObjectInherit
         $Propogation=[System.Security.AccessControl.PropagationFlags]::None
         $Access=[System.Security.AccessControl.AccessControlType]::Allow
         $AccessRule = new-object System.Security.AccessControl.FileSystemAccessRule("$userprincipalname",$Rights,$Inherit,$Propogation,$Access)
         $ACL = Get-Acl $userDir
         $ACL.AddAccessRule($AccessRule)
         $Account = new-object system.security.principal.ntaccount($userprincipalname)
         $ACL.setowner($Account)
         $ACL.SetAccessRule($AccessRule)
         Set-Acl $userDir $ACL
    }
 
    # We need some sleep...
    start-sleep -sec 20
 
    # Now we need to clean up the users Exchange account
    Get-Mailbox -Identity $userprincipalname |
 
    # Loop through all the emailaddresses
    foreach { 
       $a = $_.emailaddresses
       $b = $_.emailaddresses
 
     # Remove all but $keepmail
       foreach($e in $a) 
           { 
           if ($e.tostring() -notmatch $keepmail ) 
               { $b -= $e; } 
           $_ | Set-mailbox -EmailAddressPolicyEnabled $false -emailaddresses $b -alias $userprincipalname
           }
    }
 
    # We had to remove the emailaddresspolicy to make changes. Let's reactivate it
    Set-mailbox -Identity $userprincipalname -EmailAddressPolicyEnabled $true
}

Delete all email addresses but one on a mailbox

  Get-Mailbox -Identity $userprincipalname |
 
    # Loop through all the emailaddresses
    foreach { 
       $a = $_.emailaddresses
       $b = $_.emailaddresses
 
     # Remove all but $keepmail
       foreach($e in $a) 
           { 
           if ($e.tostring() -notmatch $keepmail ) 
               { $b -= $e; } 
           $_ | Set-mailbox -EmailAddressPolicyEnabled $false -emailaddresses $b -alias $userprincipalname
           }
    }
 
    # We had to remove the emailaddresspolicy to make changes. Let's reactivate it
    Set-mailbox -Identity $userprincipalname -EmailAddressPolicyEnabled $true
}

Easier ForEach/Where-Object in Powershell V3

PowerTip of the Day, from PowerShell.com

In the upcoming PowerShell v3 which you can already download as a Beta version, using Where-Object and ForEach-Object becomes a lot simpler. No longer do you need a script block and code. This, for example, is all you need to find files larger than 1MB:

Get-ChildItem $env:windir | Where-Object Length -gt 1MB

Previously, you would have had to write:

Get-ChildItem $env:windir | Where-Object { $_.Length -gt 1MB }

Creating home directory for user

When setting the -homedirectory switch on a user through Powershell the directory is not created.
Use this code to create the folder and apply the necessary ACLs:

    if ( !(Test-Path -Path "$homedir\$userprincipalname" -PathType Container) ) {
         ## Doesn't exist so create it.
         Write-Host "home directory doesn't exist. Creating home directory."
 
         ## Create the directory
         New-Item -path $homedir -Name $userprincipalname -ItemType Directory
         $userDir = "$homedir\$userprincipalname"
 
         ## Modify  Permissions on homedir
         $Rights= [System.Security.AccessControl.FileSystemRights]::Read -bor [System.Security.AccessControl.FileSystemRights]::Write -bor [System.Security.AccessControl.FileSystemRights]::Modify -bor [System.Security.AccessControl.FileSystemRights]::FullControl
         $Inherit=[System.Security.AccessControl.InheritanceFlags]::ContainerInherit -bor [System.Security.AccessControl.InheritanceFlags]::ObjectInherit
         $Propogation=[System.Security.AccessControl.PropagationFlags]::None
         $Access=[System.Security.AccessControl.AccessControlType]::Allow
         $AccessRule = new-object System.Security.AccessControl.FileSystemAccessRule("$userprincipalname",$Rights,$Inherit,$Propogation,$Access)
         $ACL = Get-Acl $userDir
         $ACL.AddAccessRule($AccessRule)
         $Account = new-object system.security.principal.ntaccount($userprincipalname)
         $ACL.setowner($Account)
         $ACL.SetAccessRule($AccessRule)
         Set-Acl $userDir $ACL
    }

Thank you very much for this tip Shay Levy!


BgPing – A High Performance Bulk Ping Utility


xb90 from Posh Tips has written a gorgeous masterpiece; BgPing.

Description from Posh Tips:
“BgPing.ps1 is a Background Ping Script capable of pinging enormous hosts lists in record time. You could call it a Bulk Ping Script, a Mass Ping Script, a Batch Ping Script or even a Mega Ping Script but the bottom line is: this baby will crank through a lot of IP addresses (or hostnames) very quickly.”


Create mailbox to all users in a CSV based on the 3+3 naming policy

# Create mailbox to all users in a CSV based on the 3+3 naming policy.
# This will also create the user in AD
$parentcontainer = "contoso.com/container"
$homedirectory = "\contoso.com\users\$username"
 
# Prompt the user for password
$password = Read-Host "Enter password" -AsSecureString
 
# The following has to be done to import with european characters
cat "c:\temp\users.csv" > c:\temp\listtemp.csv
 
# Loop through the list and 
# - replace european letters with o,a or e.
# - create username based on the 3+3 naming convention 
#   (three first letters in the firstname and lastname.)
# - all lower case
import-csv c:\temp\listtemp.csv | foreach {
    $username = ($_.firstname.substring(0,3) + $_.lastname.substring(0,3)).tolower()
    $username = $username.replace("ø","o")
    $username = $username.replace("å","a")
    $username = $username.replace("æ","e")
 
# The following line can create the user if you do not need mailbox.
#    new-qadUser -ParentContainer $parentcontainer -FirstName $_.firstname -LastName $_.lastname -DisplayName $($_.FirstName + " " + $_.LastName) -SamAccountName $username -Name $username -UserPrincipalName ($username + '@contoso.com') -whatif
 
# Create the mailbox
    New-Mailbox -Alias $username -Name $($_.FirstName + " " + $_.LastName) -OrganizationalUnit $parentcontainer -UserPrincipalName ($username + '@contoso.com') -SamAccountName $username -FirstName $_.firstname -LastName $_.lastname -ResetPasswordOnNextLogon $false -password $password -whatif
}

Adding snapins/modules to PowerShell

# Import the ActiveDirectory cmdlets
Import-Module ActiveDirectory
 
# List available snapins on your system:
Get-PSSnapin
 
# List registered snapins
Get-PSSnapin -Registered
 
# Alias:
gsnp
 
# Add Snapin:
Add-PSSnapin
 
# Examples:
Add-PSSnapin Microsoft.Exchange.Management.PowerShell.Admin # Exchange 2007
Add-PSSnapin Microsoft.Exchange.Management.PowerShell.E2010 # Exchange 2010
Add-PSSnapin Microsoft.SystemCenter.VirtualMachineManager # WMM (Hyper-V)
Add-PSSnapin Quest.Activeroles.ADManagement # Quest commandlets

(You can download the Quest Commandlets from here.)

You will get an error if you try to add a snapin that is already added. Your script will continue to run but you'll have a bunch of nasty red letters in your shell. Not too sexy, eh? The way to avoid this is to first check if the snapin is loaded and then only load if it is not.
Do it like this:

# Add Exchange 2007 commandlets (if not added)
if(!(Get-PSSnapin | 
    Where-Object {$_.name -eq "Microsoft.Exchange.Management.PowerShell.Admin"})) {
      ADD-PSSnapin Microsoft.Exchange.Management.PowerShell.Admin
    }
 
# Add Exchange 2010 commandlets (if not added)
if(!(Get-PSSnapin | 
    Where-Object {$_.name -eq "Microsoft.Exchange.Management.PowerShell.E2010"})) {
      ADD-PSSnapin Microsoft.Exchange.Management.PowerShell.E2010
    }
 
# Add Virtual Machine Manager (Hyper-V) commandlets (if not added)
if(!(Get-PSSnapin | 
    Where-Object {$_.name -eq "Microsoft.SystemCenter.VirtualMachineManager"})) {
      ADD-PSSnapin Microsoft.SystemCenter.VirtualMachineManager
    }
 
# Add Quest commandlets (if not added)
if(!(Get-PSSnapin | 
    Where-Object {$_.name -eq "Quest.Activeroles.ADManagement"})) {
      ADD-PSSnapin Quest.Activeroles.ADManagement
    }

Head on over to http://blogs.technet.com/b/heyscriptingguy/archive/2010/10/16/learn-how-to-load-and-use-powershell-snap-ins.aspx to learn more about snapins.


Launching remote desktop from the command line

The  remote desktop connection dialog box provides you with everything that you need to configure and connect to another computer capable of RDP. You can use the Save As button on the Connection Settings panel to save all your connection settings as an RDP file. Then, you can launch and connect to a remote computer simply by double-clicking the RDP file.

You can also script a remote desktop connection. The remote desktop connection executable file is MSTSC.EXE, and the following are some of the most common parameters:

  • /v:<computername>–specifies the name of the computer to connect to.
  • /f–starts the connection in a full screen.
  • /w:<width>–specifies the width of the remote desktop screen.
  • /h:<height>–specifies the height of the remote desktop screen.
  • /admin – connects with admin rights
  • /console – connects to the console of a Windows Server 2003 based system

For example, to remotely connect to a computer named Kaltec in a 640 x 480 remote desktop screen, you would use the following command:

mstsc /v: Kaltec /w:640 /h:480

You can type this command line in the Run dialog box, as well as use it in a batch file.
mstsc.exe {ConnectionFile | /v:ServerName[:Port]} [/console] [/f] [/w:Width/h:Height]


Display size of cluster shared volumes

Import-Module FailoverClusters
 
$objs = @()
 
$csvs = Get-ClusterSharedVolume
foreach ( $csv in $csvs )
{
   $csvinfos = $csv | select -Property Name -ExpandProperty SharedVolumeInfo
   foreach ( $csvinfo in $csvinfos )
   {
      $obj = New-Object PSObject -Property @{
         Name        = $csv.Name
         Path        = $csvinfo.FriendlyVolumeName
         Size        = $csvinfo.Partition.Size
         FreeSpace   = $csvinfo.Partition.FreeSpace
         UsedSpace   = $csvinfo.Partition.UsedSpace
         PercentFree = $csvinfo.Partition.PercentFree
      }
      $objs += $obj
   }
}
 
$objs | ft -auto Name,Path,@{ Label = "Size(GB)" ; Expression = { "{0:N2}" -f ($_.Size/1024/1024/1024) } },@{ Label = "FreeSpace(GB)" ; Expression = { "{0:N2}" -f ($_.FreeSpace/1024/1024/1024) } },@{ Label = "UsedSpace(GB)" ; Expression = { "{0:N2}" -f ($_.UsedSpace/1024/1024/1024) } },@{ Label = "PercentFree" ; Expression = { "{0:N2}" -f ($_.PercentFree) } }

WebExplorer

WebExplorer is a Windows Explorer style file manager through your webbrowser, but don’t let the “Windows part” of it scare you away! Just upload the file to your designated “admin” directory on your PHP enabled website, edit the variable $basedir to reflect your website, and off you go!!

This application lets you edit, browse, CHMOD, view, move, rename, copy, and create files/directories in any forms/tables enabled browser. You even have the option to create html skeleton-files.

I haven’t done any work on WebExplorer since 2002 so it can be considered abandonware. But feel free to use.

What does WebExplorer require?
Webserver with PHP3 or newer installed

How about security?
WebExplorer does not have any “built in” authorization function. Use HTAccess or similar for this.

Can you develop blah blah or make WebExplorer do yaddayadda?
Donate first, ask later.

Do you support WebExplorer?
See previous q/a

How many downloads?
Lost track a long time ago… I’d guess around 35 000.

Download
webexplorer20.zip
webexplorer20.tar.gz

Screenshots


Keyboard Tricks for Launching PowerShell

PowerTip of the Day, from PowerShell.com:

If you have pinned PowerShell to your taskbar (on Windows 7: launch PowerShell, right-click its icon in the task bar, choose “Pin this program to taskbar”), then a click on the icon will open PowerShell. Here are three neat tricks:
First, move the pinned PowerShell icon with your mouse to the leftmost position in your taskbar. Yes, icons are movable! Now, press WIN+1. PowerShell will launch or jump into the foreground, just as if you had clicked the PowerShell icon.
Next, press SHIFT+WIN+1 (or hold SHIFT while you click your PowerShell icon). You get a new PowerShell window.
Finally, hold CTRL+SHIFT+WIN+1 (watch your fingers). This time, PowerShell starts elevated (provided Windows uses UAC).
As you may have guessed, the number reflects the icon position, so pressing a “2″ instead of a “1″ applies your keyboard magic to the second icon in your taskbar.


Using PowerShell ISE with Full Privileges

PowerTip of the Day, from PowerShell.com:

Sometimes you need full administrator privileges to debug or test a script in the ISE script editor. By default, ISE starts with restricted privileges (when Windows User Account Control (UAC) is enabled).

To launch ISE with full privileges, right-click the PowerShell icon in your taskbar, and hold CTRL+SHIFT while you click on it. This key shortcut works for anything you click and invokes the UAC elevation dialog.


Implicit Foreach in PSv3

PowerTip of the Day, from PowerShell.com:

PowerShell v3 Beta is available for quite some time now, so every now and then we start tossing in some PowerShell v3 tips. We’ll clearly mark them as such since they only run on PowerShell v3, not v2. This is something new in PowerShell v3:

When you work with array data, in previous PowerShell versions you had to loop through all elements to get to their properties and methods. No more in PowerShell v3. PowerShell now detects that you are working with an array and applies your call to all array elements. So with this line you could gracefully close all running notepad.exe.

(Get-Process notepad).CloseMainWindow()

Previously, you would have had to write:

Get-Process notepad | ForEach-Object { $_.CloseMainWindow() }

Export-CSV with append

function Export-CSV {
[CmdletBinding(DefaultParameterSetName='Delimiter',
  SupportsShouldProcess=$true, ConfirmImpact='Medium')]
param(
 [Parameter(Mandatory=$true, ValueFromPipeline=$true,
           ValueFromPipelineByPropertyName=$true)]
 [System.Management.Automation.PSObject]
 ${InputObject},
 [Parameter(Mandatory=$true, Position=0)]
 [Alias('PSPath')]
 [System.String]
 ${Path},
 
 #region -Append (added by Dmitry Sotnikov)
 [Switch]
 ${Append},
 #endregion 
 [Switch]
 ${Force},
 [Switch]
 ${NoClobber},
 [ValidateSet('Unicode','UTF7','UTF8','ASCII','UTF32',
                  'BigEndianUnicode','Default','OEM')]
 [System.String]
 ${Encoding},
 [Parameter(ParameterSetName='Delimiter', Position=1)]
 [ValidateNotNull()]
 [System.Char]
 ${Delimiter},
 [Parameter(ParameterSetName='UseCulture')]
 [Switch]
 ${UseCulture},
 [Alias('NTI')]
 [Switch]
 ${NoTypeInformation})
begin
{
 # This variable will tell us whether we actually need to append
 # to existing file
 $AppendMode = $false
 
 try {
  $outBuffer = $null
  if ($PSBoundParameters.TryGetValue('OutBuffer', [ref]$outBuffer))
  {
      $PSBoundParameters['OutBuffer'] = 1
  }
  $wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('Export-Csv',
    [System.Management.Automation.CommandTypes]::Cmdlet)
 
 #String variable to become the target command line
 $scriptCmdPipeline = ''
 # Add new parameter handling
 #region Dmitry: Process and remove the Append parameter if it is present
 if ($Append) {
 
  $PSBoundParameters.Remove('Append') | Out-Null
 
  if ($Path) {
   if (Test-Path $Path) {        
    # Need to construct new command line
    $AppendMode = $true
 
    if ($Encoding.Length -eq 0) {
     # ASCII is default encoding for Export-CSV
     $Encoding = 'ASCII'
    }
 
    # For Append we use ConvertTo-CSV instead of Export
    $scriptCmdPipeline += 'ConvertTo-Csv -NoTypeInformation '
 
    # Inherit other CSV convertion parameters
    if ( $UseCulture ) {
     $scriptCmdPipeline += ' -UseCulture '
    }
    if ( $Delimiter ) {
     $scriptCmdPipeline += " -Delimiter '$Delimiter' "
    } 
 
    # Skip the first line (the one with the property names) 
    $scriptCmdPipeline += ' | Foreach-Object {$start=$true}'
    $scriptCmdPipeline += '{if ($start) {$start=$false} else {$_}} '
 
    # Add file output
    $scriptCmdPipeline += " | Out-File -FilePath '$Path'"
    $scriptCmdPipeline += " -Encoding '$Encoding' -Append "
 
    if ($Force) {
     $scriptCmdPipeline += ' -Force'
    }
    if ($NoClobber) {
     $scriptCmdPipeline += ' -NoClobber'
    }   
   }
  }
 } 
 
 $scriptCmd = {&amp; $wrappedCmd @PSBoundParameters }
 
 if ( $AppendMode ) {
  # redefine command line
  $scriptCmd = $ExecutionContext.InvokeCommand.NewScriptBlock(
      $scriptCmdPipeline
    )
 } else {
  # execute Export-CSV as we got it because
  # either -Append is missing or file does not exist
  $scriptCmd = $ExecutionContext.InvokeCommand.NewScriptBlock(
      [string]$scriptCmd
    )
 }
 # standard pipeline initialization
 $steppablePipeline = $scriptCmd.GetSteppablePipeline(
        $myInvocation.CommandOrigin)
 $steppablePipeline.Begin($PSCmdlet)
 
 } catch {
   throw
 }
 
}
process
{
  try {
      $steppablePipeline.Process($_)
  } catch {
      throw
  }
}
end
{
  try {
      $steppablePipeline.End()
  } catch {
      throw
  }
}
 
}