The PowerShell equivalent of your foo.bat file is a foo.ps1 file with the following content:
# Passes all arguments received by this script to bar.exe
bar.exe @args
PowerShell exposes all (unbound) positional arguments[1] as an array stored in the automatic $args variable.
By prefixing $args with @ instead of $, argument splatting is employed, which means that the elements of the $args array are passed as individual arguments to bar.exe
- Note: This isn't strictly necessary when calling external programs (such as
bar.exe) - $args would work there too - but is more versatile in that it can also pass named arguments correctly through to other PowerShell commands, which typically have declared parameters that can be bound by name (e.g., -Path C:\temp to bind value C:\temp to declared parameter -Path)
As for working with $args in general:
$args.Count tells you how many (unbound) positional arguments were passed,
$args[0] returns the first such argument, $args[1] the second, and so on.
However, it is usually preferable to formally declare parameters in PowerShell scripts and functions, which can then also be bound by name (e.g., -FooParam foo instead of just foo). See this answer for more information.
[1] If your script doesn't formally declare any parameters (via a param(...) block - see about_Scripts and the linked answer), all arguments are by definition unbound (i.e. not mapped to a declared parameter) and positional (not prefixed by a target parameter name). However, if your script does declare parameters, $args only contains those arguments, if any, that were passed in addition to those binding to declared parameters. If your script is an advanced script (or function), $args isn't supported at all, because passing unbound arguments is then categorically prevented. See this answer for more information.