Thursday, November 19, 2009

Messing with output from Format-Table

Found at question at psobject.codeplex.com:

I was wondering if I could write the outout without any blank spaces between 2 fields. For example, I am using hash tables to display 2 columns from dir output. But it always comes up with a blank space between those 2 data elements. I need this to generate a fixed format output with data elements only and no spaces in between. Any help is greatly appreciated.

$column1 = @{expression="mode";width=5;label="mode";alignment="left"}
$column2 = @{expression="name";width=10;label="name";alignment="left"}

$dir |format-table $column1,$column2

$mode  name
----  ----
d---- download
d---- extract
-a--- alias.txt
-a--- Compute...
-a--- execute...
-a--- get_dn.ps1
-a--- hh

Well, it can be done. I looked into the objects the Format-Table spit out and after some poking around, I came up with this -

$column1 = @{expression="mode";width=5;label="mode";alignment="left"}
$column2 = @{expression="name";width=20;label="name";alignment="left"}

# Save widths, all non-fixed length value should specify a width
$widths=@{}
dir | format-table $column1,@{l="|";e={"|"}},$column2 | foreach {
if ($_.pstypenames[0] -eq "Microsoft.PowerShell.Commands.Internal.Format.FormatEntryData") {
# Capture the values and convert them to one value
$value=""
$count=$_.formatentryinfo.formatPropertyFieldList.count
foreach($i in 0..($count-1)) {
$value+=$_.formatentryinfo.formatPropertyFieldList.item($i).propertyvalue.ToString().padright($widths.$i)
}
# Delete all but one field
$_.formatentryinfo.formatPropertyFieldList.removerange(1,$count-1)
# and update its value
$_.formatentryinfo.formatPropertyFieldList.item(0).propertyValue=$value
$_
}
elseif ($_.pstypenames[0] -eq "Microsoft.PowerShell.Commands.Internal.Format.FormatStartData") {
# Capture the headers and convert them to one header
$value=""
$width=0

$count=$_.shapeinfo.tablecolumninfolist.count
foreach($i in 0..($count-1)) {
$w=$_.shapeinfo.tablecolumninfolist.item($i).width
$width+=$w
$widths.$i=$w
$value+=$_.shapeinfo.tablecolumninfolist.item($i).propertyname.ToString().padright($w)
}
# Delete all but one field
$_.shapeinfo.tablecolumninfolist.removerange(1,$count-1)
# and update its value
$_.shapeinfo.tablecolumninfolist.item(0).propertyName=$value
$_.shapeinfo.tablecolumninfolist.item(0).width=$width
$_
}
else
{
$_
}
}







If you like it, convert it to a function as en exercise ;)



 



Happy formatting!

PS Remoting to Home Server

I wanted to PowerShell remote to my home server, but as it is – for the good reason, that it is impossible – not in the domain of my PC, I have to add it to TrustedHosts.

This is my story.

First, I enabled PS remoting on the home server with a simple

Enable-PSRemoting





Next, I attempted to access the server




enter-pssession server -cre server\administrator





By doing so, I received a very long error message.



I tried with –authentication negotiate. This reduced the error message to 10 lines ;) It told me to configure TrustedHosts with Winrm.cmd.



I looked at Winrm.cmd, but that looked very complicated and this is at the end of the day. Luckily, the WSMAN drive popped up in my mind. I switched to my administrative account and did this




PS C:\Users\user> cd wsman:
PS WSMan:\> dir

WSManConfig:

ComputerName Type
------------ ----
localhost Container

PS WSMan:\> cd .\localhost
PS WSMan:\localhost> dir


WSManConfig: Microsoft.WSMan.Management\WSMan::localhost

Name Value Type
---- ----- ----
MaxEnvelopeSizekb 150 System.String
MaxTimeoutms 60000 System.String
MaxBatchItems 32000 System.String
MaxProviderRequests 4294967295 System.String
Client Container
Service Container
Shell Container
Listener Container
Plugin Container
ClientCertificate Container


PS WSMan:\localhost> cd .\Client
PS WSMan:\localhost\Client> dir


WSManConfig: Microsoft.WSMan.Management\WSMan::localhost\Client

Name Value Type
---- ----- ----
NetworkDelayms 5000 System.String
URLPrefix wsman System.String
AllowUnencrypted false System.String
Auth Container
DefaultPorts Container
TrustedHosts System.String


PS WSMan:\localhost\Client> Set-Item .\TrustedHosts server

