Managing Microsoft Sentinel table retention and tiers is typically done through scripts or the portal, but neither approach fits well into an infrastructure-as-code workflow and it can be difficult to maintain tables at scale. While exploring the Log Analytics Bicep resource provider, I came across the Microsoft.OperationalInsights/workspaces/tables resource type1, which makes it possible to manage table tier and retention declaratively with Bicep: version-controlled, reviewable, and reproducible across environments.
For each table we typically want to control:
Table tier: Analytics or Data Lake (Auxiliary) Interactive retention: the hot, queryable period Total retention: the overall retention period of the table Typical Table Configuration Settings Managing the total retention per table is necessary whenever you want to keep logs longer than the 90-day default to satisfy logging or compliance requirements. Since there is no workspace-level default for total retention, it has to be configured table by table, a perfect fit for a declarative approach.
Identify tables with recent ingestion # To identify ‘active’ tables, both a KQL- and API-based approach exist.
KQL # The following KQL query lists tables that have received data in the last 90 days along with their current tier, which is helpful when deciding what to put under Bicep management:
We all have probably been there and developed a PowerShell script that took some fair amount of time until the execution completed, weren’t we? Of course one could argue and say that as long a script ‘works’ it is good enough but depending on the use case and environment a PowerShell script that runs 30 to 60 minutes exceeds the patience of most (IT) people and can also lead to increased costs. But what makes those kinds of scripts that awfully slow and can’t we just tweak them to run faster?
The following examples and script will be related to PowerShell and the Microsoft Graph API but the mentioned approaches can be adapted for any kind of RESTful API and scripting language. For all interactions with the Graph API I use the Microsoft Graph PowerShell SDK which is available as a PowerShell module from the PowerShell gallery.
Reasons why your script is slow # I would say I have already read and (tried to) understand hundreds of PowerShell scripts out there and I often notice the following flaws which lead to poor performance:
Slow algorithms and wrong data structures # Slow algorithms are extricably linked to the understanding of data structures when it comes to PowerShell scripting. Here the top two cases I often observe:
The Microsoft Store for Business will be discontinued mid 2023 and Intune recently introduced the new Windows Store experience backed by winget to distribute apps to your Intune managed endpoints.
To simplify the migration to the new Windows Store experience I created a PowerShell Script that migrates all currently assigned Windows Store for Business apps to the new Windows Store experience.
Kudos to Sander Rozemuller for providing detailed instructions about creating winget apps as PowerShell code samples.
Challenges # While scripting and extracting the existing Windows Store for Business (WSfB) apps I encountered the following issues:
Not all apps in WSfB have valid privacy and information URLs, therefore I added a check whether the URL starts with http(s). Some apps have characters present (äöüë….) that require UTF-8 encoding. So I explicitly set the HTTP content-type header to UTF8. Script prerequisites # To run the script you need to have the Microsoft Graph PowerShell SDK modules installed on your machine. You can install them with the following command:
Install-Module Microsoft.Graph.Authentication, Microsoft.Graph.Devices.CorporateManagement -Scope CurrentUser From a permissions perspective you need an Azure AD Application Administrator for the initial OAuth permission consent and for regular execution the Intune Administrator role.
Workload Identity Federation (let’s just call this WIF) allows app principals not residing within Azure to request short lived access tokens. This removes the need of storing client secrets or certificates within GitHub as Action secrets. One drawback ist that currently only the Azure modules support the usage of WIF.
How it works # WIF relies on trust. This trust is directly configured on the Azure AD app registration and scoped to an individual GitHub repository and optionally fine grained by limiting the usage to single git refs, specifically branches and tags. By trusting GitHub as external identity provider (IdP), a GitHub Action can request an identity token from the GitHub IdP and exchange this one against an access token within Azure AD.
A GitHub IdP issued token (decoded) contains the following information:
{ "jti": "1a9fe224-c936-46f6-a38b-3300e76251b0", "sub": "repo:nicolonsky/musical-octo-telegram:ref:refs/heads/main", "aud": "api://AzureADTokenExchange", "ref": "refs/heads/main", "sha": "9f112c0b82547e35b10250d7f3165789bf97862f", "repository": "nicolonsky/musical-octo-telegram", "repository_owner": "nicolonsky", "repository_owner_id": "32899754", "run_id": "3986560796", "run_number": "2", "run_attempt": "2", "repository_visibility": "private", "repository_id": "592303002", "actor_id": "32899754", "actor": "nicolonsky", "workflow": ".github/workflows/wif.yaml", "head_ref": "", "base_ref": "", "event_name": "push", "ref_type": "branch", "workflow_ref": "nicolonsky/musical-octo-telegram/.github/workflows/wif.yaml@refs/heads/main", "workflow_sha": "9f112c0b82547e35b10250d7f3165789bf97862f", "job_workflow_ref": "nicolonsky/musical-octo-telegram/.github/workflows/wif.yaml@refs/heads/main", "job_workflow_sha": "9f112c0b82547e35b10250d7f3165789bf97862f", "iss": "https://token.actions.githubusercontent.com", "nbf": 1674486850, "exp": 1674487750, "iat": 1674487450 } Did you recognize the matching sub attribute within the GitHub issued token and the Azure AD configuration? For a successful authentication the:
Based on one of my older posts about PowerShell script signing with Azure DevOps I recently implemented a PowerShell script signing workflow with GitHub actions I wanted to share with the community.
Prerequisites # For this post the following prerequisites are required:
Code signing certificate in PFX format GitHub account Add variables to GitHub actions # Because GitHub variables can only be of string content we need to get the contents of the pfx file as base64 encoded string:
$pfxCertFilePath = "~\Downloads\CodeSigningCertificate.pfx" $pfxContent = Get-Content $pfxCertFilePath -Encoding Byte [System.Convert]::ToBase64String($pfxContent) | Set-Clipboard GitHub action variables # Add two variables as actions secrets:
BASE64_PFX: Base 64 encoded string of the PFX (automatically copied into your clipboard with the above commands) PFX_PASSWORD: Password for the private key of the pfx file
GitHub action workflow # Place the following workflow file within your git repository: .github/workflows/SignPowerShell.yaml whereas the name of the YAML file can be freely chosen.
The Send-MailMessage cmdlet has been around for a couple of years and is mostly used to send email messages from PowerShell. But with the deprecation and security flaws of legacy authentication it’s time for a better option which actually supports modern authentication. For this purpose we can use the Microsoft Graph API and the Microsoft Graph PowerShell SDK. The best thing is that this solution works without any service account and does not need any exclusions from conditional access.
Microsoft Graph resource # To send a mail we simply specify the user account from which we want to send the email:
POST: https://graph.microsoft.com/v1.0/users/sean.connery@dev.nicolonsky.ch/sendMail Create an app registration # Simply create a new app registration with the Mail.Send permissions and use a certificate for the authentication.
We need to take additional steps to limit the permissions of the app registration. Otherwise the app can send mails on behalf of any user in your tenant. To limit the permissions we leverage exchange application access policies.
Connect to Exchange Online with the ExchangeOnlineManagement PowerShell module Connect-ExchangeOnline
While fine-tuning and adjusting applocker policies for co-managed Windows 10 clients I got really annoyed by special characters commonly used in the German/Swiss language. The Intune portal seemed to use different encoding and didn’t allow me to just copy/paste the currently deployed policy and extend it with a new rule. I needed to request the original file that was uploaded to the tenant in order to adjust the rule. Instead of just accepting this I decided that it is time for an easier approach which I will share with you.
The actual issue # After uploading the XML with the applocker policies (in my case EXE rules), special characters like ‘ö’ or ‘ü’ have a weird encoding displayed in the portal. The next person that wants to edit the policy needs to take care to fix the unrecognized characters otherwise the publisher rule won’t work anymore.
Top: Portal view of the special characters, bottom: original file.
Fixing things # Save file with UTF-8 encoding # First, make sure that you saved an uploaded the file with UTF-8 encoding as this introduces support for most character sets. You can do this by selecting the encoding box in the right bottom of Visual Studio Code:
When working with the Microsoft Graph API or introducing the API to colleagues I often get asked about the steps required to obtain an access token for the API with PowerShell. Out in the wild, I’ve spotted many different ways and lots of implementations still relying on the ADAL (Active Directory Authentication Library) despite the fact that this client library is superseded by MSAL (Microsoft Authentication Library). So let’s talk about acquiring access token “in stile” with the most simple method available.
Why do we need an access token? # When talking about the Microsoft Graph API an access token fulfills two roles, first: prove authentication (proof of identity) second prove authorization (permissions). Each request needs to submit a request-header that contains the access token.
For an API it’s crucial to validate the authentication and authorization for every request. Otherwise, requests could be made to resources the actor has no access to.
What’s inside the access token # You did probably stumble over the terms “bearer authentication” or “bearer token” these describe a mechanism within the OAuth 2.0 Authorization framework to authenticate requests with access tokens. Tokens are issued by the authorization server (Azure AD) and contain a server-generated string in the format of a JSON Web Token (JWT) with the following information (the list is not exhaustive and truncated to only contain the most interesting parts):
When involved in new projects I often find a bunch of old profiles in the Microsoft Endpoint Management Console. Before going ahead with a new implementation it’s the best time to clean-up all the leftovers from past ramblings.
How to identify stale profiles # If one or multiple statements are met for a profile it is very likely to be a stale profile:
No assignments, assignments to a group without members “Test” included within the profile name or description Last modified points back in time for more than a year No devices reported success/failure status for the given profile type What to do with stale profiles # So let’s be brave and delete them. But Intune doesn’t offer any [CTRL] + [Z] or recycle bin possibilities so we might want to have some kind of archive, just in case?
Let’s agree that we:
Check the points from the list above Ask our colleagues if they know something about the profiles and their usage Take a backup deleting them afterward is a reasonable action which is probably beneficial for everyone.
Recently I got a DM on Twitter with a question about how to export and import Endpoint Security profiles with Microsoft Graph. Besides a technical answer which might be of interest for you, I’d like to show you the workflow I used to give a proper reply.
Original question:
Hi @nicolonsky, I was advised on the MS Elite Partner focus groups team (MEM Automation) to reach out to you regarding my question about export/import policies from Endpoint Security in Intune. I’ve been able to export the Disk Encryption policy (via graph explorer), but haven’t been able to find the correct format to use to upload/import it. I was hoping that you would be able to advise on how to go about achieving this.
Workflow # Discover request URL’s and payload # To discover the request URLs and payloads I used the methodology I explained in the this post a while ago. Basically, I tracked the network activity and used a filter to only include requests made to the Graph API while doing the following activities: