by Wojciech Sura

Powershell philosophy

One way to think about Powershell is that it is a shell version of IEnumerable Linq extensions.

For instance, let’s imagine, that we want to know the size of the biggest .wav file in the folder. If we was in C#, the code would probably look like the following:

[csharp]int size = Files.Where(f => f.Name.Contains(".wav")).OrderBy(file => file.Size).Last().Select(f => f.Size);[/csharp]

PowerShell version differs only by the syntax, philosophy is the same:

[shell]PS D:\Temporary> Get-ChildItem | Where-Object {$_.Name -like "*.wav"} | Sort-Object -Property Length | Select-Object -last 1 | Select-Object Length[/shell]

They’re very similar, aren’t they?

by thorhalvor

Executing commands and programs on a remote machine – Part 2

One month a ago my friend Njål blogged about executing commands and programs on a remote machine using powershell.
Njål wrote about PsExec and Invoke-WmiMethod and I will present some more ways to do this. First one is through MSDeploy.exe and the second, my preferred way of doing it, using WinRM, – and then WinRS. I won’t give pros and cons merely inform about the different techniques.

Msdeploy
Msdeploy has two properties that can be set when calling the executable which is called preSync and postSync. MsDeploy is build around a synchronization from source to destination and the commands given in preSync will be executed before the actual synchronization and postSync after. This comes in handy when for example deploying a Windows Service. If the services isn’t stopped before new files are synchronized then you might have seen that a “File(s) in use exception” is thrown.

Example:

 Msdeploy.exe –verb:sync –source: -dest: -preSync: runCommand="net stop " –postSync:runCommand="net start "

When executed this command will first stop the service on the remote machine, then synchronize all the files and then start the service when done. (when actually doing this live you might add waitInterval and waitAttempts since the stopping of the service might take a while).
The value in “runCommand” can contain all sorts of script executing and you can also add “&&” and execute two commands after each other directly.

WinRM
To be able to use WinRM it must be installed and started on the remote computer. Open powershell and run “winrm qc” (qc=quickconfig) and everything should be fixed ready for use. If it’s a test server and you don’t care that much about security you can enable the listener on port 80 (will not interfere with IIS) running this command:

winrm set winrm/config/service @{EnableCompatibilityHttpListener="true"}

You can enable unencrypted messaging setting “allowunencrypted = true“. You can do this almost like the command above or you can browse to the setting and set it specific:

cd wsman:localhostclient
set-item .allowunencrypted $true

The above settings have to be executed on the server. On the client (buildserver when running automated deploys) you might have to enable AllowUnencrypted and also add the server as a TrustedHosts:

winrm s winrm/config/client '@{TrustedHosts="RemoteComputer"}'

Everything should now be set up for running unencrypted remote commands. This can be done through Invoke-Command:

Invoke-Command -Session $ps -ScriptBlock {
   Unzip-File x y
   Copy-Item x y
   …
 }

Within the ScriptBlock you can add all the commands you want and they will all be executed on the remote macine
The $ps variable above is a session which I create and remove like this:

$ps = new-pssession -Computername $computername -Port 80 -credential $cred
Invoke-Command……
Remove-PSSession $ps

If your script got the password in cleartext you can not put it directly into a parameter in the new-pssession command but you can write is as a SecureString to variable and then use this as Credential:

$secureString = ConvertTo-SecureString $password -AsPlainText –Force
 $cred = new-object -typename System.Management.Automation.PSCredential `
 -argumentlist $usernameAndDomain, $secureString

WinRS
I am not sure, but I think WinRS only a wrapper for the Invoke-Command used in the examples above. Atleast it simplifies the whole process. I have actually not used this command I just saw it when “browsing around” and I haven’t botherd changing my current scripts.
But as an example you can write

winrs –r:http://remotemachine <command>

The <command>  will be run on the remote macine if it got WinRM enabled and running.

by Njål

TeamCity: Executing commands and programs on a remote machine using Powershell

Windows Sysinternals has a range of pretty neat tools. My favorite is PsExec – which lets me execute programs on a remote computer. It’s like Remote Desktop – but with the console. Or you could think of it as a Windows alternative of SSH – if you’re a *NIX guy.

It’s perfect when you need to restart a server where Remote Desktop Services is fubar.

 

I can execute

psexec \myWebServer cmd.exe

and I am now running a console window on the webserver.

It is also handy when scripting/automating tasks – such as building, deploying and making backups of software projects.

But invoking PsExec from TeamCity isn’t a walk in the park, to say the least. PsExec does some funky things with the standard input/output, and invoking this from Java (which TeamCity is built on) raises all kinds of problems and stability issues.

Luckily – Powershell takes care of business.

The script below stops an IIS 7 ApplicationPool on a remote server.

[string]$HostName = “myWebServer”
[string]$Cmd = C:WindowsSystem32inetsrvappcmd.exe stop apppool MyMainAppPool”
Invoke-WmiMethod –class Win32_process -name Create -ArgumentList ($Cmd) -ComputerName $HostName

Change $HostName and $Cmd in order to run other commands.

by thorhalvor

Powershell returns exitcode 0 instead of 1

I am currently setting up buildscripts in Teamcity for a website I am working on. I use Ctt.exe to transform my web.config files and this program is invoked through PowerShell. But my problem is that that Ctt.exe in some cases has an error or something else is wrong in my ps1-script but the Teamcity buildstep returns "Process exited with code 0" and the build Succeeds instead of failing.

Since this blogpost might be boring and badly written I will start with the solution:

$ErroActionPreference = ‘Stop’

My scenario is that I have a script, ConfigTransform.ps1, which is invoking the ctt.exe file. This script file takes one parameter which is the Environment (Test/Production). If an error occurs in the script I need the hole build to fail.

To force an error while testing I have set my script to invoke the file "DoesNotExist.exe" instead of the Ctt.exe.

My script (ConfigTransform.ps1)

param($Environment)
Write-Host Environment is $Environment
..DoesNotExist.Exe

 

In Teamcity there are many different alternatives for you to call the script.

Script=File and Executionmode = -FILE

Since I have a predefined powershell script with all the logic and I need to pass a parameter to it, – I first choose the PowerShell-runner with Script=File and Executionmode to –File. As you can see below the parameter is passed on but this process exits with code 0 and therefore the Build won’t fail.

[13:51:05][Step 1/5] Starting: (…)cmd.exe /c (…)powershell.exe -NonInteractive -File (…)
ConfigTransform.ps1 -Environment Test && exit /b %ERRORLEVEL%
[13:51:06][Step 1/5] Environment is Test
[13:51:06][Step 1/5] The term '..DoesNotExist.Exe' is not recognized as the name of a cmdlet, funct
[13:51:06][Step 1/5] ion, script file, or operable program. Check the spelling of the name, or if a
[13:51:06][Step 1/5] path was included, verify that the path is correct and try again.
[13:51:06][Step 1/5] At C:TeamCitybuildAgentwork7100e0a13f6ca7b6BuildToolsAndScriptsScriptsCo
[13:51:06][Step 1/5] nfigTransformConfigTransform.ps1:4 char:20
[13:51:06][Step 1/5] + ..DoesNotExist.Exe <<<<
[13:51:06][Step 1/5]     + CategoryInfo          : ObjectNotFound: (..DoesNotExist.Exe:String) [],
[13:51:06][Step 1/5]     CommandNotFoundException
[13:51:06][Step 1/5]     + FullyQualifiedErrorId : CommandNotFoundException
[13:51:06][Step 1/5]
[13:51:06][Step 1/5] Process exited with code 0

 

Script=File and Executionmode = -Command

Then I try setting the Executionmode to "-Command". The exit code is now correct and the Build is not failing but it is not possible to pass on script parameters.

[13:51:07][Step 1/5] Starting: (…)cmd.exe /c (…)powershell.exe -NonInteractive -Command (…)
ConfigTransform.ps1 && exit /b %ERRORLEVEL%
[13:51:07][Step 1/5] Environment is
[13:51:07][Step 2/5] The term '..DoesNotExist.Exe' is not recognized as the name of a cmdlet, funct
[13:51:07][Step 2/5] ion, script file, or operable program. Check the spelling of the name, or if a
[13:51:07][Step 2/5] path was included, verify that the path is correct and try again.
[13:51:07][Step 2/5] At line:1 char:20
[13:51:07][Step 2/5] + ..DoesNotExist.Exe <<<<
[13:51:07][Step 2/5]     + CategoryInfo          : ObjectNotFound: (..DoesNotExist.Exe:String) [],
[13:51:07][Step 2/5]     CommandNotFoundException
[13:51:07][Step 2/5]     + FullyQualifiedErrorId : CommandNotFoundException
[13:51:07][Step 2/5]
[13:51:07][Step 2/5] Process exited with code 1

 

Script=Source code and Executionmode = -Command

Since executionmode –File does not return correct errorcode I need to use "-Command" but also pass on my parameter. This can be done PowerShell-runner with Script=Source code. In the Script Source field I am now adding the full path to my script including my parameter.

The output is:

[13:51:05][Step 1/5] Starting: (…)cmd.exe /c (…)powershell.exe -NonInteractive -Command (…)
powershell3439435836265.ps1 && exit /b %ERRORLEVEL%
[13:51:06][Step 1/5] Environment is Test
[13:51:06][Step 1/5] The term '..DoesNotExist.Exe' is not recognized as the name of a cmdlet, funct
[13:51:06][Step 1/5] ion, script file, or operable program. Check the spelling of the name, or if a
[13:51:06][Step 1/5] path was included, verify that the path is correct and try again.
[13:51:06][Step 1/5] At C:TeamCitybuildAgentwork7100e0a13f6ca7b6BuildToolsAndScriptsScriptsCo
[13:51:06][Step 1/5] nfigTransformConfigTransform.ps1:4 char:20
[13:51:06][Step 1/5] + ..DoesNotExist.Exe <<<<
[13:51:06][Step 1/5] + CategoryInfo : ObjectNotFound: (..DoesNotExist.Exe:String) [],
[13:51:06][Step 1/5] CommandNotFoundException
[13:51:06][Step 1/5] + FullyQualifiedErrorId : CommandNotFoundException
[13:51:06][Step 1/5]
[13:51:06][Step 1/5] Process exited with code 0

My script is runtime wrapped in a temporary ps1-file "powershell3439435836265.ps1 ", the parameter Environment is Test BUT exit code is now 0 again! So what now…

Solution:

Powershell is by default continuing when "minor" error occurs. I guess the Powershell-runner has done something with that and therefore it works using the –Command switch. I also guess they have forgotten that errorhandling when creating the –File switch. When setting Script=SourceCode all the code is wrapped in a temporary script and you have to take care of the errors yourself. One solution that at first glance worked for me was to wrap my script in a try catch and then just have a ‘throw’ in my errorhandling. What was not so good about this is that the exception itself was suppressed and therefore not visible in the build output.

So the best solution I found is to add the following code (see the first line) in Team City’s Source Code-field:

$ErrorActionPreference = ‘Stop’

(…)/ConfigTransform.ps1 –Enviroenment Test

And then finally everything worked as I wanted. I got exit code 1 in the examples above. Smile

by Njål

Outlook 2010 & Powershell: Importing all email addresses in Sent Items to Autocomplete list

If you have reinstalled Outlook or perhaps set up your email account on a new PC – you’ll be pretty annoyed that the AutoComplete list in Outlook is empty.

This list has nothing to do with your contact list – it’s just a list of people you have sent emails to. Until Outlook 2010 this list was handled locally by Outlook – not on the Exchange/email server. The list used to be a relatively simple .nk2 file.

 

In Outlook 2010 this list is now stored in a separate Address Group called Suggested Contacts.  You’ll find this Group under Contacts in Outlook. Like any other Address Group.

 

 

This is actually a lot better than using the nk2 files – which had the habit of corrupting themselves. Another advantage is that the Autocomplete list in Outlook 2010 should work across different pc’s – given you are using Exchange server (or some other ActiveSync server like Kerio etc.)

Nevertheless – here’s a script that loops through all Emails in SentItems and adds Names & Emails of Recipients to the Suggested Contacts group. This will make your autocomplete work like a charm again. The script can relatively easy be modified to scan through your inbox and add all email addresses there as well.

 

# OUTLOOK AUTOCOMPLETE POWERIMPORT
#
# Author: N. Gjermundshaug - Degree Consulting Group AS - www.degree.no
#
# This script requires Outlook to be installed and configured. It scans through the SentItems folder.
# For each sent email, it loops through each Recipient - and adds the recipient to the "Suggested Contacts" Address Group.
# Contacts in the "Suggested Contacts" will be displayed in the autocomplete field - when composing new emails (like nk2).
# Contacts are only added once from the script.

$outlook = new-object -com outlook.application
$olFolders = "Microsoft.Office.Interop.Outlook.OlDefaultFolders" -as [type]
$namespace = $outlook.GetNameSpace("MAPI")
$sentItems = $namespace.getDefaultFolder($olFolders::olFolderSentMail)
$alreadyAddedEmails = @() #Empty Array
$counter = 0;
$totalItems = $sentItems.items.count;

Write-Host "Scanning through" $totalItems "emails in SentItems"

$contacts = $outlook.Session.GetDefaultFolder($olFolders::olFolderSuggestedContacts)


##############################################################################################################
# FUNCTION - Adds Name/Email to SuggestedContacts - Unless it has already been added before (by this script).
##############################################################################################################
Function AddToSuggestedContactsIfNotAlreadyAdded ($name, $email)
{

    if(($name -eq "") -or ($email -eq "") -or ($name -eq $null) -or ($email -eq $null)){
        return;
    }

    $name = $name.Replace("'", "").Replace("""", "")


    $contactAlreadyAdded = $false

    foreach ($elem in $global:alreadyAddedEmails) {
        if(($elem.ToLower() -eq $email.ToLower())){
            $contactAlreadyAdded = $true
            Write-Host  ($global:counter)"/"($totalItems)  "SKIPPED " $name.PadRight(25," ") "-" $email
            return;
        }
    }

    if(!$contactAlreadyAdded )    {
        $newcontact = $contacts.Items.Add()
        $newcontact.FullName = $name
        $newcontact.Email1Address = $email
        $newcontact.Save()
        $global:alreadyAddedEmails += $email
        Write-Host ($global:counter)"/"($totalItems)  "ADDED   " $name.PadRight(25," ") "-" $email
    }
}

# Loop through all emails in SentItems
$sentItems.Items | % {

    #Loop through each recipient
    $_.Recipients | %{
        AddToSuggestedContactsIfNotAlreadyAdded $_.Name  $_.Address
    }
    $global:counter = $global:counter + 1
}

Write-Host "Done!"
$outlook.Quit()

 

 

Other keywords: Rebuild Cache Restore Entry Post Migration Auto Complete Repair Batch Bulk Auto Completion PS1 Import

by Njål

Permanent VPN connection in Windows

Windows does not really offer any good ways of setting up a permanent VPN connection that is stable.
People are literally loosing their minds over it.

We came up with a pretty nice solution to this problem by combining

  • Windows Powershell
  • Windows VPN Client
  • Windows Task scheduler
At the server side
  • Make sure your VPN Server is set up and working. This will not be covered here.
At the client side
  1.  Set up a new VPN Connection. Just follow the usual “Connecto to a workplace” wizard. Make sure that the VPN connection works.
  2. After creating the connection – set these Redial properties. The redial functionality works as it should most of the time, but it’s not 100% trustable.
  3. Also – Uncheck “Use default gateway on remote network” – in order to prevent all network/internet trafic to go through the VPN (unless this is what you want).
  4. Since the Redial functionality of the VPN Connection isn’t trustable – it’s best to deal with this yourself. It’s hammer powershell time. Create c:pn.ps1and add the following:
    $ip = "10.20.30.40"
    $result = gwmi -query "SELECT * FROM Win32_PingStatus WHERE Address = '$ip'"
    if ($result.StatusCode -eq 0) {
        Write-Host "$ip is up."
    }
    else{
        Write-Host "$ip is down."
        Write-Host "Disconnecting..."
        rasdial.exe DegreeVPN /DISCONNECT
        Write-Host "Connecting..."
        rasdial.exe DegreeVPN vpnUsername vpnPassword12345
    }

    The script pings an IP that should be available through the VPN. If the ping is unsucecssfull – the connection is reestablished. You can also set up routes here (and get notifications by email every time the VPN was disconnected) if you add a few more lines to the script.

  5. Start up a powershell and type “Set-ExecutionPolicy Unrestricted” order to allow the script to be executed.
  6. Schedule this script to execute at startup (no user/login necessary) and also every 5 minutes.

  7. Bingo! You now have a robust and permanent VPN Connection in windows. This works with PPTP, L2TP, SSTP and IKEv2, and have been tested on Windows 7 and Windows 2008 R2.
by Andreas

Sharepoint 2010 – backup og import av lister og libraries

En viktig funksjon i Sharepoint 2010 er muligheten for å eksportere lister og libraries på en enklere måte enn sP 2007. Dette er en bare en kjapp gjennomgang av hvordan du eksporterer en liste du har opprettet og deretter importere den i en annen instans av SP 2010.

Backup / export
Fra Sharepoint Central Administration
-> Backup and restore
-> Granular Backup :: Export a site or list
-> Velg Site (i mitt tilfelle var url kun “/”)
-> Velg liste (søk på navn, eksempel KUNDE), listen burde da vises
-> Gi den et navn, om du ønsker å dumpe alt av brukerinfo osv må du huke av Export full security. Jeg så ikke poenget med det i mitt tilfelle
-> Export All versions
-> Start Export

Dette gir deg to filer, en .CMP og en .EXPORT. CMP er den faktiske eksporten, mens .EXPORT kun er for logging.

Restore

Selv om export av lister kan gjøres via Central Administration må importen gjøres via Windows Powershell.
Start Sharepoint 2010 Management Shell (dette må kjøre under en bruker som har tilgang til SharePoint_Shell_Access, for eksempel administrator. Les mer om dette her )
-> “Import-SPWeb -Identity http://[din sharepointsite, for eksempel localhost] -Path [path til .CMP-filen som skal importeres]
..:: eksempel: Import-SPWeb -Identity http://localhost -Path “C:Sharepoint2010BackupCustomer.cmp”
-> En cmp.import-fil opprettes hvor status kan sjekkes.

Listene burde nå være tilgjengelige i Sharepoint.