WinRM Security Configuration.
This command modifies the TrustedHosts list for the WinRM client. The computers in the TrustedHosts list might not be authenticated. The
client might send credential information to these computers. Are you sure that you want to modify this list?
[Y] Yes [N] No [S] Suspend [?] Help (default is "Y"): y
PS WSMan:\localhost\Client>









Back to my standard user




194 WSMan:\localhost> enter-pssession server -cre server\administrator
[server]: PS C:\Documents and Settings\Administrator\My Documents>







and it worked.



 



Happy remoting

Tuesday, November 17, 2009

Laissez-Faire Access Control

Bruce Schneier have an abstract of a paper which claims that enabling users to get the access they need while auditing the access is better than a centrally controlled setup.

Read it for yourself – at least read the abstract.

Monday, November 16, 2009

PowerShell 2.0 *is* supported for Exchange 2007 SP2

In case you wondered, the greatly improved PowerShell 2.0 can be installed on servers running Exchange 2007. But you must be running Service Pack 2.

BTW: You cannot find PowerShell v2 on www.microsoft.com/downloads. You have to go to http://support.microsoft.com/kb/968929.

Thursday, November 12, 2009

Implementing OCS presence in Outlook Live Server (a.k.a. Exchange 2010 OWA)

The tools required to implement OCS presence in Exchange 2010 OWA has been released

Microsoft Office Communications Server 2007 R2 Web Trust Tool

Microsoft Office Communications Server 2007 R2 Web Service Provider

And instead of me showing how to do it, I would recommend you checkout the blog post Implementing integrated OCS in Exchange 2010 from Chris and Robin’s Technology Blog.

One addition I have to their post is that the reference material for the OCS Web Service Provider can be found on the OCS TechNet site here.

Wednesday, November 11, 2009

Picking files with the mouse using Get-DroppedFile

Sometimes (often?) it is just easier picking your files with the mouse. As long as the files are in one folder that is not that hard, but if you have files scattered all around that is tougher. Also right-clicking and copy-as-path is annoying.

To make this easier, I have created a small drop box function. A small transparent window will be shows and when you drop files on it, those files will be send to the output pipeline where you can do the rest of your processing.

I made the forms part using Visual C#. Relativily trivial. And Add-Type enabled me to embed it into my script. The hard part was making it async so that files would appear ín the output pipeline as soon as they were dropped. I had to resort to good old VB5-style DoEvents (just revealed my age, I guess). If you can come up with a non-polling solution, please let me know.

What it can be used for -

  • Testing scripts with different files
  • Move photos to a folder, converting them as they are moved
  • Renaming files
  • Compressing files
  • continue the list yourself

All tasks where you – the human – can make the decision about what to do with a file are relevant.

With PowerShell v2 being available on all platform, do I have to say, that this is a V2-only script?

Please, read the comments in the script for further information.

Get-DroppedFile.ps1


<#
.Synopsis
Create a drop box window and output the files dropped to the pipeline
.Description
Create a drop box window. When files are dropped, they are send to the output pipeline right away.
Stop Get-DroppedFile by closing the window.
.Inputs
None
.Outputs
File names (-asText), IO.DirectoryInfo or IO.FileInfo objects
.Example
Get-DroppedFile | Copy -destination e:\ -passthru | foreach { $x.attributes=$x.Attributes.ToString()+",readonly" }
Copy dropped files to e:\ and set the readonly bit
#>

param(
   [string]
   # The caption of the drop box
   $Caption,
   [switch]
   # Return file names (full path) as text
   $AsText,
   [switch]
   # Recurse directories, I.E. the folder itself is not returned, only its children
   $Recurse,
   [switch]
   # (Internal switch used to detect -sta invocation)
   $_InternalReinvoked)

