Skip to content
Menu
The Lonely Administrator
  • PowerShell Tips & Tricks
  • Books & Training
  • Essential PowerShell Learning Resources
  • Privacy Policy
  • About Me
The Lonely Administrator

Out-MSWord Revised

Posted on September 18, 2009

This summer I wrote about a function I developed called Out-MSWord. The function was discussed in my Practical PowerShell column which was published in the free e-Journal Windows Administration in RealTime put out by RealTime Publishers. The original was published in Issue #17 if you are interested. The function accepted pipelined input and created a Microsoft Word document. Naturally, you need to have Microsoft Word installed in order for this to work.

Manage and Report Active Directory, Exchange and Microsoft 365 with
ManageEngine ADManager Plus - Download Free Trial

Exclusive offer on ADManager Plus for US and UK regions. Claim now!

PS C:\> get-service | out-MSWord

The function accepted a number of parameters so you could control font name, size, color, append, and more. The function was written for PowerShell v1.0 but also worked on PowerShell v2.0. However, I was revisiting the function and realize there were places I could tweak, such as adding additional error handing. I also realized that if rewrote this for PowerShell v2.0, I could create an advanced function and take advantage of cmdletbinding, advanced parameters and help.

   1: Function Out-MSWord {

   2: #requires -version 2.0

   3:  

   4: <#

   5: .Synopsis

   6:     Accept pipelined input from a PowerShell expression and write output to a Microsoft Word document.

   7: .Description

   8:     Pipe cmdlet output to this function and create a MS Word document. You must have Microsoft Word installed. 

   9:     This has been tested with Microsoft Word 2007. If you don't specify a filename, Microsoft Word will open 

  10:     upon completion and you can manually save the document. You can specify a filename along with optional 

  11:     parameters -append or -noclobber. 

  12:     

  13:     The -noclobber parameter will not overwrite any existing files. Otherwise, if you specify a file and it 

  14:     already exists, it will be overwritten.

  15:  

  16:     The -Tee parameter will write piped input to the console as well as send it to the Word file.

  17:     

  18:     You can specify font size, name and color for the document body and footer. The defaults are 8pt Consolas. 

  19:     You will get best results using a fixed width font like Courier New or Lucida Console. Page numbering is 

  20:     automatically defined for the lower right corner of each page. If you don't specify a footer it defaults to 

  21:     Printed [datetime].

  22:  

  23: .Parameter Path

  24:     The path and filename for the new Microsoft Word document.

  25: .Parameter InputObject

  26:     Pipelined objects passed to this function.

  27: .Parameter Tee

  28:     Display objects in the console and write to the Word document.

  29: .Parameter Append

  30:     Append data to the Microsoft Word document

  31: .Parameter NoClobber

  32:     Don't overwrite the Microsoft Word document if it already exists.

  33: .Parameter Font

  34:     Font name to use in the document body. Default is Consolas.

  35: .Parameter FontSize

  36:     Font size to use in the document body. Default is 8. This parameter has an alias of Size.

  37: .Parameter FontColor

  38:     Font color to use in the document body. Valid choices are Auto,Black,Blue,Green,Red,Teal,Violet, and Yellow. 

  39:     The default is Auto. This paramater has an alias of Color.

  40: .Parameter FooterText

  41:     Text to use for the document footer. The default is Printed [datetime]. This parameter has an alias

  42:     of Footer.

  43: .Parameter FooterFont

  44:     Font to use for the document footer. Default is Consolas.

  45: .Parameter FooterSize

  46:     Font size to use for the document footer. Default is 8.

  47:  

  48: .Example

  49:      get-service | where {$_.status -eq "running"} | Out-MSWord

  50:     

  51:     This will create a Microsoft Word document with a list of all running processes.

  52: .Example

  53:     gwmi win32_bios | ow -path "c:\files\my3.doc" -noclobber

  54:  

  55:     Save the output from Get-WMIObject for the Win32_Bios class to a file called c:\files\my3.doc. If the file 

  56:     already exists it will not be overwritten.

  57: .Example

  58:     get-eventlog -list | ow c:\test\log.docx -tee

  59:     

  60:     Take the outpout from Get-Eventlog and not only save it to the specified file but also display the results 

  61:     in the console. This example assumes you are using the alias ow for the Out-MSWord function.

  62: .Example

  63:     get-process | sort workingset -descending | where {$_.workingset -lt 100mb -and $_.workingset -gt 25mb} | ow c:\files\ps.doc -fontcolor yellow -font "Lucida Console" -fontsize 10.5 -append

  64:     

  65:     This expression will create a Word document of processes with a workingset of between 25MB and 100MB. The 

  66:     results will be appended to the file, ps.doc, written in a yellow Lucida Console 10.5 font. This example 

  67:     assumes you are using the ow alias for this function.

  68: .Example

  69:     @((gwmi win32_operatingsystem),(gwmi win32_bios),(gwmi win32_computersystem))  | ow -footertext ("{0} captured: {1}" -f $env:computername,(get-date))

  70:  

  71:     Take the output from the three Get-WMIObject expressions and write output to all of them to a Microsoft Word 

  72:     document. A custom footer will be included that shows the computername and the current datetime.

  73: .Example

  74:     $file="c:\reports\eventreport.doc"

  75:     C:\PS>"Eventlog report for $env:computername" | Out-MSWord $file -size 14

  76:     C:\PS>$events=get-eventlog System -newest 100 | group EntryType

  77:     C:\PS>for ($i=0;$i -lt $events.count;$i++) {

  78:       switch ($events[$i].name) {

  79:         "Information" {

  80:               Out-MSWord $file -input "Information Events" -size 10 -append

  81:               $events[$i].group | format-list TimeWritten,EventID,Source,Message | 

  82:               Out-MSWord $file -color Green -append 

  83:         }

  84:         "SuccessAudit" {

  85:               Out-MSWord $file -input "Success Audit Events" -size 10 -append

  86:               $events[$i].group | format-list TimeWritten,EventID,Source,Message | 

  87:               Out-MSWord $file -color Green -append     

  88:         }

  89:         "Error" {

  90:               Out-MSWord $file -input "Error Events" -size 10 -append

  91:               $events[$i].group | format-list TimeWritten,EventID,Source,Message | 

  92:               Out-MSWord $file -color Red -append

  93:         }

  94:         "FailureAudit" {

  95:               Out-MSWord $file -input "Failure Audit Events" -size 10 -append

  96:               $events[$i].group | format-list TimeWritten,EventID,Source,Message | 

  97:               Out-MSWord $file -color Red -append 

  98:         }

  99:         "Warning" {

 100:               Out-MSWord $file -input "Warning Events" -size 10 -append

 101:               $events[$i].group | format-list TimeWritten,EventID,Source,Message | 

 102:               Out-MSWord $file -color Yellow -append 

 103:          }

 104:       } #end Switch

 105:      } #end foreach

 106:  

 107:     In this example a formatted Microsoft Word document is created that list eventlog information for the local

 108:     computer. Event log information is grouped by EntryType and then group is processed by using a For statement

 109:     and a Switch construct. Event information is written to the Microsoft Word document in a corresponding font

 110:     color. This example also shows how to insert text into the document by explicitly using the -InputObject

 111:     parameter.

 112:  

 113: .Inputs

 114:     Accepts objects as pipelined input.

 115: .Outputs

 116:     Microsoft Word document object  

 117: .Link

 118:    Out-Printer

 119:    Out-File

 120: .Notes

 121:  NAME:      Out-MSWord

 122:  VERSION:   2.0

 123:  AUTHOR:    Jeffery Hicks

 124:  LASTEDIT:  September 18, 2009

 125:  

 126:  

 127: #>

 128:  

 129: [CmdletBinding()]

 130:  

 131: #define the parameters

 132: param (

 133:     [Parameter(ValueFromPipeline=$False,Position=0,Mandatory=$False,HelpMessage="The filename and path for the saved Word document.")] 

 134:     [ValidateScript({Test-Path (split-path $_)})]

 135:     [String]$Path,

 136:     

 137:     [Parameter(ValueFromPipeline=$True,Position=1,Mandatory=$True,HelpMessage="Pipelined input.")] 

 138:     [object[]]$InputObject,

 139:  

 140:     [Parameter(ValueFromPipeline=$False,Mandatory=$False, HelpMessage="If specified, write to the console and the Word document.")] 

 141:     [switch]$Tee,

 142:      

 143:     [Parameter(ValueFromPipeline=$False,Mandatory=$False,HelpMessage="If specified, append to output write to the Word document.")] 

 144:     [switch]$Append,

 145:      

 146:     [Parameter(ValueFromPipeline=$False,Mandatory=$False,HelpMessage="If specified, don't overwrite the existing Word document specified by `$path.")] 

 147:     [switch]$NoClobber,

 148:           

 149:     [Parameter(ValueFromPipeline=$False,Mandatory=$False,HelpMessage="The font family to use for the document body.")] 

 150:     [string]$Font="Consolas",

 151:       

 152:     [Parameter(ValueFromPipeline=$False,Mandatory=$False,HelpMessage="The font size to use for the document body.")] 

 153:     [Alias("Size")]

 154:     [double]$FontSize=8,      

 155:       

 156:     [Parameter(ValueFromPipeline=$False,Mandatory=$False,HelpMessage="The font color to use for the document body.")]

 157:     [ValidateSet("Auto","Black","Blue","Green","Red","Teal","Violet","Yellow")] 

 158:     [Alias("Color")]

 159:     [string]$FontColor="Auto",

 160:    

 161:     [Parameter(ValueFromPipeline=$False,Mandatory=$False,HelpMessage="Text to use for the footer.")] 

 162:     [Alias("Footer")]

 163:     [string]$FooterText=("printed {0}" -f (Get-Date)),

 164:     

 165:     [Parameter(ValueFromPipeline=$False,Mandatory=$False,HelpMessage="The font to use for the document footer.")] 

 166:     [string]$FooterFont="Consolas",

 167:     

 168:     [Parameter(ValueFromPipeline=$False,Mandatory=$False,HelpMessage="The font size to use for the document footer.")] 

 169:     [double]$FooterSize=8

 170:     

 171:      ) #end param definition

 172:  

 173:         

 174:  BEGIN{

 175:  

 176:   #define some MS Word variables

 177:   $wdSeekMainDocument       = 0

 178:   $wdSeekPrimaryFooter      = 4

 179:   $wdSeekPrimaryHeader      = 1

 180:   $wdAlignPageNumberCenter  =  1

 181:   $wdAlignPageNumberInside  =  3

 182:   $wdAlignPageNumberLeft    = 0

 183:   $wdAlignPageNumberOutside = 4

 184:   $wdAlignPageNumberRight   =  2

 185:   $wdStory = 6   

 186:   

 187:   #define color values   

 188:   $Auto   = 0

 189:   $Black  =  1 

 190:   $Blue   = 16711680 

 191:   $Green  =  32768

 192:   $Red    = 255

 193:   $Teal   = 8421376

 194:   $Violet = 8388736 

 195:   $Yellow = 32896

 196:   

 197:   # #select font color

 198:   switch ($fontcolor) {

 199:     "Auto"   {$color=$Auto}

 200:     "Black"  {$color=$Black }

 201:     "Blue"   {$color=$Blue }

 202:     "Green"  {$color=$Green }

 203:     "Red"    {$color=$Red }

 204:     "Teal"   {$color=$Teal }

 205:     "Violet" {$color=$Violet }

 206:     "Yellow" {$color=$Yellow }

 207:     

 208:     default {

 209:      #this line should never get called since there is data validation with the fontcolor parameter, but I'll leave it in

 210:      #just in case

 211:       Write-Warning "Invalid color choice: $fontcolor. Using Default. Valid color choices are: Black,Blue,Green,Red,Teal,Violet and Yellow."

 212:       $color=$wdAuto}

 213:   } #end Switch

 214:    

 215:   $ErrorActionPreference="SilentlyContinue"

 216:   

 217:   Trap {

 218:     Write-Warning "There was an error. Make sure you have Microsoft Word installed and that you are specifying valid filename or path."

 219:     Break

 220:   } 

 221:   #create the MS Word COM object

 222:   $word=New-Object -ComObject "Word.Application" -ea Stop

 223:   

 224:    #get document if -append

 225:   if ($append)

 226:   {

 227:     #verify file exists and if so, open it

 228:     if (Test-Path $path)

 229:     {

 230:         $doc=$word.documents.open($path) 

 231:         $blnNewFile=$False

 232:  

 233:     }

 234:     else

 235:     {

 236:         #you asked to append to a file that doesn't exist so create a new one

 237:         $doc=$word.Documents.add()

 238:         $blnNewfile=$True

 239:     }

 240:   }

 241:   else 

 242:   {

 243:     #create a new document

 244:     $doc=$word.Documents.add()

 245:     $blnNewFile=$True

 246:  

 247:    }

 248:  

 249:   $selection=$word.Selection    

 250:   

 251:   #get the footer

 252:   $doc.ActiveWindow.ActivePane.view.SeekView=$wdSeekPrimaryFooter 

 253:  

 254:   #set the footer

 255:   $selection.HeaderFooter.Range.Text=$footerText

 256:   

 257:   #add page numbering

 258:   $selection.HeaderFooter.PageNumbers.Add($wdAlignPageNumberRight) | Out-Null

 259:  

 260:   #get the footer and format font

 261:   $footers=$doc.Sections.Last.Footers

 262:  

 263:   foreach ($footer in $footers) {

 264:     if ($footer.exists) {

 265:        $footer.range.font.name=$footerfont

 266:        $footer.range.font.size=$footersize

 267:        }

 268:    } #end Foreach

 269:  

 270:    #return focus to main document

 271:    $doc.ActiveWindow.ActivePane.view.SeekView=$wdSeekMainDocument

 272:  

 273:   #initialize an array to hold incoming objects 

 274:   $data=@()

 275:   

 276: } #end BEGIN scriptblock

 277:  

 278:  PROCESS {

 279:    

 280:    #save incoming objects to a variable

 281:    if ($InputObject) {

 282:      $data+=$InputObject

 283:     }

 284:    else

 285:     {

 286:        $data+=$_

 287:     }

 288:    

 289:    #write piped object if -Tee

 290:    if ($tee) 

 291:    {

 292:     write $_

 293:    }

 294:     

 295: } #end PROCESS scriptblock

 296:  

 297:  END {

 298:  

 299:    #convert data to string

 300:     $text=$data | Out-String

 301:  

 302:    #only write to the file if $text exists

 303:    if ($text) {

 304:       if (!$blnNewFile)  #the file has been opened before

 305:       { 

 306:         #jump to the end

 307:         $selection.Endkey($wdStory) | Out-Null

 308:         #insert blank lines

 309:          $selection.TypeParagraph()

 310:            $selection.TypeParagraph()

 311:  

 312:        } #end if !$blnNewFile

 313:        

 314:        #set font and paragraph settings  

 315:        $selection.font.name=$font

 316:        $selection.font.size=$fontsize

 317:        $selection.font.color=$color

 318:        $selection.paragraphFormat.SpaceBefore = 0

 319:        $selection.paragraphFormat.SpaceAfter = 0

 320:     

 321:       #add the text to the document

 322:       $selection.TypeText($text.Trim()) 

 323:       

 324:       if ($path) 

 325:        {

 326:         #don't overwrite if -noclobber was specified

 327:         if ($noclobber) 

 328:         {

 329:             #does file exist?

 330:             if (Test-path $path) 

 331:             {

 332:                 Write-Warning "-NoClobber specified and $path exists"

 333:                 #show the document so you can review and/or save

 334:                 $word.visible=$True

 335:             } #end if item exists

 336:             else 

 337:             {

 338:                 $doc.SaveAs([ref]$path)

 339:                 $word.quit()

 340:             }

 341:         } #end if $noclobber

 342:         else

 343:         {

 344:             $doc.SaveAs([ref]$path)    

 345:             $word.quit()

 346:         }

 347:        } #end if $path

 348:        else #no filepath 

 349:        {

 350:         #jump to the beginning of the document

 351:         $selection.Homekey($wdStory) | Out-Null

 352:         

 353:         #show the document so you can review and/or save

 354:         $word.visible=$True

 355:       }

 356:   } #end if $text

 357:   else

 358:   {

 359:     Write-Warning "No data to write."

 360:     #turn off alerts

 361:     $word.displayAlerts=0

 362:     #close without saving

 363:     $doc.close([ref]$Word.WdSaveOptions.wdDoNotSaveChanges)

 364:    

 365:     #exit Microsoft Word

 366:       $word.quit()

 367:   }

 368:  } #end END scriptblock

 369:  

 370: } #end Function

 371:  

 372: #create an alias 

 373: Set-Alias ow Out-MSWord

 374: Get-Alias ow

 375:  

