Ever had a need to do something to every item in a collection that would change the collection? I don’t know let's say removing all SharePoint Site Collections under a managed path.
Now we all know that you can enumerate a collection and then loop through acting on each element but if you change the element in a way that’s meaningful to the enumeration, say adding or removing an element or changing its identifier then, quite rightly, the enumeration throws an error.
Now this is correct when developing a generic solution as it prevents a number of context specific bugs, i.e., I remove all the elements but another process adds one whilst I’m doing it so at the end I think my collection is empty but actually it's not. If this is important to you, then this technique is not correct, I’d suggest a while enumeration is not empty remove loop instead.
However, often when acting as an administrator, such concerns don’t apply, for example I’m the only one acting on the elements of the enumeration or I can always run it twice if needed. In this case, there is a well known solution we enumerate the collection and store the result as a variable of a suitable type, probably a list or array, and then act on the variable by enumerating it. As we are modifying the ‘copy’ of the references and not the elements themselves, we can happily add, edit and remove them because we are affecting the original elements and not the references we are now enumerating.
So how do we do this in PowerShell?
$Collection = Get-SPSite -Limit:All
This will get us a reference to all the site collections we can now act on, be careful though, this will remove all the site collections in your farm. For safety, I’ve put the somewhat confusing double negative confirm flag on so at least, you will be prompted before a site is deleted. If you want it to not prompt, you use -Confirm:$false
, i.e., do not ask me for confirmation.
$Collection |% { Remove-SPSite -Identity:$_ -Confirm:$true }
Lovely, in just two lines, we have acted on every site. We can reduce it to a single line but not as we would expect.
Get-SPSite -Limit:All |% { Remove-SPSite -Identity:$_ -Confirm:$true }
Instead, we have to cast our results to an array to force the enumeration to complete before passing it to our script block.
@(Get-SPSite -Limit:All) |% { Remove-SPSite -Identity:$_ -Confirm:$true }
Bish bosh, job’s a good’un!