Friday Fun: New-PSWindow

When I work in a command prompt, I’m used to start a new window by just typing ‘start’.
Unfortunately, it doesn’t work in a Powershell prompt as start is defined as the alias of the Start-Process cmdlet.

Get-Alias -Name Start

I was about to use the suggested one-liners that you can find on the following page:
http://stackoverflow.com/questions/15667322/how-to-open-powershell-console-window-from-powershell

Invoke-Item C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe            
# or            
start-process powershell

..But it doesn’t meet my needs perfectly.

I wrote for fun the following function that has also some limitations:

  • It’s unable to take in account the MTA (multithreaded apartment) mode vs. STA (single-threaded apartment. It always launches in STA and issues a warning if your parent window was in MTA.
  • It doesn’t use the console file and modules that were previously used that start the parent shell
  • It doesn’t use the arguments that were previously used that start the parent shell
  • Idem for the version of Powershell, the working directory, …

But it has the following advantages:

  • It takes into account whether it’s the ISE or the console, whether it’s a 32bit or a 64bit process
  • It keeps the default colors
  • It’s run under the same account and privileges

In other words, it just starts a new window as if you clicked on its shortcut in the start menu.

#Requires -Version 2.0
Function New-PSWindow {
[cmdletBinding()]
Param()
    Begin {
        
        # Is it a 32bit process
        $is32Bit = ([IntPtr]::size -eq 4)
        
        # Is it the ISE
        $isISE = (Get-Process -id $PID).ProcessName -match '_ise$'

        Switch -Regex ([Environment]::OSVersion.Version) {
        "^6\.1" {
            # Windows 7
            $FilePath = 'C:\Programdata\Microsoft\Windows\Start Menu\Programs\Accessories\Windows PowerShell';
            break;
        }
        "^6\.(2|3)" {
            # Windows 8 and 8.1
            $FilePath = 'C:\ProgramData\Microsoft\Windows\Start Menu\Programs\System Tools'
            break;
        }
            default {
                $FilePath = $false
            }
        } # end of switch
    }
    Process {
        if ($FilePath) {
            # Define a string based on architecture
            if ($is32Bit) {
                $32BitPathStr = ' (x86)'
            } else {
                $32BitPathStr = [string]::Empty
            }
            # Define a string based on the type of host
            if ($isISE) {
                $ISEPathStr = ' ISE'
            } else {
                $ISEPathStr = [string]::Empty
            }

            try {
                # Launch the appropriate shortcut
                $nshl = Join-Path -Path (Resolve-Path -Path $FilePath -ErrorAction Stop) -ChildPath ("Windows Powershell",$ISEPathStr,$32BitPathStr,".lnk" -join "")
                if ($host.Runspace.ApartmentState -ne 'STA') {
                    Write-Warning -Message "The new PS Window is not opened in MTA mode but in STA mode"
                }
                Invoke-Item -Path $nshl -ErrorAction Stop
            } catch {
                Write-Warning -Message "Failed to launch new PS Windows because $($_.Exception.Message)"
            }
        } else {
            $Msg = "Invalid OS: runs only on Windows 7/8/8.1 or 2008R2/2012/2012R2"
            throw (New-Object System.Exception -ArgumentList $Msg)

        }
    }
} # end of function

I propose nps as alias for this function:

Set-Alias -Name nps -Value New-PSWindow

Have fun 😎

Get expiry date of certificate files

I’ve been recently tasked to check when the certificates issued by our CA expire.
All these files are stored in a secure location on a central shared folder and have the same password.

I first tried the Get-PfxCertificate cmdlet. It’s working correctly and prompts for the password.
But it’s somehow limited as one cannot specify the password as an argument. In other words, it isn’t designed for my automation needs 😉

The Get-PfxCertificate cmdlet produces a System.Security.Cryptography.X509Certificates.X509Certificate2 .net object that has many constructors that can be found on the following MSDN page:
X509Certificate2 Class

Constructors can also be listed like this:

$a.GetType().Getconstructors()| % {$_.ToString() }

I chose the following constructor:

X509Certificate2(String, String) Initializes a new instance of the X509Certificate2 class using a certificate file name and a password used to access the certificate.

Here’s how I inventoried all certificates expiry date and exported the result to a CSV file:

# Get the list of all files
$allcertificates =  Get-ChildItem -Path \\server\share\path-of-certificates-folder -Include *.p12 -Recurse

# Create an array with the filename and its expiry date
$results = foreach ($cert in $allcertificates) {

    # Prepare a hashtable
    $HT = @{
        TypeName = 'System.Security.Cryptography.X509Certificates.X509Certificate2';
        ArgumentList = @($cert.FullName,"MyVeryComplexClearTextPassword");
    }
    # Output
	New-Object -TypeName psobject -Property @{
	    FileName = $cert.Name ;
	    ExpiryDate =  (New-Object @HT).NotAfter;
	}
}

# See what certificates expire in the next two years
$results | ? { $_.ExpiryDate -lt (Get-Date).AddMonths(24)  }

# Export sorted results to CSV
$results | Sort ExpiryDate | Export-Csv -Path .\inventory.2013.11.26.csv

Get the local IP Address(es)

Last year there was a brain teaser organised by Powershell Magazine.
The challenge consisted in finding a list of all IP addresses assigned to the local system

How many ways exist to retrieve the IP Addresses ?

Well it depends on the version of Powershell that you use and on what system you are:

If you’re on Powershell Version 3 on Windows 8 or version 4.0 on Windows 8.1, you can use the new CIM cmdlets:

(Get-NetIPConfiguration).IPv4Address

or

(Get-NetIPAddress).IPAddress

or by using the alias of Get-NetIPConfiguration

(gip).IPv4Address

There are also more “traditional” ways of getting this info by using

  • WMI
  • (Get-WmiObject -Class Win32_NetworkAdapterConfiguration -Filter 'ipenabled = "true"').IPAddress

    …shortened to in V3:

    (gwmi Win32_NetworkAdapterConfiguration|? ipe*).IPAddress

    …and shortened to in V2:

    (gwmi Win32_NetworkAdapterConfiguration|?{$_.ipenabled}).IPAddress
  • .Net
  • [net.dns]::GetHostAddresses("") | Select -ExpandProperty IPAddressToString

    …shortened to:

    [net.dns]::GetHostAddresses("")|Select -Expa IP*

    or

    [Net.NetworkInformation.NetworkInterface]::GetAllNetworkInterfaces().GetIPProperties().UnicastAddresses|%{$_.Address}
  • the Test-Connection cmdlet
  • Test-Connection -ComputerName ::1 -Count 1 |             
    Select-Object -Property IPv*Address            
    

    …shortened to

    Test-Connection ::1 -Cou 1|select I*
  • the registry
  • Get-ChildItem -Path HKLM:\system\CurrentControlSet\services\Tcpip\Parameters\Interfaces|             
    ForEach-Object {            
            Get-ItemProperty $_.PSPath -Name *IPAddress -ErrorAction SilentlyContinue            
    } | Select-Object -Property *IPAddress

    …shortened to:

    $key = "HKLM:\system\CurrentControlSet\services\Tcpip\Parameters\Interfaces"            
    gci $key|%{gp $_.PSPath -N *IPAdd* -EA 0}|select *IP*
  • regular expressions and local executables like
    • ipconfig.exe
    • foreach($ip in (ipconfig) -like '*IPv4*') {            
          ($ip -split ' : ')[-1]            
      }

      …shortened to

      (ipconfig)-like'*IPv4*'|%{($_-split': ')[-1]}
    • netsh.exe
    • (netsh interface ipv4 show addresses) -match             
      'Address' -replace '.+:\s+(\S+)', '$1'

      …shortened to

      (netsh i ip sh ad)-match'Address'-replace'.+:\s+(\S+)','$1'
    • systeminfo.exe
    • systeminfo|%{@(([regex]"\s{33}\[\d{2}\]:\s(?<I>.*)").Matches($_).Groups)[-1].Value}
      

      NB: This one is more unsual than traditional, I’ve used it in the Scripting Games 2012 as far as I remember and proposed it for fun in the brain teaser 😉