The information at the beginning is used by Get-Help to present help information just as you get with a cmdlet. That’s the great thing in PowerShell v2; you can write an advanced help-out-mswordfunction that behaves essentially like a cmdlet.

If you use –full you will get full parameter help and a number of examples. I love that I can now incorparate documentation directly into the script or function.

One thing I was able to accomplish was move some of the error handling to the parameters. In PowerShell v2 you can add validation tests. I’ve added one to verify the specified path value is valid and one to check the value passed for the font color. In the first version I used a Switch construct to verify and write a warning message if an invalid value was passed., Now I can use parameter validation.

   1: [Parameter(ValueFromPipeline=$False,Mandatory=$False,HelpMessage="The font color to use for the document body.")]

   2:     [ValidateSet("Auto","Black","Blue","Green","Red","Teal","Violet","Yellow")] 

   3:     [Alias("Color")]

   4:     [string]$FontColor="Auto",

You’ll notice I also defined an alias for this parameter. Read the help topic about_Functions_Advanced_Parameters to learn more.

My script file also defines an alias, ow, for the Out-MSWord function. Be sure to look at the help examples to see how to use this function.

Download

Out-MSWordv2.txt

  and save it as a .ps1 file. You’ll need to dot source the script to load it into your session or script or add it to your profile.

