To join strings in PowerShell, put the -join operator between a list and the separator you want. For two or three values, double quotes or the + operator concatenate them just as well, and -f builds a string from a template:
$cities = 'Austin', 'Denver', 'Boston'
$cities -join ', '
$first = 'Maria'
$last = 'Lopez'
"$first $last"
$first + ' ' + $last
'{0}, {1}' -f $last, $first
Output:
Austin, Denver, Boston
Maria Lopez
Maria Lopez
Lopez, Maria

Those four cover nearly every case. The details below matter when the list holds blanks, numbers or objects, and when you build long strings in a loop. I tested each one in PowerShell 7.6 and Windows PowerShell 5.1.
How the -join operator joins strings
-join has two forms. With a separator on the right, it places that text between the items. With -join in front and nothing after it, the items run together with no separator at all:
$parts = 'Seattle', 'WA', '98101'
$parts -join ' '
$parts -join ''
$parts -join ' | '
-join $parts
Output:
Seattle WA 98101
SeattleWA98101
Seattle | WA | 98101
SeattleWA98101
The separator can be any length, including an empty string. Items come out in the same order as the array. If your values already live in an array, converting an array to a string goes deeper into casting and $OFS.
Why does -join ‘a’, ‘b’, ‘c’ print separate lines?
The unary -join grabs only the first value, because it binds more tightly than the comma. PowerShell joins 'Austin' by itself and then outputs the other two strings untouched:
'--- without parentheses:'
-join 'Austin', 'Denver', 'Boston'
'--- with parentheses:'
-join ('Austin', 'Denver', 'Boston')
Output:
--- without parentheses:
Austin
Denver
Boston
--- with parentheses:
AustinDenverBoston

Wrap the list in parentheses, or store it in a variable first, and the whole list is joined. Both versions behave the same way here.
Join strings with a comma, space or new line
The separator is just a string, so a comma with or without a space, a pipe or a line break all work:
$states = 'Texas', 'Ohio', 'Utah'
$states -join ','
$states -join ', '
$states -join [Environment]::NewLine
Output:
Texas,Ohio,Utah
Texas, Ohio, Utah
Texas
Ohio
Utah
[Environment]::NewLine gives you the line ending Windows expects. For other ways to add line breaks, see how to add a new line in a string.
Skip empty values before joining
-join doesn’t skip $null or empty strings. They become empty spots between separators, so filter them out first:
$names = 'Washington', $null, 'Lincoln', '', 'Roosevelt'
"With blanks: $($names -join ' | ')"
"Blanks removed: $(($names | Where-Object { $_ }) -join ' | ')"
Output:
With blanks: Washington | | Lincoln | | Roosevelt
Blanks removed: Washington | Lincoln | Roosevelt
Join the lines of a here-string
A here-string is a single string, so -join has nothing to join until you break it apart. Split it on whitespace, then join the pieces:
$list = @'
Austin
Denver
Boston
'@
(-split $list) -join ', '
Output:
Austin, Denver, Boston
The unary -split breaks on any whitespace. To split on something else, split a string on a specific character first.
Join numbers, objects and command output
Numbers are converted to text for you. Objects are a different story, as this test shows:
$team = [pscustomobject]@{ Name = 'Maria Lopez'; City = 'Austin' },
[pscustomobject]@{ Name = 'Tom Baker'; City = 'Denver' }
"Objects: [$($team -join '; ')]"
"Names: $($team.Name -join '; ')"
$labels = $team | ForEach-Object { "$($_.Name) ($($_.City))" }
"Formatted: $($labels -join '; ')"
"Numbers: $((1, 2, 3) -join ' + ')"
Output:
Objects: [; ]
Names: Maria Lopez; Tom Baker
Formatted: Maria Lopez (Austin); Tom Baker (Denver)
Numbers: 1 + 2 + 3
