Writing serverless Azure Functions which run PowerShell scripts is one of the most versatile and productive way to handle automation tasks when working with Office 365. You can easily use different modules such as SharePoint Patterns and Practices PowerShell, Azure Active Directory PowerShell for Graph or Microsoft Teams PowerShell Module. However, sometimes things go wrong (especially with the beta version of Teams commandlets) and Azure Function logging for PowerShell leaves quite a lot to hope for. In theory, it would be possible to use Application Insights, but frankly I think it is an overkill and quite difficult to use from PowerShell.
The main problem with the out-of-the-box logging is that unless you are actually monitoring the log stream, it is hard to find out what went wrong because log information is often truncated (even if you use Kudu tools WebJobs logging features). To overcome this problem I created a simple PowerShell logging solution for Azure Functions. The solution is one PowerShell Module which includes just a few PowerShell functions to write log files to a folder of your choice.
Add simple logging solution for your Azure Function
You can add this logging solution to your Azure Function in a matter of minutes by following these simple steps.
Step 1: Create folders for log files
First open your Azure Function and click Platform features. Then select Advanced tools (Kudu) which is located under Development tools. From the top navigation bar, select Debug console and CMD. Open folder site and then wwwroot. Click plus icon to add new folder named logs and then create subfolder called log under the folder you just created. Also create any number of folders for each function you want to log under the logs folder.
Step 2: Upload Simple.PowerShell.Logging.psm1
Next upload Simple.PowerShell.Logging.psm1 to logs folder.
Step 3: Configure your Azure Function to use simple logging solution
Next open your Azure Function script view and insert the following line of PowerShell.
New-LogFile -LogFolder "D:\home\site\wwwroot\logs\[your_function_name]" -FileNamePart "HelloWorld"
You don't need to use the two parameters but you can if you like. If LogFolder paramter is omitted, the module will use "D:\home\site\wwwroot\logs\log" as log folder - that's why we created that folder in step 1. If FileNamePart is omitted, it is simply an empty string. The name of the log file will be [timestamp]<-FileNamePart>.log. I use this parameter to specify something unique such as alias of the Office 365 Group I'm using, as it makes it easy to find the correct log file. If you use FileNamePart parameter, ensure that the parameter value only includes characters that can be part of filename.
Finally replace all Write-Output commandlet names in your Azure Function with Add-LogFileMessage and you are done. Note, that if you happen to call Add-LogFileMessage before you call New-LogFile, it will automatically call New-LogFile with no parameters.
How does it work?
Hopefully well, especially since I can't really offer you any warranty what so ever to actually guarantee that it works at all. But here is how it works on my machine. :)
New-LogFile initializes full path to the log file and removes old log files
This function will set up everything up and ready for Add-LogFileMessage function, but it doesn't actually create the log file.
In addition, it looks into log file folder and deletes all log files older than 30 days. If this is too much or too little for you, you can tweak Days parameter in Simple.PowerShell.Logging.psm1 to suite your needs.
After New-LogFile you can call Get-LogFilePath function to get string value with full path to the log file, if you need that information.
Add-LogFileMessage writes any errors and a message to the log file
First of all Add-LogFileMessage calls New-LogFile (without parameters - thus writing to "D:\home\site\wwwroot\logs\log" folder) if it doesn't have full path to the log file.
Then it compares internal error counter (initialized to 0 when module is imported) and $Error.Count value. If they are the same it moves on, but if there are errors, those errors get written into the log file. After that the internal error counter is set to $Error.Count so than on next time we only write new errors to the log file.
If you call $Error.Clear() inside your function, you need immediately call Reset-ErrorCounter function so that internal error counter is set to value of $Error.Count and everything will keep on working.
I recommend that your Azure Function ends with final Add-LogFileMessage with a message that we are all done, so that any errors that have not been logged will be written into the log file.
That's it. I hope that this solution saves your time when you are troubleshooting your Azure Function PowerShell scripts!