If you find a use for this in a production environment, I’d love to hear about it.


Behind the PowerShell Pipeline

Share this:

  • Click to share on X (Opens in new window) X
  • Click to share on Facebook (Opens in new window) Facebook
  • Click to share on Mastodon (Opens in new window) Mastodon
  • Click to share on LinkedIn (Opens in new window) LinkedIn
  • Click to share on Pocket (Opens in new window) Pocket
  • Click to share on Reddit (Opens in new window) Reddit
  • Click to print (Opens in new window) Print
  • Click to email a link to a friend (Opens in new window) Email

Like this:

Like Loading...

Related

reports

Powered by Buttondown.

Join me on Mastodon

The PowerShell Practice Primer
Learn PowerShell in a Month of Lunches Fourth edition


Get More PowerShell Books

Other Online Content

github



PluralSightAuthor

Active Directory ADSI Automation Backup Books CIM CLI conferences console Friday Fun FridayFun Function functions Get-WMIObject GitHub hashtable HTML Hyper-V Iron Scripter ISE Measure-Object module modules MrRoboto new-object objects Out-Gridview Pipeline PowerShell PowerShell ISE Profile prompt Registry Regular Expressions remoting SAPIEN ScriptBlock Scripting Techmentor Training VBScript WMI WPF Write-Host xml

©2025 The Lonely Administrator | Powered by SuperbThemes!
%d