# Create the script code - direct execution or SingleThreadedApartment is determined later
$script={

$loaded=$false
try {
    $null=[system.type] "Get_DroppedFile.Form1"
    $loaded=$true
}
catch {}

# The form code. Created in Visual C# 2008 Express and slightly adopted
if (!$loaded) {
    add-type -TypeDefinition @'

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;

namespace Get_DroppedFile
{
public partial class Form1 : Form
{
Color defaultColor;
public Form1()
{
InitializeComponent();
defaultColor = this.BackColor;

}

public void avoidWarning()
{
}

private void Form1_DragDrop(object sender, DragEventArgs e)
{
// Back to default color
this.BackColor = defaultColor;
}

private void Form1_DragOver(object sender, DragEventArgs e)
{
// Start dragdrop
e.Effect = DragDropEffects.Copy;
}

private void Form1_DragEnter(object sender, DragEventArgs e)
{
// visual feedback in drag
this.BackColor = Color.FromArgb(defaultColor.ToArgb() - 0x101010);
}

private void Form1_DragLeave(object sender, EventArgs e)
{
// restore color
this.BackColor = defaultColor;
}
}
}

namespace Get_DroppedFile
{
partial class Form1
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;

/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}

#region Windows Form Designer generated code

/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.SuspendLayout();
//
// Form1
//
this.AllowDrop = true;
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.BackColor = System.Drawing.SystemColors.ActiveCaption;
this.ClientSize = new System.Drawing.Size(116, 49);
this.Cursor = System.Windows.Forms.Cursors.Default;
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "Form1";
this.Opacity = 0.75;
this.ShowIcon = false;
this.Text = "Drop Box";
this.TopMost = true;
//this.Load += new System.EventHandler(this.Form1_Load);
this.DragLeave += new System.EventHandler(this.Form1_DragLeave);
this.DragDrop += new System.Windows.Forms.DragEventHandler(this.Form1_DragDrop);
this.DragEnter += new System.Windows.Forms.DragEventHandler(this.Form1_DragEnter);
this.DragOver += new System.Windows.Forms.DragEventHandler(this.Form1_DragOver);
this.ResumeLayout(false);

}

#endregion


}
}

'@
 -verbose -ReferencedAssemblies system.drawing,system.windows.forms
}

# Create our form object
$form=New-object get_droppedfile.form1

# Get rid of any events leftover
get-event get-droppedfile -erroraction silentlycontinue | remove-event

# Add handlers
# The handlers transfer the action/file to the main loop using event
$form.add_dragdrop( { $null=new-event -sourceidentifier get-droppedfile -messagedata $args[1].Data.GetData("FileDrop", $true)  })
$form.add_formclosed( { $null=new-event -sourceidentifier get-droppedfile -messagedata "[close]" })

#Register-ObjectEvent -InputObject $form -EventName dragdrop #-SourceIdentifier blah#
#Register-ObjectEvent -InputObject $form -EventName formclosed #-SourceIdentifier blah

# Custom caption
if ($caption) {$form.text=$caption}

# This is the tricky part. Dropping is quite simple, but feeding the output pipeline with
# the dropped files (so you do not have to wait until the drop box is closed) is not simple.
# I came up with this solution:
# - Do not use ShowDialog as is modal and will suspend PowerShell processing
# - use Show and doevents in a loop
# - this method consumes some CPU, but the Start-Sleep keeps it to a few per cent
# - The Event actions generates events and they are read here in the main loop
# and converted to file names which are sent to the pipeline

# Show the drop box
$form.show()

do {
  $e=get-event get-droppedfile -erroraction silentlycontinue # suppress no such events
$exit=$e.messagedata -eq "[close]" # Test close message
if ($e.messagedata -and !$exit) {$e.MessageData} # Send file to pipeline
if ($e) {$e | remove-event} # Remove event from queue
if ($exit) {break}
start-sleep -m 100 # Wait a little
[System.Windows.Forms.Application]::doevents() # React to form events so the windows can be moved etc.
} while($true)

# Shutdown
$form.close()
} # end of script assignment


# Generate command for -sta recursive call. Done here where $myinvocation has the right value
$command = "&'" + $myinvocation.mycommand.definition + "' -caption '$caption' -_InternalReinvoked"

# Execute the next in a scriptblock so output can be piped
&{

# Forms must run in SingleThreadedApartment style
# Re-invoke PowerShell if necessary
    if ($host.runspace.ApartmentState -ne "sta") {


     write-verbose "Invoking PowerShell with -sta"
        $bytes = [System.Text.Encoding]::Unicode.GetBytes($command)
        $encodedCommand = [Convert]::ToBase64String($bytes)
        powershell.exe -sta -noprofile -encodedCommand $encodedCommand
     #powershell -sta -noprofile $script
    }
    else {
     &$script
    }

} | where {$_} | foreach {
    # Handle the different return options
if ($_InternalReinvoked.ispresent) {
        # -sta call, always return strings
$_
}
elseif ($recurse.ispresent -and (test-path -pathtype container $_)) {
        # Recurse folder tree, return text or objects
Get-ChildItem $_ -recurse -force | foreach {
if ($astext.ispresent) {
$_.fullname
}
else {
$_
}
}
}
elseif ($astext.ispresent) {
$_
}
else {
Get-Item $_
}
}


