1

UserData by default will run with Powershell V5.1 on the Windows Server 2022 AMI on an AWS EC2 instance that spins up. However, I want to use some cmdlets that are only supported in Powershell version 7 and greater.

How am I best able to run a script with Powershell 7+ when booting the instance with UserData?

I currently have a script that installs powershell 7, but then from that point I am not sure how to use v7 to run the rest of the commands that I have.

Invoke-WebRequest -Uri https://github.com/PowerShell/PowerShell/releases/download/v7.3.1/PowerShell-7.3.1-win-x64.msi -OutFile PowerShell.msi
Start-Process msiexec.exe -ArgumentList '/i PowerShell.msi /quiet' -Wait

I am using the WINDOWS_SERVER_2022_ENGLISH_FULL_BASE AMI.

I have tried using something like Invoke-Expression, and also have tried to get the script to call itself recursively with some conditionals, e.g.

# First Run with ps 5.1
if ($PSVersionTable.PSVersion -lt [Version]"7.0") {
    Invoke-WebRequest -Uri https://github.com/PowerShell/PowerShell/releases/download/v7.3.1/PowerShell-7.3.1-win-x64.msi -OutFile PowerShell.msi
    Start-Process msiexec.exe -ArgumentList '/i PowerShell.msi /quiet' -Wait
    cd "C:\Program Files\PowerShell\7"
    # Run this same script with ps7
    ./pwsh $PSCommandPath
    exit
}

# 
if ($PSVersionTable.PSVersion -gt [Version]"7.0") {
    # Do the things I need to do with ps7...
}

Both of my attempts have been silently failing, and with ec2 userdata it is very hard to get info on why.

Michael
  • 31
  • 4
  • how about placing the code you want to run inside a scriptblock (`{...}`), call it using the invocation operator (`&`), then do the same with PowerShell 7 by calling the application itself, or entering into pwsh as an interactive console. – Abraham Zinala Jan 18 '23 at 22:34
  • Would you be able to give a short example of the scriptblock and invocation operator? I don't think that I would be able to enter into pwsh interactively, as it all needs to be done via script, and there won't be any human intervention. – Michael Jan 18 '23 at 22:47
  • Sure. Like this: `$scriptblock = {Param($text)Write-Host -Object "$text " -NoNewline;};& $scriptblock 'Hello';pwsh.exe -Command "$scriptblock 'World'"` – Abraham Zinala Jan 18 '23 at 22:52
  • @Michael - is there anything useful in /var/log/cloud-init-output.log? Have you tried putting some print statements etc to see where it is failing by any chance? What exactly is your UserData? Is it even running PowerShell code within.. ? – Adil Hindistan Jan 19 '23 at 00:48
  • @AdilHindistan, this is a Windows Instance so the logs are found here: C:\ProgramData\Amazon\EC2Launch\log\agent.log What I'm finding is that when something has gone wrong, usually I can't even connect to the instance via SSM. I'm using CDK and the UserData class, so my script gets wrapped correctly in the `` tags. I know my UserData script is getting in there because I can find it in on the instance once I do have a connection. – Michael Jan 19 '23 at 04:24
  • Use two scripts, one to install powershell and the other to run your code? – Paolo Jan 19 '23 at 09:34
  • @Michael - of course you're right about location. Honestly, your script looks fine. It might be worth looking into separating the PS7 code to a different script like Paolo suggested . You can include commands to download and run the script from s3. You need a way to get logs which means you need a functioning instance first, run the pwsh code second. Once you figure out the problem, you can put it back again. – Adil Hindistan Jan 19 '23 at 13:24
  • 1
    @AdilHindistan Thanks! This idea of downloading the second script from S3 and executing it via the initial UserData turned out to be a bit easier and more ergonomic than putting the code inside a script block (which did work). – Michael Jan 20 '23 at 01:42

1 Answers1

2

The approach that ended up working was to have 2 different scripts. The first script installs PS7, and then downloads the second script from S3 and executes it using PS7.

User Data exectued with PS5:

#init.ps1
<powershell>
Invoke-WebRequest -Uri https://github.com/PowerShell/PowerShell/releases/download/v7.3.1/PowerShell-7.3.1-win-x64.msi -OutFile PowerShell.msi
Start-Process msiexec.exe -ArgumentList '/i PowerShell.msi /quiet' -Wait
cd "C:\Program Files\PowerShell\7"
mkdir (Split-Path -Path 'C:/temp/setupGateway.ps1' ) -ea 0
Read-S3Object -BucketName 'my-bucket' -key 'setupGateway.ps1' -file 'C:/temp/setupGateway.ps1' -ErrorAction Stop
& "C:\Program Files\PowerShell\7\pwsh" "C:\temp\setupGateway.ps1"
</powershell>
<persist>true</persist>

PS7 script, executed separately:

# setup.ps1
Write-Output $PSVersionTable
Write-Output "Hello from PS7"

All that needs to happen to make this work is to make sure that you copy the setup.ps1 script to an S3 location. Which can be achieved in lots of different ways depending on the rest of your setup.

Michael
  • 31
  • 4