You can use LINQ (adapted from this C# answer):
$myarray = 'herp', 'dederp', 'dedoo', 'flerp'
$substring = 'erp'
[int[]] $indices = [Linq.Enumerable]::Range(0, $myarray.Count).
Where({ param($i) $myarray[$i] -match $substring })
$indices receives 0, 1, 3.
As for what you tried:
$thisiswrong = @($myarray.IndexOf($substring))
System.Array.IndexOf only ever finds one index and matches entire elements, literally and case-sensitively in the case of strings.
There's a more PowerShell-like, but much slower alternative, as hinted at by js2010's answer; you can take advantage of the fact that the match-information objects that Select-String outputs have a .LineNumber property, reflecting the 1-based index of the position in the input collection - even if the input doesn't come from a file:
$myarray = 'herp', 'dederp', 'dedoo', 'flerp'
$substring = 'erp'
[int[]] $indices =
($myarray | Select-String $substring).ForEach({ $_.LineNumber - 1 })
Note the need to subtract 1 from each .LineNumber value to get the 0-based array indices, and the use of the .ForEach() array method, which performs better than the ForEach-Object cmdlet.
#13772idea, it would be something like:$indices = $myarray | Foreach-Object { if ($_ -match $substring) { $PSIndex } }