Let's start with the first problem.
After you run $template = "$($_.ProcessName)" you expect the value of $template to be the string $($_.ProcessName) but it isn't.
> $template = "$($_.ProcessName)"
> "[" + $template + "]"
[]
The expression in the double quotes was evaluated (and became the empty string).
You need to prevent that by using single quotes.
> $template = '$($_.ProcessName)'
> "[" + $template + "]"
[$($_.ProcessName)]
Ok. So let's try that in your foreach loop?
> $list = Get-Process
> $template = '$($_.ProcessName)'
> $list | ForEach-Object {$template}
$($_.ProcessName)
$($_.ProcessName)
$($_.ProcessName)
$($_.ProcessName)
...
Hm... not what we wanted exactly either. That's because powershell isn't evaluating the string as an expression. So we need to force it to do that.
You can do that with Invoke-Expression.
> $list = Get-Process
> $template = '$($_.ProcessName)'
> $list | ForEach-Object {Invoke-Expression $template}
procA
procB
procC
...
(You can also use a scriptblock as iCodez indicates in their answer. I can't comment on the relative merits of one approach over the other. I think the scriptblock is likely to be more extensible and useful in general though.)
$templatethere? It doesn't hold what you think it does.