1

I am creating a text file template for Arista switch configs, and I just worked out how to replace variables in my templates using Invoke-Expression.

The existing solution works fine for singular variables, but I have arrays I'd like to use as well, and later on, I will have some ForEach loops.

I need to know how to get this method to actually perform the operations inside my template, or alternatively, edit the 'invoked expression' before dumping it to a file.

Current script snippet :

#Variables
$peerlink1int = "49"
$stamps = @("208","209","210","211")

$filename = ('SwitchConfig' + $(get-date -f 'MM-dd-yyyy HH-mm'))
$destination_file = ".\$filename.txt"

$Base = Get-Content '.\Arista\01AristaBase.txt' | out-string
$revisedBase = Invoke-Expression "`"$Base`""
$revisedBase | Out-File -append $destination_file

Source file snippet :

interface Ethernet $peerlink1int
   switchport trunk allowed vlan $Stamps[0]-$Stamps[-1],1111,1234

Output result :

interface Ethernet 49
   switchport trunk allowed vlan 208 209 210 212[0]-208 209 210 211[-1],1111,1234

What I expected :

interface Ethernet 49
   switchport trunk allowed vlan 208-211,1111,1234

EDIT : As an aside, there was another method proposed to me that did not use Invoke-Expression. (Everywhere I go someone says it's unsafe to use.) However, this other method merely replaces text. I need a method of editing a text template that allows me to perform some commands on the resulting text.

For example, I need a ForEach loop that takes a template text of :

interface Vlan$stamps
   description V$stamps
   ip address 192.168.$stamps.2/25
   vrrp $stampnum ip 192.168.$stamps.1
   vrrp $stampnum description Stamp$stamps

And loops one time for every item in the array $stamps so you would get :

interface Vlan208
   description V208
   ip address 192.168.208.2/25
   vrrp $stampnum ip 192.168.208.1
   vrrp $stampnum description Stamp208

interface Vlan209
   description V209
   ip address 192.168.209.2/25
   vrrp $stampnum ip 192.168.209.1
   vrrp $stampnum description Stamp209

interface Vlan210
   description V210
   ip address 192.168.210.2/25
   vrrp $stampnum ip 192.168.210.1
   vrrp $stampnum description Stamp210

interface Vlan211
   description V211
   ip address 192.168.211.2/25
   vrrp $stampnum ip 192.168.211.1
   vrrp $stampnum description Stamp211
9
  • I am not sure where the problem is exactly. Do you have a particular question, if possible also with an error message? Commented Jan 23, 2020 at 21:52
  • Look at the "output result" vs "what I expected" The file 01AristaBase.txt is where the source file snippet comes from, Invoke-Expression does not respect the array [0] and [-1]. It just prints the whole array twice. Commented Jan 23, 2020 at 21:59
  • 1
    Here is the caveat to what you are doing. You are reading in a string and running invoke-expression on it. You can read in variables but [x] is considered part of the string, not variable. Unless you can do $($stamps[0]) in the text file, it wont work with invoke-expression Commented Jan 23, 2020 at 22:16
  • That is what seemed to be the case. I think that perhaps "invoke-expression" as well as the original replacement method is not going to work for what my end goal is. My objective is to create 5 or 6 config template sections that contain mostly plaintext, but a few variables as well. Then I want to either replace the variables with new values or do something like the foreach loop above. I am wondering if I should just make a bunch of small .ps1 files and use "echo" instead?? Commented Jan 23, 2020 at 22:19
  • 1
    I, honestly work with various template / ini files that have to be updated based on certain requirements. I use <variable> in the template file and replace them with -replace. Not suitable for large content but works for small sized files I have Commented Jan 23, 2020 at 22:22

2 Answers 2

1
  • Indeed, Invoke-Expression should be avoided.

  • Inside expandable strings ("..."), only simple variable references can be embedded directly (e.g., $stamps), in order to embed expressions - which includes property access and indexing - you need to enclose the expression as a whole in $(...) (e.g., $($Stamps[0])) - see this answer.

  • What you're ultimately looking for is string templating, which PowerShell provides - somewhat obscurely - via on-demand expansion of verbatim (literal) strings as expandable strings via $ExecutionContext.InvokeCommand.ExpandString().


# Define the string template - note the $(...) around
# $Stamps[0] and $Stamps[1]
$template = @'
interface Ethernet $peerlink1int
   switchport trunk allowed vlan $($Stamps[0])-$($Stamps[-1]),1111,1234
'@

# Define the variable referenced in the template
$stamps = '208', '209', '210', '211' # better than: @("208","209","210","211")

# Expand the template.
$revisedBase = $ExecutionContext.InvokeCommand.ExpandString($template)

$revisedBase now contains the following, which is what you expected:

interface Ethernet 
   switchport trunk allowed vlan 208-211,1111,1234

A simple demonstration that you can use the technique in a loop as well, with then-current variable values used in the on-demand expansion:

$template = '`$foo is now: $foo'

