-
Notifications
You must be signed in to change notification settings - Fork 21
Expand file tree
/
Copy pathTimer-Remove-PrimaryUser.ps1
More file actions
160 lines (135 loc) · 7.62 KB
/
Timer-Remove-PrimaryUser.ps1
File metadata and controls
160 lines (135 loc) · 7.62 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# This Azure Function is designed to remove the primary user from Intune-managed devices that are members of specific Entra ID groups and match certain criteria.
# The function authenticates using the Function App's managed identity to call Microsoft Graph API, retrieves devices from specified Entra ID groups, checks for devices with names containing "MVP" and enrolled in Intune, and then removes the primary user association for those devices in Intune.
# Note: This function uses the Microsoft Graph API beta endpoint, which may be subject to change. Ensure you have the necessary permissions assigned to the Function App's managed identity to read group memberships and manage Intune devices.
# Input: Timer trigger
# Output: JSON response with the results of the operation for each processed device.
# Remember change your Entra Group ID in the $EntraGroupIds variable before running the function and change your device name filter in the if condition to target the correct devices. The current filter is set to look for devices with "MVP" in the name.
# Author: Sandy Zeng (@sandytsang)
# Input bindings are passed in via param block.
param($Timer)
# Get the current universal time in the default string format.
$currentUTCtime = (Get-Date).ToUniversalTime()
# The 'IsPastDue' property is 'true' when the current function invocation is later than scheduled.
if ($Timer.IsPastDue) {
Write-Host "PowerShell timer is running late!"
}
# Write an information log with the current time.
Write-Host "PowerShell timer trigger function ran! TIME: $currentUTCtime"
function Get-ManagedIdentityToken {
[CmdletBinding()]
param (
[string]$ResourceURI = "https://graph.microsoft.com"
)
Process {
# Determine which managed identity endpoint is available
if ($env:IDENTITY_ENDPOINT -and $env:IDENTITY_HEADER) {
$Endpoint = ($env:IDENTITY_ENDPOINT).Trim()
$Secret = $env:IDENTITY_HEADER
$HeaderName = "X-IDENTITY-HEADER"
$APIVersion = "2019-08-01"
}
elseif ($env:MSI_ENDPOINT -and $env:MSI_SECRET) {
$Endpoint = ($env:MSI_ENDPOINT).Trim()
$Secret = $env:MSI_SECRET
$HeaderName = "Secret"
$APIVersion = "2017-09-01"
}
else {
throw "No managed identity endpoint found. Ensure the Function App has a system-assigned managed identity enabled."
}
# Validate the endpoint URI
$TestUri = $null
if (-not [System.Uri]::TryCreate($Endpoint, [System.UriKind]::Absolute, [ref]$TestUri)) {
throw "Managed identity endpoint is not a valid URI: '$Endpoint'"
}
# Build the token request URI
$Separator = if ($Endpoint.Contains("?")) { "&" } else { "?" }
$AuthURI = "${Endpoint}${Separator}resource=${ResourceURI}&api-version=${APIVersion}"
$Response = Invoke-RestMethod -Uri $AuthURI -Method Get -Headers @{ $HeaderName = $Secret } -ErrorAction Stop
# Return auth header
return @{
"Authorization" = "Bearer $($Response.access_token)"
"Content-Type" = "application/json"
}
}
}
# Retrieve authentication token
try {
$Script:AuthToken = Get-ManagedIdentityToken
}
catch {
Write-Error "Authentication failed: $($_.Exception.Message)"
return
}
# Entra Group Ids to process - consider moving to app settings
$EntraGroupIds = @(
"07c0ae23-09b1-45ce-9f74-e63096a13c64" #Change this to your Entra Group ID
)
$Results = [System.Collections.Generic.List[object]]::new()
foreach ($EntraGroupId in $EntraGroupIds) {
Write-Information "Processing Entra Group Id: $EntraGroupId"
$url = "https://graph.microsoft.com/beta/groups/$EntraGroupId/members?`$select=id,deviceId,displayName,enrollmentType,mdmAppId"
Write-Information "Getting members of Entra Group Id: $EntraGroupId"
$GroupMembers = [System.Collections.Generic.List[object]]::new()
try {
do {
$Response = Invoke-RestMethod -Method Get -Uri $url -Headers $Script:AuthToken -ErrorAction Stop
if ($Response.value) {
$GroupMembers.AddRange([object[]]$Response.value)
}
$url = $Response.'@odata.nextLink'
} while ($url)
}
catch {
Write-Error "Failed to retrieve group members for group $EntraGroupId : $($_.Exception.Message)"
$Results.Add(@{ GroupId = $EntraGroupId; Error = "Failed to retrieve members: $($_.Exception.Message)" })
continue
}
Write-Information "Total members retrieved: $($GroupMembers.Count)"
foreach ($GroupMember in $GroupMembers) {
$DeviceId = $GroupMember.deviceId
$DeviceName = $GroupMember.displayName
$JoinType = $GroupMember.enrollmentType
$mdmAppId = $GroupMember.mdmAppId
Write-Information "Processing Device: $DeviceName with DeviceId: $DeviceId, JoinType: $JoinType, MDM AppId: $mdmAppId"
if ($DeviceName -like "*MVP*" -and $mdmAppId -eq '0000000a-0000-0000-c000-000000000000') { #Check for device name containing your own device name and enrolled in Intune (mdmAppId for Intune is 0000000a-0000-0000-c000-000000000000)
# Get Intune managed device Id
$intuneUrl = "https://graph.microsoft.com/beta/deviceManagement/manageddevices?`$filter=azureADDeviceId eq '$DeviceId'&`$select=id"
Write-Information "Getting Intune Device Id for device: $DeviceName"
try {
$IntuneResponse = Invoke-RestMethod -Method Get -Uri $intuneUrl -Headers $Script:AuthToken -ErrorAction Stop
$IntuneDeviceId = $IntuneResponse.value[0].id
}
catch {
Write-Error "Failed to retrieve Intune device Id for $DeviceName ($DeviceId): $($_.Exception.Message)"
$Results.Add(@{ Device = $DeviceName; DeviceId = $DeviceId; Status = "Failed"; Error = "Intune lookup failed: $($_.Exception.Message)" })
continue
}
if (-not $IntuneDeviceId) {
Write-Warning "Device $DeviceName ($DeviceId) not found in Intune, skipping."
$Results.Add(@{ Device = $DeviceName; DeviceId = $DeviceId; Status = "Skipped"; Error = "Not found in Intune" })
continue
}
Write-Information "Intune Device Id is: $IntuneDeviceId"
# Remove primary user
$deleteUrl = "https://graph.microsoft.com/beta/deviceManagement/managedDevices('$IntuneDeviceId')/users/`$ref"
Write-Information "Removing Primary User for Intune Device: $IntuneDeviceId"
try {
Invoke-RestMethod -Method Delete -Uri $deleteUrl -Headers $Script:AuthToken -ErrorAction Stop
Write-Information "Primary user removed successfully for device: $DeviceName ($IntuneDeviceId)"
$Results.Add(@{ Device = $DeviceName; DeviceId = $DeviceId; IntuneDeviceId = $IntuneDeviceId; Status = "Success" })
}
catch {
$StatusCode = $_.Exception.Response.StatusCode
Write-Error "Failed to remove primary user for $DeviceName ($IntuneDeviceId). Status: $StatusCode. Error: $($_.Exception.Message)"
$Results.Add(@{ Device = $DeviceName; DeviceId = $DeviceId; IntuneDeviceId = $IntuneDeviceId; Status = "Failed"; Error = "Delete failed ($StatusCode): $($_.Exception.Message)" })
}
}
else {
Write-Information "Device: $DeviceName is not in correct device name filter or not enrolled in Intune."
}
}
}
# Log results summary
Write-Host "Processing complete. Total results: $($Results.Count)"
$Results | ForEach-Object { Write-Host ($_ | ConvertTo-Json -Compress) }