Monday, October 25, 2021

Saving PowerPoint Deck as a PDF - with PowerShell 7

 This week, I got into a twitter discussion about a presenter posting their slides for delegates. My personal preference is to always share the slides. Often, especially when I was working for an employer, I'd share a PDF of the file rather than the slides. Some decks had had a LOT of effort put in (some by inhouse graphics guys) and giving those animations away was sub-optimal for IP reasons. 

My approach was to post whatever material was appropriate to a DropBox share - and let people have that. I created a unique Bit.Ly short URL, and put that on the first few and final slides. Since the shortend URL never changed, I could put whatever content was relevant - letting attendees know this would all disappear in a few week's time.

What I used to do was to run a short little PowerShell script, during the final development of the deck. If, like SO many presenters, I made a change in the speaker's lounge just before going on - I could run the script just before unplugging and heading to the talk. I could just share https://bit.ly/TFLPresentations and manage the content.

Converting your PowerPoint deck to a PDF is straightforward - I blogged about how to convert a Powerpoint deck into a PDF using PowerShell a long time ago.  The script uses the PowerPoint objects that are part of Office and which Office setup conveniently stores in the .NET Framework's Global Assembly Cache (the GAC). 

After the Twitter conversation, I dusted off this script and to my surprise, it did not work. And the reason is that PowerShell 7 uses .NET and not The .NET Framework. The former does not make use of the GAC, so when I tried to load the appropriate assembly I got this error:

PS C:\Foo> Add-Type -AssemblyName microsoft.office.interop.powerpoint

Add-Type: Cannot find path 'C:\Foo\microsoft.office.interop.powerpoint.dll' because it does not exist.

After a few minutes contemplation I realised what the issue was. And by using Void Tools most excellent SearchEvereything product, I found the assembly at the snappy location of: C:\Windows\assembly\GAC_MSIL\Microsoft.Office.Interop.PowerPoint\15.0.0.0__71e9bce111e9429c\Microsoft.Office.Interop.PowerPoint.dll.

So now I know the location, I could do this to save a single file:

# Define the files
$PPTFile = 'C:\foo\test.pptx'
$PDFFile = 'D:\Dropbox\AAA-TFL Presententations LIVE\test.pdf'
# Add the DLL
$DLLFile = 'C:\Windows\assembly\GAC_MSIL\Microsoft.Office.Interop.PowerPoint\' +
           '15.0.0.0__71e9bce111e9429c\Microsoft.Office.Interop.PowerPoint.dll'
# Add the type
Add-Type -Path $DLLFile
# Create save as PDF option
$SaveOption = [Microsoft.Office.Interop.PowerPoint.PpSaveAsFileType]::ppSaveAsPDF
# Open the PowerPoint Presentation
$PPT = New-Object -ComObject "PowerPoint.Application"
$Presentation = $PPT.Presentations.Open($PPTFile)
# Save it as a PDF and close the presentation
$Presentation.SaveAs($PdfFile,$SaveOption)
$Presentation.Close()
# Close PPT and kill the process to be sure
$PPT.Quit()
Get-Process 'POWERPNT' | Stop-Process

This fragment adds in the necessary DLL, opens the presentation from wherever I have it stored today, and then saves the file as PDF into the DropBox folder. 

I kind of thought that the Quit() method would kill PowerPoint but in my testing, the process hung around hence killing the process at the end of the fragment. Depending on how adventurous you wish to be, you can always wrap this code to get all the PPT files in one folder and save them away. 

Tuesday, October 19, 2021

Patching and PowerShell 7

 Many of you reading that blog know I'm a big supporter of PowerShell 7. I hope that many of you share my enthusiasm. After writing several books on PowerShell, and two on PowerShell 7, I find it a pretty good product.

One of the challenges which PowerShell brings to the enterprise is updates. Like most software products, there are bugs and vulnerabilities in PowerShell. I was reminded again of this fact reading an article from my Powershell Paper.Li paper. Today the top article comes from Bleeping Computer: https://www.bleepingcomputer.com/news/microsoft/microsoft-asks-admins-to-patch-powershell-to-fix-wdac-bypass/.

The team do a great job in updating the code as soon as the vulnerabilities are found. In some (many) cases, the issue is not in PowerShell itself, but in one of the components, such as .NET. Once the team releases an update, you need to ensure that the update is rolled out everywhere you use PowerShell 7.

The method you use to keep PowerShell 7 up to date depends on how you installed PowerShell 7 in the first place. And here you have (at least) 3 options:

  • Use Install-PowerShell.ps1 - this is a script you download from GitHub which enables you to install the latest production version of PowerShell, the latest next version Preview and (for the very brave) today's daily builds. If you use this, then you must manually update the software yourself. I love this as I am in control!
  • Use the Microsoft store - you get the released version directly from the store. This should automatically keep your version up to date as the PowerShell team. At present, there is no Store application for Windows Server. You can also get the Preview version from the store (although I do not know how that plays if you also have the released version installed). 
  • Use a third-party repository - you could download PowerShell directly from Github and install it (and manually update it as new versions get pushed out). You can also use Chocolatey although technically that is not supported.
So you have options. Personally, I update the daily build once or twice a week and update the preview and production versions once I get a warning about an updated version the next time I start PowerShell.  

So no matter how you install PowerShell 7, make sure you have a patch strategy in place. And if you have read this far, make sure you have installed 7.1.5 (or 7.0.8 if you are still on 7.0).