foreach ($foo in 1..3) {
  $ExecutionContext.InvokeCommand.ExpandString($template)
}

The above prints:

$foo is now: 1
$foo is now: 2
$foo is now: 3
Sign up to request clarification or add additional context in comments.

1 Comment

@n0ttsweet: Got it. A few tips: Use Get-Content -Raw to read your file in full, as a single multi-line string - piping to Out-File is not only less efficient, it appends an extra newline. The more concise and efficient approach is to use the foreach statement as an expression to collect its outputs: $result = foreach ($stamp in $stampArray) { $ExecutionContext.InvokeCommand.ExpandString($Vlans) }
1

This is what I would recommend for different scenarios that you have.

NOTE: You should not use Invoke-Expression but I wanted to pass out the information based on your previous request where you wanted to see how it would work if you didnt use -replace. I am providing with -replace which should be considered before you use Invoke-Expression.

1. Replace two indexed items

You can replace the two indexed items using -replace method on a string. For these examples, you can read the data from File by using Get-Content and when you do, please make sure you pipe that to Out-String. Otherwise, the newline and other special characters seem to disappear.

$stampArray = @("208","209","210","211")
$peerlink1int = "49"
$template = 'interface Ethernet $peerlink1int
   switchport trunk allowed vlan $Stamps[0]-$Stamps[-1],1111,1234'
#OR $template = Get-Content Your-File | Out-String

# You have to escape not only $, but also [ and ].
$template -replace '\$peerlink1int', $peerlink1int `
            -replace '\$Stamps\[0\]', $stampArray[0] `
            -replace '\$Stamps\[-1\]', $stampArray[-1] `
            | Out-File C:\temp\replaced.txt

or

# Invoke-Expression can only work if your variable is covered with $() to include indecies 
$peerlink1int = "49"
$stamps = @("208","209","210","211")
$template = 'interface Ethernet $peerlink1int
   switchport trunk allowed vlan $($Stamps[0])-$($Stamps[-1]),1111,1234'
$result = Invoke-Expression "`"$template`""

Output you get from both of the above samples is:

interface Ethernet 49
   switchport trunk allowed vlan 208-211,1111,1234

2. Replace multiple values from Array

There are two ways of going about it. 1 is the Invoke-Expression, which can invoke dangerous expressions in your script, and other is the -replace method.

$stampArray = @("208","209","210","211")
$template = 'interface Vlan$stamps
   description V$stamps
   ip address 192.168.$stamps.2/25
   vrrp $stampnum ip 192.168.$stamps.1
   vrrp $stampnum description Stamp$stamps'

# Using Invoke-Expression
$stampnum = 1
$result = @()
foreach($stamps in $stampArray) {
    $result += Invoke-Expression "`"$template`"" #$stamps and $stampnum will be replaced in the document.
    $stampnum ++
}

# Or you can do with the replace command
$stampnum = 1
$result = @()
foreach($stamps in $stampArray) {
    $result += $template -replace '\$stamps', $stamps -replace '\$stampnum', $stampnum++
}

$result | Out-File C:\temp\replaced.txt

And, Output you get is,

interface Vlan208
   description V208
   ip address 192.168.208.2/25
   vrrp 1 ip 192.168.208.1
   vrrp 1 description Stamp208
interface Vlan209
   description V209
   ip address 192.168.209.2/25
   vrrp 2 ip 192.168.209.1
   vrrp 2 description Stamp209
interface Vlan210
   description V210
   ip address 192.168.210.2/25
   vrrp 3 ip 192.168.210.1
   vrrp 3 description Stamp210
interface Vlan211
   description V211
   ip address 192.168.211.2/25
   vrrp 4 ip 192.168.211.1
   vrrp 4 description Stamp211

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.