Azure Automation and Function Apps offer server-less script execution.
This post explains key differences between Azure Automation and Function Apps, concludes with an opinion on the preferred choice for a Scheduled Task replacement.
Comparing site by site
Usage / Use cases
Both allow the execution of scheduled jobs which themselves are based on scripts. PowerShell is supported on both options.
Azure Automation is triggered either by time or via web-hook. The “result” is like when working with Scheduled tasks. From file-operations to changes in the Azure infrastructure or any other job which is deterministic.
Function Apps also allow time trigger, but also offer a eventgrid integration and some native events, like for instance “on new file in storage account”. Also they can expose web endpoint. That means you could pass in parameters via HTTP Post or in the Query string and get result as JSON for example. There is also the concept of “durable functions” which more or less means that there are always ready to deliver results.
Audience
Azure Automation are targeting IT-Pros, formerly known as System Administrators.
Function Apps on the other hand are more developer focused. SREs or DevOps are also included here.
Manageability
Azure Automation uses “Accounts” as container objects for RunBooks. Settings that apply to all RunBooks such as Environment-Variables, Secrets or shared PowerShell modules.
Activating “Managed Identity” allows granting permissions on other Azure services like for instance allowing starting and stopping of VMs.
A RunBook itself is as dedicated Azure object that contains the script which may bound to a schedule and the execution choice.
Function Apps require an App Server Plan as foundation. The plan defines supported frameworks and dictates the costs. On the other hand, it allows vertical and horizontal scaling!
Dependencies and other settings are defined in *.json files and require little research.
Typically, only one script is stored on a Function App. Multiple scripts are possible but require attention when maintaining them in the different folder structures.
Complexity Level
Azure Automation in general is “simpler” in setting up and maintaining. Mostly because multiple jobs can be managed from one console. Another important aspect is that many aspects configurable via graphical user interface.
Once figured out how they work, using them is convenient.
Function Apps require more considerations when setting up and while maintaining. Most aspects must be done via configuration files. Setting up a schedule for instance requires knowledge of Cron syntax. In my experience PowerShell module dependencies are regular problematic. It is advisable to only import those which are essential for the script to work.
Mastering them have a steeper learning curve.
Various Differences
Azure Automation know the concept of “Hybrid Worker”. Behind that term are ARC enabled Windows Servers which allow the execution of scripts on them. This allows access to resources within the datacenter / the active directory / the private cloud with benefiting for all advantages that Azure technologies brings.
GIT integration with bidirectional sync. – Deployment pipeline managed automatically.
Function Apps offer a rich set of integrations with Azure PaaS services. As they run within the context of an App Service Plan there are horizontal and vertical scaling options available to overcome also very demanding jobs.
Supported languages: PowerShell, C#, NodeJS, Python and more.
GIT integration with bidirectional sync and support for complex pipelines within Azure DevOps allow also sophisticated scenarios.
Conclusion
In my opinion is Azure Automation the best fit to be the better alternative to tradition Scheduled Task. Version Control, Change-Tracking, Security, Monitoring, Redundancy options and much more are reason for it.
Scheduled Tasks have been around since the early days of Windows Server. The task scheduler is the inbuilt component used to plan and execute jobs. Typically, System administrators use them to automate reoccurring activities which are defined in scripts.
While Scheduled Tasks are robust and easy to use, they have a couple of shortcomings.
This blog explains the advantages of Azure Automation or Function App over Scheduled tasks.
Looking on Scheduled Tasks and their opponents in Azure
Storage / Location
Scheduled Tasks are stored on an individual computer. – If the computer requires replacement or decommissioning, the task needs to be migrated.
Azure Services live in the cloud and have no fixed relation to a computer.
Execution
Scheduled Tasks are executed on an individual computer. – In case the computer is unavailable, the task will not run.
Azure Services run typically in the cloud OR on designated computers.
Redundancy
Scheduled Tasks have no redundancy options out of the box. – They are typically stored only on 1 computer.
Azure Services provide different options for been redundant. Even if execution is on computers.
Change Tracking / Versioning
Scheduled Tasks are changed on demand without direct support of managing the change properly.
Azure Services can be connected to a Version Control System, e.g. Azure DevOps and allow tracking on changes and versioning via underlying GIT protocol.
Monitoring
Scheduled Tasks can be monitored on a the individual computer. For centralized monitoring the use of SCOM or an alternative Server-Monitoring system is required.
Azure Services can send diagnostic and performance data via single click to a central Log Analytics Workspace. Kusto queries allow monitoring and alerting.
Security
Scheduled Task run typically under the context of “System” which has the highest level of permissions on the individual server. By using the computer object, permissions on remote locations, like file-shares or others can be granted.
In cases a dedicated User account is created, has to be granted sufficient permissions on the computer to run the task. Typically the password is stored and never changed.
In worst cases, the credentials are stored in plain text within the script.
Azure Services can leverage a “managed identity” which password is neither exposed nor it requires attention. Furthermore, if credentials for different operations within the script are needed, the managed identity can be granted access to a Key Vault to obtain secret information securely.
Costs
Scheduled Tasks are part of the Windows operating system and do not come with extra costs or licenses.
Azure Services offer free tiers or are charged for minimal wages.
Round Up
To conclude, Azure Runbooks and Function Apps outperform the traditional Scheduled Tasks in many aspects.
When Azure AD is configured to record Sign-In activity, #Kusto can be used to gain valuable insights. This blog walks through common needs and shows how to visualize them in #SquaredUp.
Introduction
Having Azure AD as identity provider offers convenient Single Sign On experience for users and increased security due to MFA and other identity protection features.
Enabling auditing and storing the results in a Log Analytics Workplace allows detailed analysis about application usage, sign-in experience, user behavior and overseeing guest activity in your tenant.
Shortly after enabling of logging, events are logged in the SigninLogs table. – Nearly all queries in this blog are against this table.
Links about learning KQL can be found in the appendix. – Particular questions about code will be answered. – Also suggestions for better queries are appreciated! 😉
Configuration & Code
In the remaining, most of the visualizations will be explained in detail. Queries are written in #KQL and which finalizes this article.
Unique SignIns Total
This donut diagram shows the proportion of between Guests and Members ( here called Employees ) with concrete numbers. Each Guest or Member login is only count once.
SigninLogs
| where TimeGenerated between (startofday(ago (7d)) .. now())
| where ResultType == 0
| where UserPrincipalName matches regex @"\w+@\w+\.\w+"
| extend UserLoginType = iif(UserType == "Member","Employees","Guests")
| project UserLoginType, UserPrincipalName
| summarize dcount(UserPrincipalName) by UserLoginType
Azure -Log Analytics (Donut) is the best fit here.
Unique Sign Ins over Time
This diagram shows Guests and Members ( here Employees ) sign in count, summarized by day. Each day counts individually.
SigninLogs
| where TimeGenerated between (startofday(ago (7d)) .. now())
| where ResultType == 0
| where UserPrincipalName matches regex @"\w+@\w+\.\w+"
| extend UserLoginType = iif(UserType == "Member","Employee","Guest")
| project TimeGenerated, UserLoginType,UserPrincipalName
| summarize Employees = dcountif(UserPrincipalName,UserLoginType=="Employee"), Guests = dcountif(UserPrincipalName,UserLoginType=="Guest") by bin(TimeGenerated, 1d)
Azure – Log Analytics (Line Graph) is the choice here.
Operating Systems
Used operating systems are mostly correct identified and show clearly from where Azure AD applications are consumed.
Azure – Log Analytics (Bar Graph) is picked for this visualization.
Password Issues
Users failing to login due to password issues or other are shown here. Only the last day is considered in the queries.
For the donut, use the following KQL query:
SigninLogs
| where TimeGenerated between (ago(1d) .. now())
| where ResultType in(50144,50133,50126,50053)
| where UserPrincipalName matches regex @"\w+@\w+\.\w+"
| summarize arg_max(TimeGenerated, *) by UserPrincipalName
| extend IssueType = case (ResultType == 50126 , "Invalid username or bad password"
, ResultType == 50133, "Session invalid due to recent password change"
, ResultType == 50144, "Password expired"
, ResultType == 50133, "Account locked"
, "Unknown"
)
| where IssueType !in("Unknown","Session invalid due to recent password change","Invalid username or bad password")
| extend readableDate = format_datetime(TimeGenerated,"yyyy-MM-dd HH:mm")
| summarize Users = dcount(UserPrincipalName) by IssueType
The table overview is realized with the lines below
SigninLogs
| where TimeGenerated between (ago(1d) .. now())
| where ResultType in(50144,50133,50126,50053)
| where UserPrincipalName matches regex @"\w+@\w+\.\w+"
| summarize arg_max(TimeGenerated, *) by UserPrincipalName
| extend IssueType = case (ResultType == 50126 , "Invalid username or bad password"
, ResultType == 50133, "Session invalid due to recent password change"
, ResultType == 50144, "Password expired"
, ResultType == 50133, "Account locked"
, "Unknown"
)
| where IssueType !in("Unknown","Session invalid due to recent password change","Invalid username or bad password")
| extend readableDate = format_datetime(TimeGenerated,"yyyy-MM-dd HH:mm")
| extend Day = format_datetime(TimeGenerated,"yyyy-MM-dd")
| extend Time = format_datetime(TimeGenerated,"HH:mm")
| summarize by IssueType, readableDate, UserDisplayName,UserID=onPremisesSamAccountName, Day, Time
Risky Sign-ins
One of Azure ADs most famous protection features is Risky Sign-Ins. An algorithm here checks for possible malicious sign in attempts that occur when credential theft occurred.
AADUserRiskEvents is the table which stores this information.
AADUserRiskEvents
| where TimeGenerated between (ago(1d) .. now())
| where RiskState != "dismissed"
| where RiskState != "remediated"
| extend readableDate = format_datetime(TimeGenerated,"yyyy-MM-dd HH:mm")
| extend Day = format_datetime(TimeGenerated,"yyyy-MM-dd")
| extend Time = format_datetime(TimeGenerated,"HH:mm")
| summarize arg_max(TimeGenerated, *) by UserPrincipalName
| project User = replace_string(UserPrincipalName,"@mydomain.com",""), readableDate, RiskLevel, RiskEventType, RiskState, tostring(Location.city), Day, Time
Azure – Log Analytics ( grid ) is used for the table. Conditional formatting help to spot most serious events.
MFA Successful Sign Ins
Details about usage and preference of MFA can be obtained from the Sign-In logs.
SigninLogs
| where TimeGenerated between (startofday(ago(7d)) .. now())
| where ResultType == 0 and ConditionalAccessStatus == 'success' and Status.additionalDetails == "MFA completed in Azure AD" and ConditionalAccessPolicies[0].result == "success" and parse_json(tostring(ConditionalAccessPolicies[0].enforcedGrantControls))[0] == "Mfa"
| where UserType == "Member"
| project Identity, MFAType = iif(isempty(tostring(MfaDetail.authMethod)),"unknown",tostring(MfaDetail.authMethod))
| summarize TotalUsers = dcount(Identity) by MFAType
| sort by TotalUsers desc
Top 5 Non-MS Applications
Usage trends of applications can be retrieved. Microsoft recently released a website which enumerates many of its applications. – Unfortunately, not all and as a static website.