Posts RSS Comments RSS 244 Posts and 358 Comments till now

Archive for October, 2008

Printing Multiple InfoPath forms (Automated)

My name is John Negus and I work for a small consulting company called MSEtechnology.  I specialize in Active Directory and Microsoft Operating Systems, with a little bit of Exchange and everything else Microsoft thrown in.  I have taught a lot of internal AD courses for Microsoft all over the world and my current contract has me working on site alongside Brandon “The Power” Shell.  That is where I was presented with the challenge of automating the printing of multiple InfoPath 2003 forms.
I was told, by Microsoft, that this was not an easy thing to do and that I could open up an advisory case in which they could give me some sample code to do this.  I was also informed that I could create a transform file for Word and print all the forms using Word.  I attempted this approach but gave up as it was long and drawn out.

Alas, PowerShell came to the rescue.  Once I found the correct COM object to use and with the help of the “Great One” (that’s Brandon, in-case you did not know) it was easy, as you can see below.

Param($Path = "\\sharepoint.corp.com\Form_Folder",[switch]$verbose)

Write-Host

function Invoke-FormPrint
{
    Param($InfoForm)
    $shortname = $InfoForm.name
    Write-Verbose "   – FormPrint: Creating COM Object"
    $infopath = New-Object -com InfoPath.Application -verbose:$false
    Write-Verbose "   – FormPrint: Calling Open(`$InfoForm.fullname,10)"
    $infopath.XDocuments.Open($InfoForm.fullname,10) | out-null
    Write-verbose "   – FormPrint: Calling PrintOut()"
    $infopath.XDocuments[0].printout()
    sleep 10
    Write-verbose "   – FormPrint: Calling Quit()"
    $infopath.quit()
    if(!$?)
    {
        Write-Host "    - Error printing Form: $shortname" -fore RED
    }
    else
    {
        Write-Host "    - Successfully printed Form: $shortname" -fore Green
    }
}

$erroractionpreference = "silentlycontinue"

if($verbose){$verbosepreference="continue";$erroractionpreference="continue"}

Write-Verbose " + Processing all Forms from: $path"
$Forms = dir $path "*.xml"

Write-Verbose "  + $($Forms.count) Forms found"
Write-host " + Processing all $($Forms.count) Forms located at: $path" -fore White

foreach($Form in $Forms)
{
    Write-Host "  + Printing Form: $($Form.Name)" -fore White
    Invoke-FormPrint $Form
}

WoooHooo! Exciting Powershell V2 and Win7\Win2008R2 Info


Click HERE

Dealing with Parameters in Powershell

I often get asked to review code for people and I often see them use $args for argument parsing (most often with VBScript converts.) While there is nothing wrong with that method, I do not believe it is the best way. Powershell has numerous ways to pass data to scripts/functions. Today, after helping a friend understand the differences between them, I decided it would be good to blog about it.


Let’s take a look at the different ways scripts/functions can take input.



You can pass data by position parameters:

.\myscript.ps1 filename.txt corp.lab

You can pass data by named parameters:

.\myscript.ps1 -list filename.txt -domain corp.lab

Finally, you can pipe data in:

get-content filename.txt | .\myscript.ps1 -domain corp.lab


That is great… but what would the code look like foreach of these?


There is little difference between a script and a function so I will illustrate using functions.
To use positional parameters

Example: PassByPosition filename.txt corp.lab

function PassByPosition{
  "FileName: {0}" -f $args[0]
  "Domain: {0}" -f $args[1]
}



To process Named parameters you use the Param() statement included in Powershell

Example: PassByName -list filename.txt -domain corp.lab

function PassByName{
    Param($FileName,$DomainName)
    "FileName: {0}" -f $FileName
    "Domain: {0}" -f $DomainName
}



To process piped data you can do something like

Example: get-content filename.txt | PassByPipe corp.lab

function PassByPipe{
    if($input)
    {
        foreach($val in $input)
        {
            "FileName: {0}" -f $val
            "Domain: {0}" -f $args[0]
        }
    }
}

NOTE: This is not only way to process piped input, but it is the simplest example. If you would like to see a more efficient way to process look HERE.


It gets REALY cool when using them together


Using the script below you can do any of these 

UseAllThree filename.txt corp.lab
UseAllThree -list filename.txt -domain corp.lab
get-content filename.txt | UseAllThree -domain corp.lab

