There are many ways to iterate a collection in PowerShell. I just really like using delegate functions. This approach is not native PowerShell but utilises the .NET Action class as a function parameter. Using a delegate function approach, it is possible to create a recursive loop that can be very easily reused in the future just by providing an alternative Action.
The example code I provide below demonstrates how to create a delegate function in PowerShell, how to write a function that accepts one as a parameter, and provides some ready made samples for iterating SharePoint objects, specifically all webs or all lists. I am using some specific SharePoint objects in these samples, however the fundamental pattern can be used to effectively iterate any recursive structure.
foreachDecendentWeb : perform an action on every web below the provided web foreachListInWeb : perform an action on every list in the provided web foreachListInWebAndAllDecendentWebs : perform an action on every list in the current and all decendent webs
The below script references ‘TopOfScript.ps1’, it is specifically related to calling SharePoint CSOM from PowerShell. Read about it here on sharepointnutsandbolts.
Making the call, providing the delegate
The utility scipts, recursive functions accepting delegate parameters
Infrastructure contacted me to complain that one of our SharePoint environments was logging too much data (via the ULS) and it was becoming unmanageable (an Operations Management tool like SCOM has not been configured). Looking through many gigabytes of text, even with a free tool like ULSViewer, it is difficult to be confident that you are correctly identifying the most common issues, it is an inaccurate art at best.
That is why I wrote a log analyser as a PowerShell script which will process ULS log files and, using fuzzy comparison, create a report of the most frequently occurring log entries.
I am very well aware that this is not necessarily useful information in many cases (hence I had to write this script myself). Nevertheless I found it useful in my scenario and I hope that some of you may as well.
Just in case you are interested: using this script I was able to declare with certainty that logging by the SPMonitoredScope class made up almost 30% of total log entries. This will be reduced by explicitly stating the log severity in the class constructor as verbose and maintaining a log level of Medium for the SharePoint Foundation : Monitoring diagnostic logging category.
A few things of note:
You may want to add to or remove from the set of replace statements in order to increase/decrease the ‘fuzziness’ of the comparison. Adding a replace statement for removing URLs may be a good candidate if you wish to increase matches.
The script loads entire files into memory at once. Be aware of this if you have very large log files or not much RAM.
The output file is a CSV, open it with Excel.
By default the script will identify and analyse all *.log files in the current directory.
If you cancel the script during processing (ctrl+c) it will still write all processed data up until the point at which it was cancelled.
I quite enjoy PowerShell-ing so expect to see more utilities in the future.
There is a web.config file under the _layouts folder. This is worth being aware of as you may notice that files that you access from the _layouts folder may not behave as you would expect. This came to my attention when I was getting the following error:
”To enable the details of this specific error message to be viewable on remote machines, please create a tag within a “web.config” configuration file located in the root directory of the current web application. This tag should then have its “mode” attribute set to “Off”.
This initially surprised me as I knew that I had already done as such and had recently seen the default error screens when I was building web parts on content pages.
As the web.config under the layouts folder is deeper in the site hierarchy, any elements defined here will overwrite those defined at the web application level. By default the customErrors element exists at this level and it must be set accordingly to your needs as well.
As always, be careful when editing web.config files and always make a backup beforehand.