Have fun

Sunday, November 08, 2009

ATE Schedule for TechEd Berlin

I’m in Copenhagen waiting for my flight to Berlin. If you want to touch base then I have booth duty at the UC Ask-The-Experts (and Dennis ;-) area Tuesday from 15:15 – 18:15 and Friday 11:30 – 14:45.

CU there !

Wednesday, November 04, 2009

Code Contracts and Pex

If your are coding, you should check out this Channel 9 video (11:30 minutes) where Manuel Fähndrich and Peli de Halleux show you how to specify code contracts and testing them using Pex directly in your coding environment.

Here’s a screen shot showing the Contract statements in the code and the Pex test runs below -

image

Happy coding

Tuesday, November 03, 2009

OCS Cumulative Server Update Installer

One of the biggest serviceability issues with OCS 2oo7 R2 hotfixes, has been that you as an admin had a list of 17 (Seventeen – yes) different hotfixes that you had to apply to the correct server/role. This could be done by checking the 13 (Thirteen) different server roles and the list of updates that applied to them and then manually installing each and every of the necessary updates separately.

This “design issue” has now been mitigated with the “Cumulative Server Update Installer”, that basically checks each server, its roles and then suggest and applies the necessary hotfixes to your OCS Server (How hard can it be ;-).

So one installer (and one download, that contains all the hotfixes), that handles all servers/roles and it can be done either through GUI or scripted through Command Line.

You can download the installer from KB968802 that contains a list of all the updates (October Patches), a description of the process for updating and a download link.

At the download site you can download each update, but just scroll down to the download called “ServerUpdateInstaller.exe” and only download this. Do note that you should execute it from an empty folder as it extracts all the necessary updates for the relevant server role on execution (After installation has been completed it removes updates and only logs from each applied update is left).

Below is an example from ServerUpdateInstaller executed on a mediation server:

ServerUpdateInstaller.exe

As you can see it both lists existing installed version, the new updated version and not least a link to the hotfix KB.

Do note that the July Database Update found in KB969834 isn’t installed automagically, this update has to be run manually!

Good work Microsoft (And happy updating to all of you ;-)

Monday, November 02, 2009

PowerShell V2 now Available on Older Operating Systems

Now you can safely forget V1 and switch to the vastly superior V2. As of last week it is now available on -

  • Windows Server 2008 with Service Pack 2
  • Windows Server 2003 with Service Pack 2
  • Windows Vista with Service Pack 2
  • Windows Vista with Service Pack 1
  • Windows XP with Service Pack 3
  • In Windows 7 and WS08 R2 it is part of the package.

    PS2 is part of Windows Management Framework which also includes WinRM and BITS4.

    BTW: You need to install PowerShell 2 on server core. Read how in KB 976736.

    Windows Server 2008 R2 Service and Virtual Accounts

    One of the best reasons for upgrading to R2, is the new account types for managing services. Changing user account passwords being used for running services, scheduled tasks and application pools are often a real pain and consequently, often being skipped. And wouldn’t it be nice if it was handled automatically like a computer account? Well, that is exactly what R2 offers.

    Two new types of service accounts are available in Windows Server® 2008 R2 and Windows® 7—the managed service account and the virtual account. The managed service account is designed to provide crucial applications such as SQL Server and IIS with the isolation of their own domain accounts, while eliminating the need for an administrator to manually administer the service principal name (SPN) and credentials for these accounts. Virtual accounts in Windows Server 2008 R2 and Windows 7 are "managed local accounts" that can use a computer's credentials to access network resources.

    Read the Service Accounts Step-by-Step Guide for more information.

    DFS, IPv6 and – sort of – disabling it

    Ask the Directory Services Team has a good article on troubleshooting DFS links (DFS Referrals and IPv6: Outta site!) as well as a discussion of how not to  disable IPv6 (unbinding it from an adapter) and how to do it correctly (KB929852).

    In case you really need to disable IPv6, consider using a Group Policy Preference or automate it with PowerShell -

    Set-ItemProperty HKLM:\SYSTEM\CurrentControlSet\Services\Tcpip6\Parameters DisabledComponents 0xffffffff -type dword





    Useful information.