function UseAllThree{
    Param($FileName,$DomainName)
    if($input)
    {
        foreach($val in $input)
        {
            "FileName: {0}" -f $val
            "Domain: {0}" -f $args[0]
        }
    }
    else
    {
        "FileName: {0}" -f $FileName
        "Domain: {0}" -f $DomainName
    }
}

 

We can even have default values for the Parameters


Using the script below you can do any of these 

UseAllThreewithDefaults filename.txt
UseAllThreewithDefaults -list filename.txt
get-content filename.txt | UseAllThreewithDefaults

function UseAllThreewithDefaults{
    Param($FileName = "FileName.txt",$DomainName = "Corp.lab")
    if($input)
    {
        foreach($val in $input)
        {
            "FileName: {0}" -f $val
            "Domain: {0}" -f $DomainName
        }
    }
    else
    {
        "FileName: {0}" -f $FileName
        "Domain: {0}" -f $DomainName
    }
}

Blog post by Mark Wilson on Win 2008 R2

Mark Wilson has a great blog post on some of the features expected in Win2k8 R2. You can find it HERE.

The bestpart is (of course ;) )

On the management front: there is a greater emphasis on the command line with improved scripting capabilities with PowerShell 2 and over 200 new cmdlets for server roles as well as power, blade and chassis management – working with vendors to deliver hardware which is compatible with WS-Management – and new command line tools for migration of Active Directory, DNS, DHCP, file and print servers; Server Manager will support remote connections, with a performance counter view and best practices analyzer (similar to the ones which we have seen shipped for server products such as Exchange Server for a few years now); and a new migration portal will expose step-by-step documentation for migration of roles and operating system settings from Windows Server 2003 and 2008 servers to Windows Server 2008 R2.

The depressing part

So, when do we get to use all this Windows Server 2008 R2 goodness? Well, Microsoft is not yet ready to release a beta and, based on previous versions of Windows Server, I would expect to see at least two betas and a couple of CTPs before the release candidates – but the product team is currently not committing to a date – other than to say “early 2010″ (which, incidentally, will be 2 years after Windows Server 2008 shipped).

Test-CitrixHotfix

I often find the need to compare the state of my PS servers. This little script allows me to find all the Citrix hotfixes for a group of server(s), or list of server(s) that have a specific hotfix.

Parameters:
$File: The name of the file which contains a list of servers
$Filter: Name or Regex of the Hotfix(es) to return
$Server: Name of the server to check

What it returns (a custom object):
ServerName: Name of the Server with the Hotfix(es)
Hotfixes: List of hotfix names [String[]]
RawObjects: Array of MFCOM MetaFrameHotfix objects

Examples
To List all the Hotfixes on a list of servers
.\Test-CitrixHotfix.ps1 ServerList.txt

To List a Specific hotfix on a list of servers
.\Test-CitrixHotfix.ps1 ServerList.txt -filter PSE450W2K3021

To List all the hotfixes on a specific Server
.\Test-CitrixHotfix.ps1 -s Server1

To List a specific hotfix for a specific server
.\Test-CitrixHotfix.ps1 -s Server1 -filter PSE450W2K3021

The Code:

Param($file,$filter=".*",$server)

Function Ping-Server {
   Param([string]$server)
   $pingresult = Get-WmiObject win32_pingstatus -f "address=’$Server’"
   if($pingresult.statuscode -eq 0) {$true} else {$false}
}
Function Check-CitrixHotfix{
    Param([string]$server)
    $mf = New-Object -ComObject MetaframeCOM.MetaframeFarm
    $mf.Initialize(1)
    $srv = $mf.GetServer2(6,$server)
    $list = $srv.winServerObject2.hotfixes | ?{$_.Name -match $filter}
    $list
}

if($file -and (Test-Path $file))
{
    $servers = cat $file
}
if($input)
{
    $servers = $input
}

if($server){$servers += $server}

foreach($srv in $servers)
{
    if(Ping-Server $srv)
    {
        $hotfixes = Check-CitrixHotfix $srv
        $myobj = "" | Select-Object ServerName,Hotfixes,RawObjects
        $myobj.ServerName = $srv
        $myobj.Hotfixes   =  $hotfixes | %{$_.Name}
        $myobj.RawObjects = $hotfixes
        $myobj
    }
    else
    {
        write-host $srv
        write-host "————"
        write-host "Server not pingable"
    }
}

Setting lDAPAdminLimits via Powershell

I was having a conversation with a friend the other day and he brought up a question about updating the AD property lDAPAdminLimits.

The Problem
The property is stored as an array of string values (at least as far as Powershell is concerned.) The initial reaction was do try something like this $queryPolicies.lDAPAdminLimits.MaxNotificationPerConn = 30, but this assumed that MaxNotficationPerConn was a property of lDAPAdminLimits and not the actual value (or at least part of the value.)

The Solution
Use the ADSI method PutEX to modify the value. PutEx uses ADS_PROPERTY_OPERATION_ENUM to make selective changes to an existing property. In the script below, we add the new value using the Update operation and then use the Delete operation to remove the old value.

Some Examples of Use

D:\Scripts\Set-ldapAdminPolicy.ps1 MaxNotificationPerConn 45
D:\Scripts\Set-ldapAdminPolicy.ps1 MaxQueryDuration 360
D:\Scripts\Set-ldapAdminPolicy.ps1 MaxPageSize 500
D:\Scripts\Set-ldapAdminPolicy.ps1 MaxPoolThreads 8

Here is a link on how to view/set via NTDSUtil.exe
How to view and set LDAP policy in Active Directory by using Ntdsutil.exe

The Code

Param($policy=$(throw ‘$policy is required’),$count=30)
$rootDSE = [ADSI]"LDAP://rootDSE"
$config = $rootDSE.configurationNamingContext
$queryPolicies = [adsi]"LDAP://CN=Default Query Policy,CN=Query-Policies,cn=Directory Service,cn=Windows NT,CN=Services,$config"
$oldvalue = $queryPolicies.lDAPAdminLimits | ?{$_ -match $policy}
$queryPolicies.PutEx(3,"lDAPAdminLimits",@("$policy=$count"))
$queryPolicies.Setinfo()
$queryPolicies.PutEx(4,"lDAPAdminLimits",@("$oldvalue"))
$queryPolicies.Setinfo()

Active Directory cmdlet of the Week (on turbochargead.org)

This week I will begin a series that will focus on a new Active Directory cmdlet every week. It will cover the basics of that cmdlet providing a use case example and any gotcha’s I have uncovered.

While I want this series to be dynamic and flexible I also need to start somewhere so I will start with the Quest cmdlets and then probably move on to other third party providers or even the Microsoft Active Directory cmdlets (assuming the information embargo is removed.)

If you have a recommendation or any ideas of what you would like to know more about please do not be shy. Leave a note and let me know.

You can subscribe to the series here:  http://turbochargead.org/articles/

Bulk Imports using CSV (cross post from TurboChargeAD.org)

As some of you may know I am doing some guest blogging over at TurboChargeAD.org and below is a link to the second such article.

Here is the Link: Bulking Importing User from CSV file using Quest cmdlets

Here is the code

#Import-ADUser.ps1
#requires -pssnapin Quest.ActiveRoles.ADManagement
Param($file,$OU,[switch]$whatif,[switch]$verbose)

if($verbose){$VerbosePreference = "Continue"}

if(!(test-path $file))
{
   throw " File NOT found!"
   return
}

if(!$ou)
{
    # Setting the OU to the Users container
    $ou = "CN=Users,{0}" -f ([ADSI]"").distinguishedName[0]
}

Write-Verbose " Using File: $file"
Write-Verbose " Using OU: $ou"

foreach($user in (Import-Csv $file))
{
    $props = @{}
    # Getting a list property names.
    $propNames = $user | Get-Member -MemberType properties | %{$_.name}
    # Foreach of the property names add an entry in the hash table with
    # the key being the property name and value being the value from the object
    foreach($prop in $propNames)
    {
        # This removes quotes if they exist
    $value = $user.$prop -replace "’|`"",""
        $props += @{$prop=$value}
    }
    # Create User using the displayname as the CN
    $MyUser = new-qaduser -name $user.displayName `
                          -ObjectAttributes $props `
                          -parent $OU `
                          -whatif:$whatif `
                          -verbose:$verbose
    $MyUser
}

They like me, right now, they like me (Sally Field joke)

Just got the mail. It looks like I am MVP for another year :)

The orginal quote said “you like me, right now, you like me!”

Cool new blog to watch (up and coming Powershellite)

I often run into blogs while googling for stuff at work and when I find good ones I like to share.

I found this one looking for NC373i driver info and it turned out to be a great blog and not just because it answered my question, but it has a solid focus on engineering type tasks (like debuggin and scripting.)

Anyway here it is: http://blogs.technet.com/brad_rutkowski/default.aspx

p.s. Funny story, The evening before I found his blog we had an email conversation regarding Powershell (we have a common friend Brian Puhl.) His blog never came up, but the next day I was googling something completely unrelated and his blog was the first hit… how crazy is that?