├── AccessPackages
├── LA-001-Create-TemporaryAccessPass.json
└── Set-ManagedIdentityPermission.ps1
├── AccessReviews
└── Enable_Access_Reviews_On_Scale_M365_Groups.ps1
├── AdminLifecycleManagement
├── Configure-MsGraphPermissions.ps1
├── Configure-MsGraphPermissions_AdminAccount_Offboarding.ps1
├── Configure-MsGraphPermissions_AdminAccount_PostOffboarding.ps1
├── LA-099-Request-Admin-Account.json
├── LA-AdminAccount-Offboarding.json
└── LA-AdminAccount-PostOffboarding.json
├── AzureAD-GroupWriteBackV2.ps1
├── Configure-AuthenticationMethods.ps1
├── Configure-AuthenticationMethods_including_token_request.ps1
├── EntraPrivateAccess
└── AppDiscoveryWorkbook.json
├── Guest-IdentityLifecycleManagement.ps1
├── IdentityLifecycleManagement
├── RB-001-TransformHRUserInputforAPI.ps1
└── la-001-identity-provisioning-api.json
├── LifecycleWorkflows
├── RB-100-EnableAccountAndMbxInAD.ps1
├── RB-400-ConvertMailboxEXO.ps1
├── RB-400-DisableAccountAndConvertMbxInAD.ps1
└── RB-500-DeleteAccountInAD.ps1
├── NordicVirtualSummit
├── LA-99-NordicsVirtualSummit
├── RB-99-NordicsVirtualSummit-Saml
├── RB-99-NordicsVirtualSummit-Saml.ps1
└── RB-99-NordicsVirtualSummit.ps1
├── Pre-ConfigureAuthMethods V2
├── AuthMethodsImport.csv
└── Pre-ConfigureAuthMethods.ps1
└── WPNinjas-Demo-GuestLifeCycle
/AccessPackages/LA-001-Create-TemporaryAccessPass.json:
--------------------------------------------------------------------------------
1 | {
2 | "definition": {
3 | "$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#",
4 | "actions": {
5 | "Condition": {
6 | "actions": {
7 | "Condition_2": {
8 | "actions": {},
9 | "expression": {
10 | "and": [
11 | {
12 | "equals": [
13 | "@triggerBody()?['Stage']",
14 | "CustomExtensionConnectionTest"
15 | ]
16 | }
17 | ]
18 | },
19 | "runAfter": {},
20 | "type": "If"
21 | }
22 | },
23 | "expression": {
24 | "and": [
25 | {
26 | "equals": [
27 | "@{triggerBody()?['AccessPackageCatalog']?['Id']}",
28 | "3287d6ec-504d-4237-a0b5-20ff79d1767d"
29 | ]
30 | }
31 | ]
32 | },
33 | "runAfter": {
34 | "Condition_-_One_Answer_need_to_be_'Yes'": [
35 | "Succeeded"
36 | ]
37 | },
38 | "type": "If"
39 | },
40 | "Condition_-_One_Answer_need_to_be_'Yes'": {
41 | "actions": {
42 | "HTTP_-_Request_TAP_for_user": {
43 | "inputs": {
44 | "authentication": {
45 | "audience": "https://graph.microsoft.com",
46 | "type": "ManagedServiceIdentity"
47 | },
48 | "body": {
49 | "isUsableOnce": false,
50 | "lifetimeInMinutes": 2870
51 | },
52 | "headers": {
53 | "Content-Type": "application/json"
54 | },
55 | "method": "POST",
56 | "uri": "https://graph.microsoft.com/beta/users/@{triggerBody()?['Assignment']?['Target']?['ObjectId']}/authentication/temporaryAccessPassMethods"
57 | },
58 | "runAfter": {
59 | "Parse_JSON_-_Retrieve_user_details": [
60 | "Succeeded"
61 | ]
62 | },
63 | "type": "Http"
64 | },
65 | "HTTP_-_Retrieve_User_Details": {
66 | "inputs": {
67 | "authentication": {
68 | "audience": "https://graph.microsoft.com",
69 | "type": "ManagedServiceIdentity"
70 | },
71 | "method": "GET",
72 | "uri": "https://graph.microsoft.com/beta/users/@{triggerBody()?['Assignment']?['Target']?['ObjectId']}"
73 | },
74 | "runAfter": {},
75 | "type": "Http"
76 | },
77 | "Parse_JSON_-_Request_TAP_for_user": {
78 | "inputs": {
79 | "content": "@body('HTTP_-_Request_TAP_for_user')",
80 | "schema": {
81 | "properties": {
82 | "@@odata.context": {
83 | "type": "string"
84 | },
85 | "createdDateTime": {
86 | "type": "string"
87 | },
88 | "id": {
89 | "type": "string"
90 | },
91 | "isUsable": {
92 | "type": "boolean"
93 | },
94 | "isUsableOnce": {
95 | "type": "boolean"
96 | },
97 | "lifetimeInMinutes": {
98 | "type": "integer"
99 | },
100 | "methodUsabilityReason": {
101 | "type": "string"
102 | },
103 | "startDateTime": {
104 | "type": "string"
105 | },
106 | "temporaryAccessPass": {
107 | "type": "string"
108 | }
109 | },
110 | "type": "object"
111 | }
112 | },
113 | "runAfter": {
114 | "HTTP_-_Request_TAP_for_user": [
115 | "Succeeded"
116 | ]
117 | },
118 | "type": "ParseJson"
119 | },
120 | "Parse_JSON_-_Retrieve_user_details": {
121 | "inputs": {
122 | "content": "@body('HTTP_-_Retrieve_User_Details')",
123 | "schema": {
124 | "properties": {
125 | "@@odata.context": {},
126 | "accountEnabled": {},
127 | "ageGroup": {},
128 | "assignedLicenses": {},
129 | "assignedPlans": {},
130 | "businessPhones": {},
131 | "city": {},
132 | "companyName": {},
133 | "consentProvidedForMinor": {},
134 | "country": {},
135 | "createdDateTime": {},
136 | "creationType": {},
137 | "deletedDateTime": {},
138 | "department": {},
139 | "deviceKeys": {},
140 | "displayName": {},
141 | "employeeHireDate": {},
142 | "employeeId": {},
143 | "employeeLeaveDateTime": {},
144 | "employeeOrgData": {},
145 | "employeeType": {},
146 | "externalUserConvertedOn": {},
147 | "externalUserState": {},
148 | "externalUserStateChangeDateTime": {},
149 | "faxNumber": {},
150 | "givenName": {},
151 | "id": {},
152 | "imAddresses": {},
153 | "infoCatalogs": {},
154 | "isLicenseReconciliationNeeded": {},
155 | "isManagementRestricted": {},
156 | "isResourceAccount": {},
157 | "jobTitle": {},
158 | "legalAgeGroupClassification": {},
159 | "mail": {},
160 | "mailNickname": {},
161 | "mobilePhone": {},
162 | "officeLocation": {},
163 | "onPremisesDistinguishedName": {},
164 | "onPremisesDomainName": {},
165 | "onPremisesExtensionAttributes": {
166 | "properties": {
167 | "extensionAttribute1": {},
168 | "extensionAttribute10": {},
169 | "extensionAttribute11": {},
170 | "extensionAttribute12": {},
171 | "extensionAttribute13": {},
172 | "extensionAttribute14": {},
173 | "extensionAttribute15": {},
174 | "extensionAttribute2": {},
175 | "extensionAttribute3": {},
176 | "extensionAttribute4": {},
177 | "extensionAttribute5": {},
178 | "extensionAttribute6": {},
179 | "extensionAttribute7": {},
180 | "extensionAttribute8": {},
181 | "extensionAttribute9": {}
182 | },
183 | "type": "object"
184 | },
185 | "onPremisesImmutableId": {},
186 | "onPremisesLastSyncDateTime": {},
187 | "onPremisesObjectIdentifier": {},
188 | "onPremisesProvisioningErrors": {},
189 | "onPremisesSamAccountName": {},
190 | "onPremisesSecurityIdentifier": {},
191 | "onPremisesSyncEnabled": {},
192 | "onPremisesUserPrincipalName": {},
193 | "otherMails": {},
194 | "passwordPolicies": {},
195 | "postalCode": {},
196 | "preferredDataLocation": {},
197 | "preferredLanguage": {},
198 | "provisionedPlans": {},
199 | "proxyAddresses": {},
200 | "refreshTokensValidFromDateTime": {},
201 | "securityIdentifier": {},
202 | "serviceProvisioningErrors": {},
203 | "showInAddressList": {},
204 | "signInSessionsValidFromDateTime": {},
205 | "state": {},
206 | "streetAddress": {},
207 | "surname": {},
208 | "usageLocation": {},
209 | "userPrincipalName": {},
210 | "userType": {}
211 | },
212 | "type": "object"
213 | }
214 | },
215 | "runAfter": {
216 | "HTTP_-_Retrieve_User_Details": [
217 | "Succeeded"
218 | ]
219 | },
220 | "type": "ParseJson"
221 | },
222 | "Send_TAP_to_end_user": {
223 | "inputs": {
224 | "body": {
225 | "Body": "
Hi @{body('Parse_JSON_-_Retrieve_user_details')?['givenName']},
\n
\nYou recently requested a Temporary Access Pass via My Access, this request has now been approved!
\n
\nYour Temporary Access Pass is: @{body('Parse_JSON_-_Request_TAP_for_user')?['temporaryAccessPass']}
\n
\nYou can use the Temporary Access Pass to enroll for MFA or Passwordless Methods via My Security Info .
\n
\nPlease be aware that from the moment you received this email the Temporary Access Pass is valid for 48 hours.
\n
\nKind Regards,
\n
\nIT Team Identity Man
",
226 | "Importance": "Normal",
227 | "Subject": "Your Temporary Access Pass request is ready!",
228 | "To": "@{body('Parse_JSON_-_Retrieve_user_details')?['mail']}"
229 | },
230 | "host": {
231 | "connection": {
232 | "name": "@parameters('$connections')['office365']['connectionId']"
233 | }
234 | },
235 | "method": "post",
236 | "path": "/v2/Mail"
237 | },
238 | "runAfter": {
239 | "Parse_JSON_-_Request_TAP_for_user": [
240 | "Succeeded"
241 | ]
242 | },
243 | "type": "ApiConnection"
244 | }
245 | },
246 | "expression": {
247 | "or": [
248 | {
249 | "equals": [
250 | "@triggerBody()?['Answers'][1].value",
251 | "Yes"
252 | ]
253 | },
254 | {
255 | "equals": [
256 | "@triggerBody()?['Answers'][0].value",
257 | "Yes"
258 | ]
259 | }
260 | ]
261 | },
262 | "runAfter": {},
263 | "type": "If"
264 | }
265 | },
266 | "contentVersion": "1.0.0.0",
267 | "outputs": {},
268 | "parameters": {
269 | "$connections": {
270 | "defaultValue": {},
271 | "type": "Object"
272 | }
273 | },
274 | "triggers": {
275 | "manual": {
276 | "inputs": {
277 | "schema": {
278 | "properties": {
279 | "AccessPackage": {
280 | "properties": {
281 | "Description": {
282 | "description": "AccessPackage-Description",
283 | "type": "string"
284 | },
285 | "DisplayName": {
286 | "description": "AccessPackage-DisplayName",
287 | "type": "string"
288 | },
289 | "Id": {
290 | "description": "AccessPackage-Id",
291 | "type": "string"
292 | }
293 | },
294 | "type": "object"
295 | },
296 | "AccessPackageAssignmentRequestId": {
297 | "type": "string"
298 | },
299 | "AccessPackageCatalog": {
300 | "properties": {
301 | "Description": {
302 | "description": "AccessPackageCatalog-Description",
303 | "type": "string"
304 | },
305 | "DisplayName": {
306 | "description": "AccessPackageCatalog-DisplayName",
307 | "type": "string"
308 | },
309 | "Id": {
310 | "description": "AccessPackageCatalog-Id",
311 | "type": "string"
312 | }
313 | },
314 | "type": "object"
315 | },
316 | "Answers": {
317 | "type": "array"
318 | },
319 | "Assignment": {
320 | "properties": {
321 | "AssignmentPolicy": {
322 | "properties": {
323 | "DisplayName": {
324 | "description": "AssignmentPolicy-DisplayName",
325 | "type": "string"
326 | },
327 | "Id": {
328 | "description": "AssignmentPolicy-Id",
329 | "type": "string"
330 | }
331 | },
332 | "type": "object"
333 | },
334 | "Id": {
335 | "description": "Assignment-Id",
336 | "type": "string"
337 | },
338 | "State": {
339 | "description": "Assignment-State",
340 | "type": "string"
341 | },
342 | "Status": {
343 | "description": "Assignment-Status",
344 | "type": "string"
345 | },
346 | "Target": {
347 | "properties": {
348 | "ConnectedOrganization": {
349 | "properties": {
350 | "Description": {
351 | "description": "Assignment-Target-ConnectedOrganization-Description",
352 | "type": "string"
353 | },
354 | "DisplayName": {
355 | "description": "Assignment-Target-ConnectedOrganization-DisplayName",
356 | "type": "string"
357 | },
358 | "Id": {
359 | "description": "Assignment-Target-ConnectedOrganization-Id",
360 | "type": "string"
361 | }
362 | },
363 | "type": "object"
364 | },
365 | "DisplayName": {
366 | "description": "Assignment-Target-DisplayName",
367 | "type": "string"
368 | },
369 | "Id": {
370 | "description": "Assignment-Target-Id",
371 | "type": "string"
372 | },
373 | "ObjectId": {
374 | "description": "Assignment-Target-ObjectId",
375 | "type": "string"
376 | }
377 | },
378 | "type": "object"
379 | }
380 | },
381 | "type": "object"
382 | },
383 | "CallbackConfiguration": {
384 | "properties": {
385 | "DurationBeforeTimeout": {
386 | "type": "string"
387 | }
388 | },
389 | "type": "object"
390 | },
391 | "CallbackUriPath": {
392 | "type": "string"
393 | },
394 | "CustomExtensionStageInstanceId": {
395 | "type": "string"
396 | },
397 | "RequestType": {
398 | "type": "string"
399 | },
400 | "Requestor": {
401 | "properties": {
402 | "DisplayName": {
403 | "description": "Requestor-DisplayName",
404 | "type": "string"
405 | },
406 | "Id": {
407 | "description": "Requestor-Id",
408 | "type": "string"
409 | },
410 | "ObjectId": {
411 | "description": "Requestor-ObjectId",
412 | "type": "string"
413 | }
414 | },
415 | "type": "object"
416 | },
417 | "Stage": {
418 | "type": "string"
419 | },
420 | "State": {
421 | "type": "string"
422 | },
423 | "Status": {
424 | "type": "string"
425 | }
426 | },
427 | "type": "object"
428 | }
429 | },
430 | "kind": "Http",
431 | "operationOptions": "IncludeAuthorizationHeadersInOutputs",
432 | "type": "Request"
433 | }
434 | }
435 | },
436 | "parameters": {
437 | "$connections": {
438 | "value": {
439 | "office365": {
440 | "connectionId": "/subscriptions/faaeca84-60af-4e98-8846-ebf0944a5fba/resourceGroups/RG-Generic/providers/Microsoft.Web/connections/office365",
441 | "connectionName": "office365",
442 | "id": "/subscriptions/faaeca84-60af-4e98-8846-ebf0944a5fba/providers/Microsoft.Web/locations/westeurope/managedApis/office365"
443 | }
444 | }
445 | }
446 | }
447 | }
448 |
--------------------------------------------------------------------------------
/AccessPackages/Set-ManagedIdentityPermission.ps1:
--------------------------------------------------------------------------------
1 | #Import Modules
2 | Import-Module Microsoft.Graph.Applications
3 |
4 | #Connect with the right permission scopes
5 | Connect-MgGraph -Scopes Application.Read.All, AppRoleAssignment.ReadWrite.All, RoleManagement.ReadWrite.Directory
6 |
7 | #Configure Variables
8 | $managedIdentityId = "ObjectID of ServicePrincipal"
9 | $roleName = "User.Read.All"
10 |
11 | #Retrieve additional Details
12 | $msgraph = Get-MgServicePrincipal -Filter "AppId eq '00000003-0000-0000-c000-000000000000'"
13 | $role = $Msgraph.AppRoles| Where-Object {$_.Value -eq $roleName}
14 |
15 | #Assign application permissions to Managed Identity
16 | New-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $managedIdentityId -PrincipalId $managedIdentityId -ResourceId $msgraph.Id -AppRoleId $role.Id
17 |
--------------------------------------------------------------------------------
/AccessReviews/Enable_Access_Reviews_On_Scale_M365_Groups.ps1:
--------------------------------------------------------------------------------
1 | #Configure Managed Identity Variable for the use a managed identity (permissions required are AccessReview.ReadWrite.All & Group.Read.All
2 | $ManagedIdentity = $True
3 | If ($ManagedIdentity -ne $True) {
4 | #Configure tenant variables based on your own service pricipal (permissions required are AccessReview.ReadWrite.All & Group.Read.All
5 | $AppClientId = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
6 | $TenantId = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
7 | $ClientSecret = "xxxxxxxxxxxx"
8 |
9 | #Configure connection to Graph API and make sure to retrieve access token
10 | $RequestBody = @{client_id=$AppClientId;client_secret=$ClientSecret;grant_type="client_credentials";scope="https://graph.microsoft.com/.default";}
11 | $OAuthResponse = Invoke-RestMethod -Method Post -Uri https://login.microsoftonline.com/$TenantId/oauth2/v2.0/token -Body $RequestBody
12 | $AccessToken = $OAuthResponse.access_token
13 |
14 | #Form request headers with the acquired $AccessToken
15 | $headers = @{'Content-Type'="application\json";'Authorization'="Bearer $AccessToken"}
16 | }
17 | else {
18 | $resourceURI = "https://graph.microsoft.com"
19 | $tokenAuthURI = $env:IDENTITY_ENDPOINT + "?resource=$resourceURI&api-version=2019-08-01"
20 | $tokenResponse = Invoke-RestMethod -Method Get -Headers @{"X-IDENTITY-HEADER"="$env:IDENTITY_HEADER"} -Uri $tokenAuthURI
21 | $AccessToken = $tokenResponse.access_token
22 |
23 | #Form request headers with the acquired $AccessToken
24 | $headers = @{'Content-Type'="application\json";'Authorization'="Bearer $AccessToken"}
25 | }
26 |
27 | ###################################################
28 |
29 | Function Build-AccessReviewRequestBody($id,$displayname,$LabelResponse) {
30 | ## For Confidential only review guest users each quarter and give the review (owner) 14 days time to respond.
31 | ## If owner doesn't respond don't take action.
32 | if ($LabelResponse -eq "Confidential") {
33 | $accessReviewBody = @"
34 | {
35 | "displayName": "Confidential Guest Access to the group $displayname ",
36 | "descriptionForAdmins": "Access Review for guest access to the group $displayname ",
37 | "descriptionForReviewers": "Please review guest membership and access to the group $displayname as it's labeled as confidential.",
38 | "scope": {
39 | "query": "/v1.0/groups/$id/transitiveMembers/microsoft.graph.user/?`$count=true&`$filter=(userType eq 'Guest')",
40 | "queryType": "MicrosoftGraph"
41 | },
42 | "instanceEnumerationScope": {
43 | "query": "/groups/$id",
44 | "queryType": "MicrosoftGraph"
45 | },
46 | "reviewers": [
47 | {
48 | "query": "/v1.0/groups/$id/owners",
49 | "queryType": "MicrosoftGraph"
50 | }
51 | ],
52 | "settings": {
53 | "mailNotificationsEnabled": true,
54 | "reminderNotificationsEnabled": true,
55 | "justificationRequiredOnApproval": true,
56 | "defaultDecisionEnabled": false,
57 | "defaultDecision": "None",
58 | "instanceDurationInDays": 21,
59 | "autoApplyDecisionsEnabled": true,
60 | "recommendationsEnabled": true,
61 | "recommendationLookBackDuration": "P30D",
62 | "decisionHistoriesForReviewersEnabled": false,
63 | "recurrence": {
64 | "pattern": {
65 | "type": "absoluteMonthly",
66 | "interval": 6,
67 | },
68 | "range": {
69 | "type": "noEnd",
70 | "numberOfOccurrences": 0,
71 | "recurrenceTimeZone": null,
72 | "startDate": "2022-01-02",
73 | "endDate": "9999-12-31"
74 | }
75 | },
76 | "applyActions": [
77 | {
78 | "@odata.type": "#microsoft.graph.removeAccessApplyAction"
79 | }
80 | ],
81 | "recommendationInsightSettings": [
82 | {
83 | "@odata.type": "#microsoft.graph.userLastSignInRecommendationInsightSetting",
84 | "recommendationLookBackDuration": "P30D",
85 | "signInScope": "tenant"
86 | }
87 | ]
88 | }
89 | }
90 | "@
91 | }
92 |
93 | ## For Highly Confidential review all users in the M365 Group each quarter and give the review (owner) 14 days time to respond.
94 | ## If owner doesn't respond take recommended actions.
95 | if ($LabelResponse -eq "Highly Confidential") {
96 | $accessReviewBody = @"
97 | {
98 | "displayName": "Highly Confidential user Access to the group $displayname ",
99 | "descriptionForAdmins": "Access Review for user access to the group $displayname ",
100 | "descriptionForReviewers": "Please review user membership and access to the group $displayname as it's labeled as Highly Confidential.",
101 | "scope": {
102 | "query": "/groups/$id/transitiveMembers",
103 | "queryType": "MicrosoftGraph"
104 | },
105 | "instanceEnumerationScope": {
106 | "query": "/groups/$id",
107 | "queryType": "MicrosoftGraph"
108 | },
109 | "reviewers": [
110 | {
111 | "query": "/v1.0/groups/$id/owners",
112 | "queryType": "MicrosoftGraph"
113 | }
114 | ],
115 | "settings": {
116 | "mailNotificationsEnabled": true,
117 | "reminderNotificationsEnabled": true,
118 | "justificationRequiredOnApproval": true,
119 | "defaultDecisionEnabled": true,
120 | "defaultDecision": "Recommendation",
121 | "instanceDurationInDays": 21,
122 | "autoApplyDecisionsEnabled": true,
123 | "recommendationsEnabled": true,
124 | "recommendationLookBackDuration": "P30D",
125 | "decisionHistoriesForReviewersEnabled": false,
126 | "recurrence": {
127 | "pattern": {
128 | "type": "absoluteMonthly",
129 | "interval": 3,
130 | },
131 | "range": {
132 | "type": "noEnd",
133 | "numberOfOccurrences": 0,
134 | "recurrenceTimeZone": null,
135 | "startDate": "2022-01-02",
136 | "endDate": "9999-12-31"
137 | }
138 | },
139 | "applyActions": [
140 | {
141 | "@odata.type": "#microsoft.graph.removeAccessApplyAction"
142 | }
143 | ],
144 | "recommendationInsightSettings": [
145 | {
146 | "@odata.type": "#microsoft.graph.userLastSignInRecommendationInsightSetting",
147 | "recommendationLookBackDuration": "P30D",
148 | "signInScope": "tenant"
149 | }
150 | ]
151 | }
152 | }
153 | "@
154 | }
155 |
156 | return $accessReviewBody
157 | }
158 |
159 | ###################################################
160 |
161 | #Define Graph API Call for all Microsoft 365 Groups / Microsoft Teams Groups.
162 | $ApiGroupUrl = "https://graph.microsoft.com/v1.0/groups?`$filter=groupTypes/any(c:c+eq+'Unified')"
163 |
164 | #Perform pagination if next page link (odata.nextlink) returned.
165 | While ($ApiGroupUrl -ne $Null) {
166 | #Retrieve all groups.
167 | $GroupResponse = Invoke-WebRequest -Method GET -Uri $ApiGroupUrl -ContentType "application\json" -Headers $headers -UseBasicParsing | ConvertFrom-Json
168 |
169 | #If the variable $groupresponse contains a value continue.
170 | if($GroupResponse.value) {
171 | #Retrieve the value details
172 | $Groups = $GroupResponse.value
173 |
174 | #Define Graph API Call to retrieve all current access reviews.
175 | $ApiReviewsUrl = "https://graph.microsoft.com/beta/identityGovernance/accessReviews/definitions/"
176 |
177 | #Perform pagination if next page link (odata.nextlink) returned.
178 | While ($ApiReviewsUrl -ne $Null){
179 |
180 | #retrieve all Access Reviews
181 | $AccessReviewsResponse = Invoke-WebRequest -Method GET -Uri $ApiReviewsUrl -ContentType "application\json" -Headers $headers -UseBasicParsing | ConvertFrom-Json
182 |
183 | #List all groups in Azure AD which do contain an Access Review and grab their ID.
184 | $GroupsWithAccessReviews = $AccessReviewsResponse.value.instanceEnumerationScope.query | where {$_ -like "*/groups/*"} | ForEach-Object { ($_ -split "/groups/")[1] }
185 |
186 | #for each group in $groups do the following
187 | ForEach($Group in $Groups) {
188 |
189 | #Grab ID and DisplayName and check if group already has an Access Review Applied.
190 | $id = $group.id
191 | $displayname = $group.displayName
192 | $AccessReviewApplied = $GroupsWithAccessReviews.Contains($id)
193 |
194 | #If the group doesn't have an access review yet the next section will be ran.
195 | if ($AccessReviewApplied -eq $false) {
196 |
197 | #Now let's retreive the label from the group (if any).
198 | $ApiLabelUrl = "https://graph.microsoft.com/beta/groups/{$id}?`$select=assignedLabels"
199 | $LabelResponse = Invoke-WebRequest -Method GET -Uri $ApiLabelUrl -ContentType "application\json" -Headers $headers -UseBasicParsing | ConvertFrom-Json
200 | $LabelResponse = $LabelResponse.assignedLabels.displayname
201 |
202 | #If the group does have a specific label applied the following section will be ran.
203 | If ($LabelResponse) {
204 | $AcceptLabelResponse = @("Confidential","Highly Confidential")
205 | If ($LabelResponse -in $AcceptLabelResponse) {
206 | #write output to the screen, build the access review based on the function and post it to the Graph API.
207 | write-output "Microsoft 365 Group '$displayname' current has Sensitivity Label '$labelresponse' assigned, creating Access Review type $labelresponse!"
208 | $accessReviewBody = Build-AccessReviewRequestBody -id $id -displayname $displayname -LabelResponse $labelresponse
209 | $accessReviewCreateUrl = "https://graph.microsoft.com/beta/identityGovernance/accessReviews/definitions/"
210 | $accessReviewResponse = Invoke-RestMethod -Method Post -Uri $accessReviewCreateUrl -Body $accessReviewBody -ContentType 'application/json' -Headers $headers -UseBasicParsing
211 | write-output "AR Applied for Microsoft 365 group: $displayname"
212 | }
213 | else {
214 | #write-output
215 | write-output "Group $displayname has Sensitivity Label 'Default' applied, skipping Access Review Creation."
216 | }
217 | }
218 | #If the group does not have a label applied the following section will be ran.
219 | Else {
220 | #write-output
221 | write-output "Group $displayname has no Sensitivity Label applied, skipping Access Review Creation."
222 | }
223 | }
224 |
225 | #If the group already has an access review the next section will be ran.
226 | else {
227 | #write-output
228 | write-output "Group $displayname already has an Access Review Applied, skipping Access Review Creation."
229 | }
230 |
231 | }
232 | $ApiReviewsUrl=$AccessReviewsResponse.'@odata.nextlink'
233 | }
234 | }
235 | $ApiGroupUrl=$GroupResponse.'@odata.nextlink'
236 | }
237 |
--------------------------------------------------------------------------------
/AdminLifecycleManagement/Configure-MsGraphPermissions.ps1:
--------------------------------------------------------------------------------
1 | # Connect to Microsoft Graph with Global Administrator Permissions
2 | #Connect-MgGraph -Scopes "Application.Read.All","AppRoleAssignment.ReadWrite.All,RoleManagement.ReadWrite.Directory"
3 |
4 | # You will be prompted for the Name of you Managed Identity
5 | $MdId_Name = Read-Host "Name of your Managed Identity"
6 | $MdId_ID = (Get-MgServicePrincipal -Filter "displayName eq '$MdId_Name'").id
7 |
8 | # Adding Microsoft Graph permissions
9 | $graphApp = Get-MgServicePrincipal -Filter "AppId eq '00000003-0000-0000-c000-000000000000'"
10 |
11 | # Add the required Graph scopes
12 | $graphScopes = @(
13 | "User.ReadWrite.All"
14 | "EntitlementManagement.ReadWrite.All"
15 | "Mail.Send"
16 | )
17 |
18 | ForEach($scope in $graphScopes) {
19 | $appRole = $graphApp.AppRoles | Where-Object {$_.Value -eq $scope}
20 | # Check if permissions isn't already assigned
21 | $assignedAppRole = Get-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $MdId_ID | Where-Object { $_.AppRoleId -eq $appRole.Id -and $_.ResourceDisplayName -eq "Microsoft Graph"}
22 |
23 | if ($null -eq $assignedAppRole) {
24 | New-MgServicePrincipalAppRoleAssignment -PrincipalId $MdId_ID -ServicePrincipalId $MdId_ID -ResourceId $graphApp.Id -AppRoleId $appRole.Id
25 | }
26 | Else {
27 | write-host "Scope $scope already assigned"
28 | }
29 | }
30 |
31 | #Add Office 365 Exchange Online Permissions for the App Registration
32 | $ExoApp = Get-MgServicePrincipal -Filter "AppId eq '00000002-0000-0ff1-ce00-000000000000'"
33 | $AppPermission = $ExoApp.AppRoles | Where-Object {$_.DisplayName -eq "Manage Exchange As Application"}
34 |
35 | $AppRoleAssignment = @{
36 | "PrincipalId" = $MdId_ID
37 | "ResourceId" = $ExoApp.Id
38 | "AppRoleId" = $AppPermission.Id
39 | }
40 |
41 | New-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $MdId_ID -BodyParameter $AppRoleAssignment
42 |
--------------------------------------------------------------------------------
/AdminLifecycleManagement/Configure-MsGraphPermissions_AdminAccount_Offboarding.ps1:
--------------------------------------------------------------------------------
1 | # Connect to Microsoft Graph with Global Administrator Permissions
2 | Connect-MgGraph -Scopes "Application.Read.All,AppRoleAssignment.ReadWrite.All,RoleManagement.ReadWrite.Directory"
3 |
4 | # You will be prompted for the Name of you Managed Identity
5 | $MdId_Name = Read-Host "Name of your Managed Identity"
6 | $MdId_ID = (Get-MgServicePrincipal -Filter "displayName eq '$MdId_Name'").id
7 |
8 | # Adding Microsoft Graph permissions
9 | $graphApp = Get-MgServicePrincipal -Filter "AppId eq '00000003-0000-0000-c000-000000000000'"
10 |
11 | # Add the required Graph scopes
12 | $graphScopes = @(
13 | "User.EnableDisableAccount.All"
14 | )
15 |
16 | ForEach($scope in $graphScopes) {
17 | $appRole = $graphApp.AppRoles | Where-Object {$_.Value -eq $scope}
18 | # Check if permissions isn't already assigned
19 | $assignedAppRole = Get-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $MdId_ID | Where-Object { $_.AppRoleId -eq $appRole.Id -and $_.ResourceDisplayName -eq "Microsoft Graph"}
20 |
21 | if ($null -eq $assignedAppRole) {
22 | New-MgServicePrincipalAppRoleAssignment -PrincipalId $MdId_ID -ServicePrincipalId $MdId_ID -ResourceId $graphApp.Id -AppRoleId $appRole.Id
23 | }
24 | Else {
25 | write-host "Scope $scope already assigned"
26 | }
27 | }
--------------------------------------------------------------------------------
/AdminLifecycleManagement/Configure-MsGraphPermissions_AdminAccount_PostOffboarding.ps1:
--------------------------------------------------------------------------------
1 | # Connect to Microsoft Graph with Global Administrator Permissions
2 | Connect-MgGraph -Scopes "Application.Read.All,AppRoleAssignment.ReadWrite.All,RoleManagement.ReadWrite.Directory"
3 |
4 | # You will be prompted for the Name of you Managed Identity
5 | $MdId_Name = Read-Host "Name of your Managed Identity"
6 | $MdId_ID = (Get-MgServicePrincipal -Filter "displayName eq '$MdId_Name'").id
7 |
8 | # Adding Microsoft Graph permissions
9 | $graphApp = Get-MgServicePrincipal -Filter "AppId eq '00000003-0000-0000-c000-000000000000'"
10 |
11 | # Add the required Graph scopes
12 | $graphScopes = @(
13 | "User.ReadWrite.All"
14 | )
15 |
16 | ForEach($scope in $graphScopes) {
17 | $appRole = $graphApp.AppRoles | Where-Object {$_.Value -eq $scope}
18 | # Check if permissions isn't already assigned
19 | $assignedAppRole = Get-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $MdId_ID | Where-Object { $_.AppRoleId -eq $appRole.Id -and $_.ResourceDisplayName -eq "Microsoft Graph"}
20 |
21 | if ($null -eq $assignedAppRole) {
22 | New-MgServicePrincipalAppRoleAssignment -PrincipalId $MdId_ID -ServicePrincipalId $MdId_ID -ResourceId $graphApp.Id -AppRoleId $appRole.Id
23 | }
24 | Else {
25 | write-host "Scope $scope already assigned"
26 | }
27 | }
--------------------------------------------------------------------------------
/AdminLifecycleManagement/LA-099-Request-Admin-Account.json:
--------------------------------------------------------------------------------
1 | {
2 | "definition": {
3 | "$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#",
4 | "actions": {
5 | "Condition": {
6 | "actions": {
7 | "Condition_2": {
8 | "actions": {
9 | "Compose_-_UserPrincipalName": {
10 | "inputs": "adm.@{replace(body('Parse_JSON_-_Get_all_user_details')?['userPrincipalName'],'@identity-man.eu','@jacobsaa.onmicrosoft.com')}",
11 | "runAfter": {
12 | "Parse_JSON_-_Get_all_user_details": [
13 | "Succeeded"
14 | ]
15 | },
16 | "type": "Compose"
17 | },
18 | "Delay_-_20_seconds": {
19 | "inputs": {
20 | "interval": {
21 | "count": 20,
22 | "unit": "Second"
23 | }
24 | },
25 | "runAfter": {
26 | "HTTP_-_Create_admin_account": [
27 | "Succeeded"
28 | ]
29 | },
30 | "type": "Wait"
31 | },
32 | "HTTP_-_Create_admin_account": {
33 | "inputs": {
34 | "authentication": {
35 | "audience": "https://graph.microsoft.com",
36 | "type": "ManagedServiceIdentity"
37 | },
38 | "body": "@variables('RequestBody')",
39 | "headers": {
40 | "Content-type": "application/json"
41 | },
42 | "method": "POST",
43 | "uri": "https://graph.microsoft.com/beta/users"
44 | },
45 | "runAfter": {
46 | "Set_variable_-_Request_Body": [
47 | "Succeeded"
48 | ]
49 | },
50 | "runtimeConfiguration": {
51 | "contentTransfer": {
52 | "transferMode": "Chunked"
53 | }
54 | },
55 | "type": "Http"
56 | },
57 | "HTTP_-_Generate_TAP_for_admin": {
58 | "inputs": {
59 | "authentication": {
60 | "audience": "https://graph.microsoft.com",
61 | "type": "ManagedServiceIdentity"
62 | },
63 | "body": {
64 | "isUsableOnce": true,
65 | "lifetimeInMinutes": 1440
66 | },
67 | "headers": {
68 | "Content-Type": "application/json"
69 | },
70 | "method": "POST",
71 | "uri": "https://graph.microsoft.com/beta/users/@{outputs('Compose_-_UserPrincipalName')}/authentication/temporaryAccessPassMethods"
72 | },
73 | "runAfter": {
74 | "Delay_-_20_seconds": [
75 | "Succeeded"
76 | ]
77 | },
78 | "runtimeConfiguration": {
79 | "contentTransfer": {
80 | "transferMode": "Chunked"
81 | }
82 | },
83 | "type": "Http"
84 | },
85 | "HTTP_-_Get_all_user_details": {
86 | "inputs": {
87 | "authentication": {
88 | "audience": "https://graph.microsoft.com",
89 | "type": "ManagedServiceIdentity"
90 | },
91 | "method": "GET",
92 | "uri": "https://graph.microsoft.com/beta/users/@{triggerBody()?['Requestor']?['ObjectId']}?$select=id,displayName,givenName,surname,accountEnabled,userPrincipalName,mailNickname,employeeId"
93 | },
94 | "runtimeConfiguration": {
95 | "contentTransfer": {
96 | "transferMode": "Chunked"
97 | }
98 | },
99 | "type": "Http"
100 | },
101 | "HTTP_-_Send_an_email": {
102 | "inputs": {
103 | "authentication": {
104 | "audience": "https://graph.microsoft.com",
105 | "type": "ManagedServiceIdentity"
106 | },
107 | "body": {
108 | "message": {
109 | "body": {
110 | "content": "Hi @{body('Parse_JSON_-_Get_all_user_details')?['givenName']},
Your request for an admin account has been approved.
You can sign-in with your admin account by using the following details and instruction:
Username: @{outputs('Compose_-_UserPrincipalName')}
Temporary Access Pass: @{body('Parse_JSON_-_Generate_TAP_for_admin')?['temporaryAccessPass']}
You can now go to MySecurityInfo to use your temporary acccess pass to register for a passwordless method (preferably a Security Key or a Passkey), be aware that we don't use passwords so there is no need anymore to know or reset your password.
At last, please don't share these account details with anyone else but you.
Kind Regards,
IdentityMan Helpdesk
",
111 | "contentType": "HTML"
112 | },
113 | "importance": "High",
114 | "subject": "Your IdentityMan Admin account has been created",
115 | "toRecipients": [
116 | {
117 | "emailAddress": {
118 | "address": "@{body('Parse_JSON_-_Get_all_user_details')?['userPrincipalName']}"
119 | }
120 | }
121 | ]
122 | }
123 | },
124 | "method": "POST",
125 | "uri": "https://graph.microsoft.com/v1.0/users/77ca8b96-893b-4f5b-a3f0-369beeaedf5a/sendMail"
126 | },
127 | "runAfter": {
128 | "Parse_JSON_-_Generate_TAP_for_admin": [
129 | "Succeeded"
130 | ]
131 | },
132 | "runtimeConfiguration": {
133 | "contentTransfer": {
134 | "transferMode": "Chunked"
135 | }
136 | },
137 | "type": "Http"
138 | },
139 | "Parse_JSON_-_Generate_TAP_for_admin": {
140 | "inputs": {
141 | "content": "@body('HTTP_-_Generate_TAP_for_admin')",
142 | "schema": {
143 | "properties": {
144 | "@@odata.context": {
145 | "type": "string"
146 | },
147 | "createdDateTime": {
148 | "type": "string"
149 | },
150 | "id": {
151 | "type": "string"
152 | },
153 | "isUsable": {
154 | "type": "boolean"
155 | },
156 | "isUsableOnce": {
157 | "type": "boolean"
158 | },
159 | "lifetimeInMinutes": {
160 | "type": "integer"
161 | },
162 | "methodUsabilityReason": {
163 | "type": "string"
164 | },
165 | "startDateTime": {
166 | "type": "string"
167 | },
168 | "temporaryAccessPass": {
169 | "type": "string"
170 | }
171 | },
172 | "type": "object"
173 | }
174 | },
175 | "runAfter": {
176 | "HTTP_-_Generate_TAP_for_admin": [
177 | "Succeeded"
178 | ]
179 | },
180 | "type": "ParseJson"
181 | },
182 | "Parse_JSON_-_Get_all_user_details": {
183 | "inputs": {
184 | "content": "@body('HTTP_-_Get_all_user_details')",
185 | "schema": {
186 | "properties": {
187 | "@@odata.context": {
188 | "type": "string"
189 | },
190 | "accountEnabled": {
191 | "type": "boolean"
192 | },
193 | "displayName": {
194 | "type": "string"
195 | },
196 | "employeeId": {},
197 | "givenName": {
198 | "type": "string"
199 | },
200 | "id": {
201 | "type": "string"
202 | },
203 | "mailNickname": {
204 | "type": "string"
205 | },
206 | "surname": {
207 | "type": "string"
208 | },
209 | "userPrincipalName": {
210 | "type": "string"
211 | }
212 | },
213 | "type": "object"
214 | }
215 | },
216 | "runAfter": {
217 | "HTTP_-_Get_all_user_details": [
218 | "Succeeded"
219 | ]
220 | },
221 | "type": "ParseJson"
222 | },
223 | "Set_variable_-_Request_Body": {
224 | "inputs": {
225 | "name": "RequestBody",
226 | "value": "{\n \"displayName\": \"@{body('Parse_JSON_-_Get_all_user_details')?['displayName']} - Admin\",\n \"givenName\": \"@{body('Parse_JSON_-_Get_all_user_details')?['givenName']}\",\n \"surname\": \"@{body('Parse_JSON_-_Get_all_user_details')?['surname']}\",\n \"accountEnabled\": \"@{body('Parse_JSON_-_Get_all_user_details')?['accountEnabled']}\",\n \"userPrincipalName\": \"@{outputs('Compose_-_UserPrincipalName')}\",\n \"mailNickname\": \"@{body('Parse_JSON_-_Get_all_user_details')?['mailNickname']}\",\n \"employeeId\": \"A@{body('Parse_JSON_-_Get_all_user_details')?['employeeId']}\",\n \"passwordProfile\" : {\n \"forceChangePasswordNextSignIn\": false,\n \"password\": \"@{variables('GeneratedPassword')}\"\n }\n}"
227 | },
228 | "runAfter": {
229 | "Compose_-_UserPrincipalName": [
230 | "Succeeded"
231 | ]
232 | },
233 | "type": "SetVariable"
234 | }
235 | },
236 | "else": {
237 | "actions": {}
238 | },
239 | "expression": {
240 | "and": [
241 | {
242 | "equals": [
243 | "@triggerBody()?['Stage']",
244 | "assignmentRequestGranted"
245 | ]
246 | }
247 | ]
248 | },
249 | "type": "If"
250 | }
251 | },
252 | "else": {
253 | "actions": {}
254 | },
255 | "expression": {
256 | "and": [
257 | {
258 | "equals": [
259 | "@triggerBody()?['AccessPackageCatalog']?['Id']",
260 | "a6461768-60ea-4a25-9863-6ec4cba55e93"
261 | ]
262 | }
263 | ]
264 | },
265 | "runAfter": {
266 | "Initialize_variable_-_GeneratedPassword": [
267 | "Succeeded"
268 | ]
269 | },
270 | "type": "If"
271 | },
272 | "HTTP_-_Resume_an_access_package_assignment_request": {
273 | "inputs": {
274 | "authentication": {
275 | "audience": "https://graph.microsoft.com",
276 | "type": "ManagedServiceIdentity"
277 | },
278 | "body": {
279 | "data": {
280 | "@@odata.type": "microsoft.graph.accessPackageAssignmentRequestCallbackData",
281 | "customExtensionStageInstanceDetail": "Admin account has been created",
282 | "customExtensionStageInstanceId": "@{triggerBody()?['CustomExtensionStageInstanceId']}",
283 | "stage": "assignmentRequestGranted"
284 | },
285 | "source": "IdentityMan.AdminAccountRequest",
286 | "type": "microsoft.graph.accessPackageCustomExtensionStage.assignmentRequestGranted"
287 | },
288 | "headers": {
289 | "Content-Type": "application/json"
290 | },
291 | "method": "POST",
292 | "uri": "https://graph.microsoft.com/v1.0/identityGovernance/entitlementManagement/assignmentRequests/@{triggerBody()?['AccessPackageAssignmentRequestId']}/resume"
293 | },
294 | "operationOptions": "DisableAsyncPattern",
295 | "runAfter": {
296 | "Condition": [
297 | "Succeeded"
298 | ]
299 | },
300 | "runtimeConfiguration": {
301 | "contentTransfer": {
302 | "transferMode": "Chunked"
303 | }
304 | },
305 | "type": "Http"
306 | },
307 | "HTTP_-_Resume_and_deny_an_access_package_assignment_request": {
308 | "inputs": {
309 | "authentication": {
310 | "audience": "https://graph.microsoft.com",
311 | "type": "ManagedServiceIdentity"
312 | },
313 | "body": {
314 | "data": {
315 | "@@odata.type": "microsoft.graph.accessPackageAssignmentRequestCallbackData",
316 | "customExtensionStageInstanceDetail": "Admin account creation failed",
317 | "customExtensionStageInstanceId": "",
318 | "stage": "assignmentRequestGranted"
319 | },
320 | "source": "IdentityMan.AdminAccountRequest",
321 | "type": "microsoft.graph.accessPackageCustomExtensionStage.assignmentRequestGranted"
322 | },
323 | "headers": {
324 | "Content-Type": "application/json"
325 | },
326 | "method": "POST",
327 | "uri": "https://graph.microsoft.com/v1.0/identityGovernance/entitlementManagement/assignmentRequests/@{triggerBody()?['AccessPackageAssignmentRequestId']}/resume"
328 | },
329 | "operationOptions": "DisableAsyncPattern",
330 | "runAfter": {
331 | "Condition": [
332 | "TimedOut",
333 | "Skipped",
334 | "Failed"
335 | ]
336 | },
337 | "runtimeConfiguration": {
338 | "contentTransfer": {
339 | "transferMode": "Chunked"
340 | }
341 | },
342 | "type": "Http"
343 | },
344 | "Initialize_variable_-_GeneratedPassword": {
345 | "inputs": {
346 | "variables": [
347 | {
348 | "name": "GeneratedPassword",
349 | "type": "string",
350 | "value": "@{guid()}"
351 | }
352 | ]
353 | },
354 | "runAfter": {
355 | "Initialize_variable_-_Request_Body": [
356 | "Succeeded"
357 | ]
358 | },
359 | "type": "InitializeVariable"
360 | },
361 | "Initialize_variable_-_Request_Body": {
362 | "inputs": {
363 | "variables": [
364 | {
365 | "name": "RequestBody",
366 | "type": "string"
367 | }
368 | ]
369 | },
370 | "runAfter": {},
371 | "type": "InitializeVariable"
372 | }
373 | },
374 | "contentVersion": "1.0.0.0",
375 | "outputs": {},
376 | "parameters": {
377 | "$connections": {
378 | "defaultValue": {},
379 | "type": "Object"
380 | }
381 | },
382 | "triggers": {
383 | "manual": {
384 | "inputs": {
385 | "schema": {
386 | "properties": {
387 | "AccessPackage": {
388 | "properties": {
389 | "Description": {
390 | "description": "AccessPackage-Description",
391 | "type": "string"
392 | },
393 | "DisplayName": {
394 | "description": "AccessPackage-DisplayName",
395 | "type": "string"
396 | },
397 | "Id": {
398 | "description": "AccessPackage-Id",
399 | "type": "string"
400 | }
401 | },
402 | "type": "object"
403 | },
404 | "AccessPackageAssignmentRequestId": {
405 | "type": "string"
406 | },
407 | "AccessPackageCatalog": {
408 | "properties": {
409 | "Description": {
410 | "description": "AccessPackageCatalog-Description",
411 | "type": "string"
412 | },
413 | "DisplayName": {
414 | "description": "AccessPackageCatalog-DisplayName",
415 | "type": "string"
416 | },
417 | "Id": {
418 | "description": "AccessPackageCatalog-Id",
419 | "type": "string"
420 | }
421 | },
422 | "type": "object"
423 | },
424 | "Answers": {
425 | "type": "array"
426 | },
427 | "Assignment": {
428 | "properties": {
429 | "AssignmentPolicy": {
430 | "properties": {
431 | "DisplayName": {
432 | "description": "AssignmentPolicy-DisplayName",
433 | "type": "string"
434 | },
435 | "Id": {
436 | "description": "AssignmentPolicy-Id",
437 | "type": "string"
438 | }
439 | },
440 | "type": "object"
441 | },
442 | "Id": {
443 | "description": "Assignment-Id",
444 | "type": "string"
445 | },
446 | "State": {
447 | "description": "Assignment-State",
448 | "type": "string"
449 | },
450 | "Status": {
451 | "description": "Assignment-Status",
452 | "type": "string"
453 | },
454 | "Target": {
455 | "properties": {
456 | "ConnectedOrganization": {
457 | "properties": {
458 | "Description": {
459 | "description": "Assignment-Target-ConnectedOrganization-Description",
460 | "type": "string"
461 | },
462 | "DisplayName": {
463 | "description": "Assignment-Target-ConnectedOrganization-DisplayName",
464 | "type": "string"
465 | },
466 | "Id": {
467 | "description": "Assignment-Target-ConnectedOrganization-Id",
468 | "type": "string"
469 | }
470 | },
471 | "type": "object"
472 | },
473 | "DisplayName": {
474 | "description": "Assignment-Target-DisplayName",
475 | "type": "string"
476 | },
477 | "Id": {
478 | "description": "Assignment-Target-Id",
479 | "type": "string"
480 | },
481 | "ObjectId": {
482 | "description": "Assignment-Target-ObjectId",
483 | "type": "string"
484 | }
485 | },
486 | "type": "object"
487 | }
488 | },
489 | "type": "object"
490 | },
491 | "CallbackConfiguration": {
492 | "properties": {
493 | "DurationBeforeTimeout": {
494 | "type": "string"
495 | }
496 | },
497 | "type": "object"
498 | },
499 | "CallbackUriPath": {
500 | "type": "string"
501 | },
502 | "CustomExtensionStageInstanceId": {
503 | "type": "string"
504 | },
505 | "RequestType": {
506 | "type": "string"
507 | },
508 | "Requestor": {
509 | "properties": {
510 | "DisplayName": {
511 | "description": "Requestor-DisplayName",
512 | "type": "string"
513 | },
514 | "Id": {
515 | "description": "Requestor-Id",
516 | "type": "string"
517 | },
518 | "ObjectId": {
519 | "description": "Requestor-ObjectId",
520 | "type": "string"
521 | }
522 | },
523 | "type": "object"
524 | },
525 | "Stage": {
526 | "type": "string"
527 | },
528 | "State": {
529 | "type": "string"
530 | },
531 | "Status": {
532 | "type": "string"
533 | }
534 | },
535 | "type": "object"
536 | }
537 | },
538 | "kind": "Http",
539 | "operationOptions": "IncludeAuthorizationHeadersInOutputs",
540 | "type": "Request"
541 | }
542 | }
543 | },
544 | "parameters": {
545 | "$connections": {
546 | "value": {}
547 | }
548 | }
549 | }
550 |
--------------------------------------------------------------------------------
/AdminLifecycleManagement/LA-AdminAccount-Offboarding.json:
--------------------------------------------------------------------------------
1 | {
2 | "definition": {
3 | "$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#",
4 | "contentVersion": "1.0.0.0",
5 | "triggers": {
6 | "manual": {
7 | "type": "Request",
8 | "kind": "Http",
9 | "inputs": {
10 | "schema": {
11 | "properties": {
12 | "data": {
13 | "properties": {
14 | "callbackUriPath": {
15 | "description": "CallbackUriPath used for Resume Action",
16 | "title": "Data.CallbackUriPath",
17 | "type": "string"
18 | },
19 | "subject": {
20 | "properties": {
21 | "displayName": {
22 | "description": "DisplayName of the Subject",
23 | "title": "Subject.DisplayName",
24 | "type": "string"
25 | },
26 | "email": {
27 | "description": "Email of the Subject",
28 | "title": "Subject.Email",
29 | "type": "string"
30 | },
31 | "id": {
32 | "description": "Id of the Subject",
33 | "title": "Subject.Id",
34 | "type": "string"
35 | },
36 | "manager": {
37 | "properties": {
38 | "displayName": {
39 | "description": "DisplayName parameter for Manager",
40 | "title": "Manager.DisplayName",
41 | "type": "string"
42 | },
43 | "email": {
44 | "description": "Mail parameter for Manager",
45 | "title": "Manager.Mail",
46 | "type": "string"
47 | },
48 | "id": {
49 | "description": "Id parameter for Manager",
50 | "title": "Manager.Id",
51 | "type": "string"
52 | }
53 | },
54 | "type": "object"
55 | },
56 | "userPrincipalName": {
57 | "description": "UserPrincipalName of the Subject",
58 | "title": "Subject.UserPrincipalName",
59 | "type": "string"
60 | }
61 | },
62 | "type": "object"
63 | },
64 | "task": {
65 | "properties": {
66 | "displayName": {
67 | "description": "DisplayName for Task Object",
68 | "title": "Task.DisplayName",
69 | "type": "string"
70 | },
71 | "id": {
72 | "description": "Id for Task Object",
73 | "title": "Task.Id",
74 | "type": "string"
75 | }
76 | },
77 | "type": "object"
78 | },
79 | "taskProcessingResult": {
80 | "properties": {
81 | "createdDateTime": {
82 | "description": "CreatedDateTime for TaskProcessingResult Object",
83 | "title": "TaskProcessingResult.CreatedDateTime",
84 | "type": "string"
85 | },
86 | "id": {
87 | "description": "Id for TaskProcessingResult Object",
88 | "title": "TaskProcessingResult.Id",
89 | "type": "string"
90 | }
91 | },
92 | "type": "object"
93 | },
94 | "workflow": {
95 | "properties": {
96 | "displayName": {
97 | "description": "DisplayName for Workflow Object",
98 | "title": "Workflow.DisplayName",
99 | "type": "string"
100 | },
101 | "id": {
102 | "description": "Id for Workflow Object",
103 | "title": "Workflow.Id",
104 | "type": "string"
105 | },
106 | "workflowVerson": {
107 | "description": "WorkflowVersion for Workflow Object",
108 | "title": "Workflow.WorkflowVersion",
109 | "type": "integer"
110 | }
111 | },
112 | "type": "object"
113 | }
114 | },
115 | "type": "object"
116 | },
117 | "source": {
118 | "description": "Context in which an event happened",
119 | "title": "Request.Source",
120 | "type": "string"
121 | },
122 | "type": {
123 | "description": "Value describing the type of event related to the originating occurrence.",
124 | "title": "Request.Type",
125 | "type": "string"
126 | }
127 | },
128 | "type": "object"
129 | }
130 | }
131 | }
132 | },
133 | "actions": {
134 | "HTTP_-_Callback": {
135 | "type": "Http",
136 | "inputs": {
137 | "uri": "https://graph.microsoft.com/beta@{triggerBody()?['data']?['callbackUriPath']}",
138 | "method": "POST",
139 | "body": {
140 | "data": {
141 | "operationStatus": "Completed"
142 | },
143 | "source": "sample",
144 | "type": "lifecycleEvent"
145 | },
146 | "authentication": {
147 | "audience": "https://graph.microsoft.com",
148 | "type": "ManagedServiceIdentity"
149 | }
150 | },
151 | "runAfter": {
152 | "For_each_-_Admin_Account_found": [
153 | "Succeeded"
154 | ]
155 | }
156 | },
157 | "HTTP_-_Get_account_details_from_MS_Graph": {
158 | "type": "Http",
159 | "inputs": {
160 | "uri": "https://graph.microsoft.com/beta/users/@{triggerBody()?['data']?['subject']?['id']}?$select=id,employeeId,userPrincipalName,accountEnabled",
161 | "method": "GET",
162 | "authentication": {
163 | "type": "ManagedServiceIdentity",
164 | "audience": "https://graph.microsoft.com"
165 | }
166 | },
167 | "runAfter": {},
168 | "runtimeConfiguration": {
169 | "contentTransfer": {
170 | "transferMode": "Chunked"
171 | }
172 | }
173 | },
174 | "Parse_JSON_-_Get_account_details_from_MS_Graph": {
175 | "type": "ParseJson",
176 | "inputs": {
177 | "content": "@body('HTTP_-_Get_account_details_from_MS_Graph')",
178 | "schema": {
179 | "type": "object",
180 | "properties": {
181 | "@@odata.context": {
182 | "type": "string"
183 | },
184 | "id": {
185 | "type": "string"
186 | },
187 | "employeeId": {
188 | "type": "string"
189 | },
190 | "userPrincipalName": {
191 | "type": "string"
192 | },
193 | "accountEnabled": {
194 | "type": "boolean"
195 | }
196 | }
197 | }
198 | },
199 | "runAfter": {
200 | "HTTP_-_Get_account_details_from_MS_Graph": [
201 | "Succeeded"
202 | ]
203 | }
204 | },
205 | "HTTP_-_Get_admin_details_from_MS_Graph": {
206 | "type": "Http",
207 | "inputs": {
208 | "uri": "https://graph.microsoft.com/v1.0/users?$filter=startswith(employeeId,'A@{body('Parse_JSON_-_Get_account_details_from_MS_Graph')?['employeeId']}')&$count=true&$select=id,employeeId,userPrincipalName,accountEnabled",
209 | "method": "GET",
210 | "headers": {
211 | "ConsistencyLevel\n": "eventual\n"
212 | },
213 | "authentication": {
214 | "type": "ManagedServiceIdentity",
215 | "audience": "https://graph.microsoft.com"
216 | }
217 | },
218 | "runAfter": {
219 | "Parse_JSON_-_Get_account_details_from_MS_Graph": [
220 | "Succeeded"
221 | ]
222 | },
223 | "runtimeConfiguration": {
224 | "contentTransfer": {
225 | "transferMode": "Chunked"
226 | }
227 | }
228 | },
229 | "Parse_JSON_-_Get_admin_account_details_from_MS_Graph": {
230 | "type": "ParseJson",
231 | "inputs": {
232 | "content": "@body('HTTP_-_Get_admin_details_from_MS_Graph')",
233 | "schema": {
234 | "type": "object",
235 | "properties": {
236 | "@@odata.context": {
237 | "type": "string"
238 | },
239 | "value": {
240 | "type": "array",
241 | "items": {
242 | "type": "object",
243 | "properties": {
244 | "id": {
245 | "type": "string"
246 | },
247 | "employeeId": {
248 | "type": "string"
249 | },
250 | "userPrincipalName": {
251 | "type": "string"
252 | },
253 | "accountEnabled": {
254 | "type": "boolean"
255 | }
256 | },
257 | "required": [
258 | "id",
259 | "employeeId",
260 | "userPrincipalName",
261 | "accountEnabled"
262 | ]
263 | }
264 | }
265 | }
266 | }
267 | },
268 | "runAfter": {
269 | "HTTP_-_Get_admin_details_from_MS_Graph": [
270 | "Succeeded"
271 | ]
272 | }
273 | },
274 | "For_each_-_Admin_Account_found": {
275 | "type": "Foreach",
276 | "foreach": "@outputs('Parse_JSON_-_Get_admin_account_details_from_MS_Graph')?['body']?['value']",
277 | "actions": {
278 | "HTTP_-_Disable_admin_account": {
279 | "type": "Http",
280 | "inputs": {
281 | "uri": "https://graph.microsoft.com/v1.0/users/@{item()?['userPrincipalName']}",
282 | "method": "PATCH",
283 | "body": {
284 | "accountEnabled": false
285 | },
286 | "authentication": {
287 | "type": "ManagedServiceIdentity",
288 | "audience": "https://graph.microsoft.com"
289 | }
290 | },
291 | "runtimeConfiguration": {
292 | "contentTransfer": {
293 | "transferMode": "Chunked"
294 | }
295 | }
296 | }
297 | },
298 | "runAfter": {
299 | "Parse_JSON_-_Get_admin_account_details_from_MS_Graph": [
300 | "Succeeded"
301 | ]
302 | }
303 | }
304 | },
305 | "outputs": {},
306 | "parameters": {
307 | "$connections": {
308 | "type": "Object",
309 | "defaultValue": {}
310 | }
311 | }
312 | },
313 | "parameters": {
314 | "$connections": {
315 | "type": "Object",
316 | "value": {}
317 | }
318 | }
319 | }
320 |
--------------------------------------------------------------------------------
/AdminLifecycleManagement/LA-AdminAccount-PostOffboarding.json:
--------------------------------------------------------------------------------
1 | {
2 | "definition": {
3 | "$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#",
4 | "contentVersion": "1.0.0.0",
5 | "triggers": {
6 | "manual": {
7 | "type": "Request",
8 | "kind": "Http",
9 | "inputs": {
10 | "schema": {
11 | "properties": {
12 | "data": {
13 | "properties": {
14 | "callbackUriPath": {
15 | "description": "CallbackUriPath used for Resume Action",
16 | "title": "Data.CallbackUriPath",
17 | "type": "string"
18 | },
19 | "subject": {
20 | "properties": {
21 | "displayName": {
22 | "description": "DisplayName of the Subject",
23 | "title": "Subject.DisplayName",
24 | "type": "string"
25 | },
26 | "email": {
27 | "description": "Email of the Subject",
28 | "title": "Subject.Email",
29 | "type": "string"
30 | },
31 | "id": {
32 | "description": "Id of the Subject",
33 | "title": "Subject.Id",
34 | "type": "string"
35 | },
36 | "manager": {
37 | "properties": {
38 | "displayName": {
39 | "description": "DisplayName parameter for Manager",
40 | "title": "Manager.DisplayName",
41 | "type": "string"
42 | },
43 | "email": {
44 | "description": "Mail parameter for Manager",
45 | "title": "Manager.Mail",
46 | "type": "string"
47 | },
48 | "id": {
49 | "description": "Id parameter for Manager",
50 | "title": "Manager.Id",
51 | "type": "string"
52 | }
53 | },
54 | "type": "object"
55 | },
56 | "userPrincipalName": {
57 | "description": "UserPrincipalName of the Subject",
58 | "title": "Subject.UserPrincipalName",
59 | "type": "string"
60 | }
61 | },
62 | "type": "object"
63 | },
64 | "task": {
65 | "properties": {
66 | "displayName": {
67 | "description": "DisplayName for Task Object",
68 | "title": "Task.DisplayName",
69 | "type": "string"
70 | },
71 | "id": {
72 | "description": "Id for Task Object",
73 | "title": "Task.Id",
74 | "type": "string"
75 | }
76 | },
77 | "type": "object"
78 | },
79 | "taskProcessingResult": {
80 | "properties": {
81 | "createdDateTime": {
82 | "description": "CreatedDateTime for TaskProcessingResult Object",
83 | "title": "TaskProcessingResult.CreatedDateTime",
84 | "type": "string"
85 | },
86 | "id": {
87 | "description": "Id for TaskProcessingResult Object",
88 | "title": "TaskProcessingResult.Id",
89 | "type": "string"
90 | }
91 | },
92 | "type": "object"
93 | },
94 | "workflow": {
95 | "properties": {
96 | "displayName": {
97 | "description": "DisplayName for Workflow Object",
98 | "title": "Workflow.DisplayName",
99 | "type": "string"
100 | },
101 | "id": {
102 | "description": "Id for Workflow Object",
103 | "title": "Workflow.Id",
104 | "type": "string"
105 | },
106 | "workflowVerson": {
107 | "description": "WorkflowVersion for Workflow Object",
108 | "title": "Workflow.WorkflowVersion",
109 | "type": "integer"
110 | }
111 | },
112 | "type": "object"
113 | }
114 | },
115 | "type": "object"
116 | },
117 | "source": {
118 | "description": "Context in which an event happened",
119 | "title": "Request.Source",
120 | "type": "string"
121 | },
122 | "type": {
123 | "description": "Value describing the type of event related to the originating occurrence.",
124 | "title": "Request.Type",
125 | "type": "string"
126 | }
127 | },
128 | "type": "object"
129 | }
130 | }
131 | }
132 | },
133 | "actions": {
134 | "For_each_-_Admin_Account_found": {
135 | "type": "Foreach",
136 | "foreach": "@outputs('Parse_JSON_-_Get_admin_account_details_from_MS_Graph')?['body']?['value']",
137 | "actions": {
138 | "HTTP_-_Delete_admin_account": {
139 | "type": "Http",
140 | "inputs": {
141 | "uri": "https://graph.microsoft.com/v1.0/users/@{item()?['userPrincipalName']}",
142 | "method": "DELETE",
143 | "authentication": {
144 | "audience": "https://graph.microsoft.com",
145 | "type": "ManagedServiceIdentity"
146 | }
147 | },
148 | "runtimeConfiguration": {
149 | "contentTransfer": {
150 | "transferMode": "Chunked"
151 | }
152 | }
153 | }
154 | },
155 | "runAfter": {
156 | "Parse_JSON_-_Get_admin_account_details_from_MS_Graph": [
157 | "Succeeded"
158 | ]
159 | }
160 | },
161 | "HTTP_-_Callback": {
162 | "type": "Http",
163 | "inputs": {
164 | "uri": "https://graph.microsoft.com/beta@{triggerBody()?['data']?['callbackUriPath']}",
165 | "method": "POST",
166 | "body": {
167 | "data": {
168 | "operationStatus": "Completed"
169 | },
170 | "source": "sample",
171 | "type": "lifecycleEvent"
172 | },
173 | "authentication": {
174 | "audience": "https://graph.microsoft.com",
175 | "type": "ManagedServiceIdentity"
176 | }
177 | },
178 | "runAfter": {
179 | "For_each_-_Admin_Account_found": [
180 | "Succeeded"
181 | ]
182 | }
183 | },
184 | "HTTP_-_Get_account_details_from_MS_Graph": {
185 | "type": "Http",
186 | "inputs": {
187 | "uri": "https://graph.microsoft.com/beta/users/@{triggerBody()?['data']?['subject']?['id']}?$select=id,employeeId,userPrincipalName,accountEnabled",
188 | "method": "GET",
189 | "authentication": {
190 | "audience": "https://graph.microsoft.com",
191 | "type": "ManagedServiceIdentity"
192 | }
193 | },
194 | "runAfter": {},
195 | "runtimeConfiguration": {
196 | "contentTransfer": {
197 | "transferMode": "Chunked"
198 | }
199 | }
200 | },
201 | "HTTP_-_Get_admin_account_details_from_MS_Graph": {
202 | "type": "Http",
203 | "inputs": {
204 | "uri": "https://graph.microsoft.com/v1.0/users?$filter=startswith(employeeId,'A@{body('Parse_JSON_-_Get_account_details_from_MS_Graph')?['employeeId']}')&$count=true&$select=id,employeeId,userPrincipalName,accountEnabled",
205 | "method": "GET",
206 | "headers": {
207 | "ConsistencyLevel\n": "eventual\n"
208 | },
209 | "authentication": {
210 | "audience": "https://graph.microsoft.com",
211 | "type": "ManagedServiceIdentity"
212 | }
213 | },
214 | "runAfter": {
215 | "Parse_JSON_-_Get_account_details_from_MS_Graph": [
216 | "Succeeded"
217 | ]
218 | },
219 | "runtimeConfiguration": {
220 | "contentTransfer": {
221 | "transferMode": "Chunked"
222 | }
223 | }
224 | },
225 | "Parse_JSON_-_Get_account_details_from_MS_Graph": {
226 | "type": "ParseJson",
227 | "inputs": {
228 | "content": "@body('HTTP_-_Get_account_details_from_MS_Graph')",
229 | "schema": {
230 | "properties": {
231 | "@@odata.context": {
232 | "type": "string"
233 | },
234 | "accountEnabled": {
235 | "type": "boolean"
236 | },
237 | "employeeId": {
238 | "type": "string"
239 | },
240 | "id": {
241 | "type": "string"
242 | },
243 | "userPrincipalName": {
244 | "type": "string"
245 | }
246 | },
247 | "type": "object"
248 | }
249 | },
250 | "runAfter": {
251 | "HTTP_-_Get_account_details_from_MS_Graph": [
252 | "Succeeded"
253 | ]
254 | }
255 | },
256 | "Parse_JSON_-_Get_admin_account_details_from_MS_Graph": {
257 | "type": "ParseJson",
258 | "inputs": {
259 | "content": "@body('HTTP_-_Get_admin_account_details_from_MS_Graph')",
260 | "schema": {
261 | "type": "object",
262 | "properties": {
263 | "@@odata.context": {
264 | "type": "string"
265 | },
266 | "value": {
267 | "type": "array",
268 | "items": {
269 | "type": "object",
270 | "properties": {
271 | "id": {
272 | "type": "string"
273 | },
274 | "employeeId": {
275 | "type": "string"
276 | },
277 | "userPrincipalName": {
278 | "type": "string"
279 | },
280 | "accountEnabled": {
281 | "type": "boolean"
282 | }
283 | },
284 | "required": [
285 | "id",
286 | "employeeId",
287 | "userPrincipalName",
288 | "accountEnabled"
289 | ]
290 | }
291 | }
292 | }
293 | }
294 | },
295 | "runAfter": {
296 | "HTTP_-_Get_admin_account_details_from_MS_Graph": [
297 | "Succeeded"
298 | ]
299 | }
300 | }
301 | },
302 | "outputs": {},
303 | "parameters": {
304 | "$connections": {
305 | "type": "Object",
306 | "defaultValue": {}
307 | }
308 | }
309 | },
310 | "parameters": {
311 | "$connections": {
312 | "type": "Object",
313 | "value": {}
314 | }
315 | }
316 | }
317 |
--------------------------------------------------------------------------------
/AzureAD-GroupWriteBackV2.ps1:
--------------------------------------------------------------------------------
1 | import-module ADSync
2 | $precedenceValue = Read-Host -Prompt "Enter a unique sync rule precedence value [0-99]"
3 |
4 | New-ADSyncRule `
5 | -Name 'In from AAD - Group SOAinAAD Delete WriteBackOutOfScope and SoftDelete' `
6 | -Identifier 'cb871f2d-0f01-4c32-a333-ff809145b947' `
7 | -Description 'Delete AD groups that fall out of scope of Group Writeback or get Soft Deleted in Azure AD' `
8 | -Direction 'Inbound' `
9 | -Precedence $precedenceValue `
10 | -PrecedenceAfter '00000000-0000-0000-0000-000000000000' `
11 | -PrecedenceBefore '00000000-0000-0000-0000-000000000000' `
12 | -SourceObjectType 'group' `
13 | -TargetObjectType 'group' `
14 | -Connector 'b891884f-051e-4a83-95af-2544101c9083' `
15 | -LinkType 'Join' `
16 | -SoftDeleteExpiryInterval 0 `
17 | -ImmutableTag '' `
18 | -OutVariable syncRule
19 |
20 | Add-ADSyncAttributeFlowMapping `
21 | -SynchronizationRule $syncRule[0] `
22 | -Destination 'reasonFiltered' `
23 | -FlowType 'Expression' `
24 | -ValueMergeType 'Update' `
25 | -Expression 'IIF((IsPresent([reasonFiltered]) = True) && (InStr([reasonFiltered], "WriteBackOutOfScope") > 0 || InStr([reasonFiltered], "SoftDelete") > 0), "DeleteThisGroupInAD", [reasonFiltered])' `
26 | -OutVariable syncRule
27 |
28 | New-Object `
29 | -TypeName 'Microsoft.IdentityManagement.PowerShell.ObjectModel.ScopeCondition' `
30 | -ArgumentList 'cloudMastered','true','EQUAL' `
31 | -OutVariable condition0
32 |
33 | Add-ADSyncScopeConditionGroup `
34 | -SynchronizationRule $syncRule[0] `
35 | -ScopeConditions @($condition0[0]) `
36 | -OutVariable syncRule
37 |
38 | New-Object `
39 | -TypeName 'Microsoft.IdentityManagement.PowerShell.ObjectModel.JoinCondition' `
40 | -ArgumentList 'cloudAnchor','cloudAnchor',$false `
41 | -OutVariable condition0
42 |
43 | Add-ADSyncJoinConditionGroup `
44 | -SynchronizationRule $syncRule[0] `
45 | -JoinConditions @($condition0[0]) `
46 | -OutVariable syncRule
47 |
48 | Add-ADSyncRule `
49 | -SynchronizationRule $syncRule[0]
50 |
51 | Get-ADSyncRule `
52 | -Identifier 'cb871f2d-0f01-4c32-a333-ff809145b947'
53 |
--------------------------------------------------------------------------------
/Configure-AuthenticationMethods.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | .DESCRIPTION
3 | This script is able to change / provision the phone number of the end user used by MFA / SMS Signin
4 | Written by: Pim Jacobs (https://identity-man.eu)
5 |
6 | .PARAMETER Token Required
7 | The token only string for a Bearer token.
8 |
9 | .PARAMETER UPN Required
10 | The UPN for which you want to add or change the phonenumber
11 |
12 | .PARAMETER ActionType Optional
13 | The actiontype for changes, which can either be Add, Update or Delete as action.
14 |
15 | .PARAMETER PhoneNumber Optional
16 | Enter the international phone number of the end user i.e. "+310612345678"
17 |
18 | .PARAMETER PhoneType Optional
19 | Choose between three values i.e. Mobile, AlternateMobile or Office
20 |
21 | .PARAMETER SMSSignin Optional
22 | The actiontype for the sms sign-in feature, which can either be Add, Update or Delete as action
23 |
24 | .EXAMPLE
25 | To read current settings
26 | Configure-MFAMethods.ps1 -Token -UPN 'username@identity-man.eu'
27 |
28 | To update, add or delete settings
29 | Configure-MFAMethods.ps1 -Token -UPN 'username@identity-man.eu' -ActionType '' -PhoneNumber '<+310612345678>' -PhoneType ''
30 |
31 | To enable or disable the SMSSignIn feature (only when the user is allowed to use this feature).
32 | Configure-MFAMethods.ps1 -Token -UPN 'username@identity-man.eu' -SMSSignIn ''
33 | #>
34 |
35 | [CmdletBinding()]
36 | Param (
37 | [Parameter(Mandatory=$true)]
38 | [String]$Token,
39 | [Parameter(Mandatory=$true)]
40 | [String]$UPN,
41 | [Parameter(Mandatory=$false)]
42 | [ValidateSet("Add", "Update", "Delete")]
43 | [String]$ActionType,
44 | [Parameter(Mandatory=$false)]
45 | [String]$PhoneNumber,
46 | [Parameter(Mandatory=$false)]
47 | [ValidateSet("Mobile", "AlternateMobile", "Office")]
48 | [String]$PhoneType,
49 | [Parameter(Mandatory=$false)]
50 | [ValidateSet("Enable", "Disable")]
51 | [String]$SMSSignIn
52 | )
53 |
54 | $ErrorActionPreference = 'Stop';
55 |
56 | $graphApiVersion = "beta";
57 | $resource = "authentication/phoneMethods";
58 | $headers = @{
59 | "Authorization" = "Bearer $($Token)";
60 | "Content-Type" = "application/json";
61 | }
62 |
63 | #Try to see if the user is currently enrolled and if so retrieve current value
64 | $currentusersetting = Invoke-RestMethod -Uri "https://graph.microsoft.com/$($graphApiVersion)/users/$($UPN)/$($resource)" -Method get -Headers $headers;
65 | $Currentsettings = $currentusersetting | ConvertTo-Json
66 | write-host "Current Authentication method settings for user $UPN." -ForegroundColor Yellow
67 | write-host $Currentsettings -ForegroundColor Yellow
68 | #endregion
69 |
70 | if ($ActionType -eq "Update"){
71 | $Method = "put"
72 | }
73 |
74 | if ($ActionType -eq "Delete"){
75 | $Method = "Delete"
76 | }
77 |
78 | if ($ActionType){
79 | $UpdateUserSetting = @{ phonetype=$phonetype;phonenumber=$phonenumber}
80 | $UpdateUserSetting = ConvertTo-Json -InputObject $UpdateUserSetting
81 |
82 | if ($ActionType -eq "Add") {
83 | $ExecuteUpdateUserSetting = Invoke-RestMethod -Uri "https://graph.microsoft.com/$($graphApiVersion)/users/$($UPN)/$($resource)" -Method Post -Headers $headers -Body $UpdateUserSetting -ErrorAction Stop -UseBasicParsing
84 | }
85 |
86 | if (($ActionType -eq "Update" -or $ActionType -eq "Delete") -and $PhoneType -like "Mobile") {
87 | $ExecuteUpdateUserSetting = Invoke-RestMethod -Uri "https://graph.microsoft.com/$($graphApiVersion)/users/$($UPN)/$($resource)/3179e48a-750b-4051-897c-87b9720928f7" -Method $Method -Headers $headers -Body $UpdateUserSetting -ErrorAction Stop -UseBasicParsing
88 | }
89 |
90 | if (($ActionType -eq "Update" -or $ActionType -eq "Delete") -and $PhoneType -like "AlternateMobile") {
91 | $ExecuteUpdateUserSetting = Invoke-RestMethod -Uri "https://graph.microsoft.com/$($graphApiVersion)/users/$($UPN)/$($resource)/b6332ec1-7057-4abe-9331-3d72feddfe41" -Method $Method -Headers $headers -Body $UpdateUserSetting -ErrorAction Stop -UseBasicParsing
92 | }
93 |
94 | if (($ActionType -eq "Update" -or $ActionType -eq "Delete") -and $PhoneType -like "Office") {
95 | $ExecuteUpdateUserSetting = Invoke-RestMethod -Uri "https://graph.microsoft.com/$($graphApiVersion)/users/$($UPN)/$($resource)/e37fc753-ff3b-4958-9484-eaa9425c82bc" -Method $Method -Headers $headers -Body $UpdateUserSetting -ErrorAction Stop -UseBasicParsing
96 | }
97 | $newusersettings = Invoke-RestMethod -Uri "https://graph.microsoft.com/$($graphApiVersion)/users/$($UPN)/$($resource)" -Method get -Headers $headers;
98 | $newusersettings = $newusersettings | ConvertTo-Json
99 | write-host "New Authentication method settings for user $UPN." -ForegroundColor Green
100 | write-host $newusersettings -ForegroundColor Green
101 | }
102 |
103 | if (!$ActionType){
104 | write-host "No settings changed for $UPN!" -ForegroundColor Yellow
105 | }
106 |
107 | if ($SMSSignIn){
108 | if ($SMSSignIn -eq "Enable") {
109 | $ExecuteUpdateUserSetting = Invoke-RestMethod -Uri "https://graph.microsoft.com/$($graphApiVersion)/users/$($UPN)/$($resource)/3179e48a-750b-4051-897c-87b9720928f7/enableSmsSignIn" -Method Post -Headers $headers -Body $UpdateUserSetting -ErrorAction Stop -UseBasicParsing
110 | }
111 | if ($SMSSignIn -eq "Disable") {
112 | $ExecuteUpdateUserSetting = Invoke-RestMethod -Uri "https://graph.microsoft.com/$($graphApiVersion)/users/$($UPN)/$($resource)/3179e48a-750b-4051-897c-87b9720928f7/disableSmsSignIn" -Method Post -Headers $headers -Body $UpdateUserSetting -ErrorAction Stop -UseBasicParsing
113 | }
114 | $newusersettings = Invoke-RestMethod -Uri "https://graph.microsoft.com/$($graphApiVersion)/users/$($UPN)/$($resource)" -Method get -Headers $headers;
115 | $newusersettings = $newusersettings | ConvertTo-Json
116 | write-host "New Authentication method settings for user $UPN." -ForegroundColor Green
117 | write-host $newusersettings -ForegroundColor Green
118 | }
119 |
120 | if (!$SMSSignIn){
121 | write-host "No SMSSignIn settings changed for $UPN!" -ForegroundColor Yellow
122 | }
123 |
--------------------------------------------------------------------------------
/Configure-AuthenticationMethods_including_token_request.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | .DESCRIPTION
3 | This script is able to change / provision the phone number of the end user used by MFA / SMS Signin
4 | Written by: Pim Jacobs (https://identity-man.eu)
5 |
6 | .PARAMETER UPN Required
7 | The UPN for which you want to add or change the phonenumber
8 |
9 | .PARAMETER ActionType Optional
10 | The actiontype for changes, which can either be Add, Update or Delete as action.
11 |
12 | .PARAMETER PhoneNumber Optional
13 | Enter the international phone number of the end user i.e. "+310612345678"
14 |
15 | .PARAMETER PhoneType Optional
16 | Choose between three values i.e. Mobile, AlternateMobile or Office
17 |
18 | .PARAMETER SMSSignin Optional
19 | The actiontype for the sms sign-in feature, which can either be Add, Update or Delete as action
20 |
21 | .EXAMPLE
22 | To read current settings
23 | Configure-MFAMethods.ps1 -UPN 'username@identity-man.eu'
24 |
25 | To update, add or delete settings
26 | Configure-MFAMethods.ps1 -UPN 'username@identity-man.eu' -ActionType '' -PhoneNumber '<+310612345678>' -PhoneType ''
27 |
28 | To enable or disable the SMSSignIn feature (only when the user is allowed to use this feature).
29 | Configure-MFAMethods.ps1 -UPN 'username@identity-man.eu' -SMSSignIn ''
30 | #>
31 |
32 | [CmdletBinding()]
33 | Param (
34 | [Parameter(Mandatory=$true)]
35 | [String]$UPN,
36 | [Parameter(Mandatory=$false)]
37 | [ValidateSet("Add", "Update", "Delete")]
38 | [String]$ActionType,
39 | [Parameter(Mandatory=$false)]
40 | [String]$PhoneNumber,
41 | [Parameter(Mandatory=$false)]
42 | [ValidateSet("Mobile", "AlternateMobile", "Office")]
43 | [String]$PhoneType,
44 | [Parameter(Mandatory=$false)]
45 | [ValidateSet("Enable", "Disable")]
46 | [String]$SMSSignIn
47 | )
48 |
49 | # =====================================================================================================================================
50 |
51 | #To request a token make sure you've installed the MSAL Module with the PS command Install-Module -Name MSAL.PS
52 |
53 | # Update this info
54 | $tenantDomain = 'tenantname.onmicrosoft.com' #Change to your tenant domain (tenantname.onmicrosoft.com)
55 | $clientId = '00000000-0000-0000-0000-000000000000' #Change to your AppID / ClientId
56 |
57 | # =====================================================================================================================================
58 |
59 | function New-Auth {
60 |
61 | param($aR)
62 |
63 | #Try silently getting a new token
64 | if ($aR) {
65 | $user = $aR.Account.Username
66 | $aR = $null
67 | $aR = Get-MsalToken -TenantId $tenantDomain -ClientId $clientId -RedirectUri 'urn:ietf:wg:oauth:2.0:oob' -LoginHint $user
68 | } else {
69 | # Interactive auth required
70 | $aR = Get-MsalToken -TenantId $tenantDomain -ClientId $clientId -RedirectUri 'urn:ietf:wg:oauth:2.0:oob' -Interactive
71 | }
72 | return $aR
73 | }
74 |
75 | function New-AuthHeaders{
76 |
77 | $aH = $null
78 | $aH = New-Object 'System.Collections.Generic.Dictionary[[String],[String]]'
79 | $aH.Add('Authorization', 'Bearer ' + $authResult.AccessToken)
80 | $aH.Add('Content-Type','application/json')
81 | $aH.Add('Accept','application/json, text/plain')
82 |
83 | return $aH
84 |
85 | }
86 |
87 | function Test-TokenValidity {
88 |
89 | if ($authResult) {
90 | # We have an auth context
91 | if ($authResult.ExpiresOn.LocalDateTime -gt (Get-Date)) {
92 |
93 | # Token is still valid, nothing to do here.
94 | $remaining = $authResult.ExpiresOn.LocalDateTime - (Get-Date)
95 | Write-Host "Access Token valid for $remaining" -ForegroundColor Green
96 |
97 | } else {
98 | # Token expired, try to get a new one silently from the token cache
99 | Write-Host 'Access Token expired, getting new token silently' -ForegroundColor Green
100 | $script:authResult = New-Auth $authResult
101 | $script:authHeaders = New-AuthHeaders
102 |
103 | }
104 |
105 | } else {
106 | # No auth context, go interactive
107 | Write-Host "We need to authenticate first, select a user with the appropriate permissions" -ForegroundColor Green
108 | $script:authResult = New-Auth
109 | $script:authHeaders = New-AuthHeaders
110 | }
111 |
112 | }
113 |
114 | $ErrorActionPreference = 'Stop';
115 |
116 | #Verify Token and refresh Token if expired or not yet requested.
117 | Test-TokenValidity
118 |
119 | #MSGraphSettings
120 | $graphApiVersion = "beta";
121 | $resource = "authentication/phoneMethods";
122 |
123 | #Try to see if the user is currently enrolled and if so retrieve current value
124 | $currentusersetting = Invoke-RestMethod -Uri "https://graph.microsoft.com/$($graphApiVersion)/users/$($UPN)/$($resource)" -Method get -Headers $AuthHeaders;
125 | $Currentsettings = $currentusersetting | ConvertTo-Json
126 | write-host "Current Authentication method settings for user $UPN." -ForegroundColor Yellow
127 | write-host $Currentsettings -ForegroundColor Yellow
128 | #endregion
129 |
130 | if ($ActionType -eq "Update"){
131 | $Method = "put"
132 | }
133 |
134 | if ($ActionType -eq "Delete"){
135 | $Method = "Delete"
136 | }
137 |
138 | if ($ActionType){
139 | $UpdateUserSetting = @{ phonetype=$phonetype;phonenumber=$phonenumber}
140 | $UpdateUserSetting = ConvertTo-Json -InputObject $UpdateUserSetting
141 |
142 | if ($ActionType -eq "Add") {
143 | $ExecuteUpdateUserSetting = Invoke-RestMethod -Uri "https://graph.microsoft.com/$($graphApiVersion)/users/$($UPN)/$($resource)" -Method Post -Headers $AuthHeaders -Body $UpdateUserSetting -ErrorAction Stop -UseBasicParsing
144 | }
145 |
146 | if (($ActionType -eq "Update" -or $ActionType -eq "Delete") -and $PhoneType -like "Mobile") {
147 | $ExecuteUpdateUserSetting = Invoke-RestMethod -Uri "https://graph.microsoft.com/$($graphApiVersion)/users/$($UPN)/$($resource)/3179e48a-750b-4051-897c-87b9720928f7" -Method $Method -Headers $AuthHeaders -Body $UpdateUserSetting -ErrorAction Stop -UseBasicParsing
148 | }
149 |
150 | if (($ActionType -eq "Update" -or $ActionType -eq "Delete") -and $PhoneType -like "AlternateMobile") {
151 | $ExecuteUpdateUserSetting = Invoke-RestMethod -Uri "https://graph.microsoft.com/$($graphApiVersion)/users/$($UPN)/$($resource)/b6332ec1-7057-4abe-9331-3d72feddfe41" -Method $Method -Headers $AuthHeaders -Body $UpdateUserSetting -ErrorAction Stop -UseBasicParsing
152 | }
153 |
154 | if (($ActionType -eq "Update" -or $ActionType -eq "Delete") -and $PhoneType -like "Office") {
155 | $ExecuteUpdateUserSetting = Invoke-RestMethod -Uri "https://graph.microsoft.com/$($graphApiVersion)/users/$($UPN)/$($resource)/e37fc753-ff3b-4958-9484-eaa9425c82bc" -Method $Method -Headers $AuthHeaders -Body $UpdateUserSetting -ErrorAction Stop -UseBasicParsing
156 | }
157 | $newusersettings = Invoke-RestMethod -Uri "https://graph.microsoft.com/$($graphApiVersion)/users/$($UPN)/$($resource)" -Method get -Headers $AuthHeaders;
158 | $newusersettings = $newusersettings | ConvertTo-Json
159 | write-host "New Authentication method settings for user $UPN." -ForegroundColor Green
160 | write-host $newusersettings -ForegroundColor Green
161 | }
162 |
163 | if (!$ActionType){
164 | write-host "No settings changed for $UPN!" -ForegroundColor Yellow
165 | }
166 |
167 | if ($SMSSignIn){
168 | if ($SMSSignIn -eq "Enable") {
169 | $ExecuteUpdateUserSetting = Invoke-RestMethod -Uri "https://graph.microsoft.com/$($graphApiVersion)/users/$($UPN)/$($resource)/3179e48a-750b-4051-897c-87b9720928f7/enableSmsSignIn" -Method Post -Headers $AuthHeaders -Body $UpdateUserSetting -ErrorAction Stop -UseBasicParsing
170 | }
171 | if ($SMSSignIn -eq "Disable") {
172 | $ExecuteUpdateUserSetting = Invoke-RestMethod -Uri "https://graph.microsoft.com/$($graphApiVersion)/users/$($UPN)/$($resource)/3179e48a-750b-4051-897c-87b9720928f7/disableSmsSignIn" -Method Post -Headers $AuthHeaders -Body $UpdateUserSetting -ErrorAction Stop -UseBasicParsing
173 | }
174 | $newusersettings = Invoke-RestMethod -Uri "https://graph.microsoft.com/$($graphApiVersion)/users/$($UPN)/$($resource)" -Method get -Headers $AuthHeaders;
175 | $newusersettings = $newusersettings | ConvertTo-Json
176 | write-host "New Authentication method settings for user $UPN." -ForegroundColor Green
177 | write-host $newusersettings -ForegroundColor Green
178 | }
179 |
180 | if (!$SMSSignIn){
181 | write-host "No SMSSignIn settings changed for $UPN!" -ForegroundColor Yellow
182 | }
183 |
--------------------------------------------------------------------------------
/Guest-IdentityLifecycleManagement.ps1:
--------------------------------------------------------------------------------
1 | #Configure tenant variables
2 | $AppClientId = Get-AutomationVariable -Name 'ApplicationID'
3 | $TenantId = Get-AutomationVariable -Name 'TenantID'
4 | $ClientSecret= Get-AutomationVariable -Name 'ClientSecret'
5 |
6 | #Configure connection to Graph API and make sure to retrieve access token
7 | $RequestBody = @{client_id=$AppClientId;client_secret=$ClientSecret;grant_type="client_credentials";scope="https://graph.microsoft.com/.default";}
8 | $OAuthResponse = Invoke-RestMethod -Method Post -Uri https://login.microsoftonline.com/$TenantId/oauth2/v2.0/token -Body $RequestBody
9 | $AccessToken = $OAuthResponse.access_token
10 |
11 | #Form request headers with the acquired $AccessToken
12 | $headers = @{'Content-Type'="application/json";'Authorization'="Bearer $AccessToken"}
13 |
14 | #This defines the filter we are applying to the guest acccount Graph call.
15 | $ApiUserUrl = "https://graph.microsoft.com/beta/users?`$filter=userType in ('Guest')&`$select=displayName,id,accountEnabled,userPrincipalName,signInActivity,userType,CreatedDateTime,ExternalUserState"
16 |
17 | #Reset variables
18 | $Today = Get-Date
19 | $DeletedUserCount = 0
20 | $DisabledUserCount = 0
21 | $ActiveUserCount = 0
22 | $DeletedUsers = @()
23 | $DisabledUsers = @()
24 | $ActiveUsers = @()
25 |
26 | #Perform pagination if next page link (odata.nextlink) returned.
27 | While ($ApiUserUrl -ne $Null) {
28 | #Retrieve all guest users with their properties
29 | $UserResponse = Invoke-WebRequest -Method GET -Uri $ApiUserUrl -ContentType "application/json" -Headers $headers –UseBasicParsing | ConvertFrom-Json
30 | #If the user response contains a value continue the script
31 | if($UserResponse.value) {
32 | $Users = $UserResponse.value
33 | #for each guest user found run the following loop
34 | ForEach($User in $Users) {
35 | #define variables or reset them
36 | $id = $user.id
37 | $count = 0
38 |
39 | #This defines the Graph call to retrieve if the user is a member of any groups.
40 | $ApiMemberOfUrl = "https://graph.microsoft.com/beta/users/$id/memberOf/microsoft.graph.group?"
41 |
42 | #Perform pagination if next page link (odata.nextlink) returned.
43 | While ($ApiMemberOfUrl -ne $Null) {
44 | #retrieve all group memberships
45 | $MemberOfResponse = Invoke-WebRequest -Method GET -Uri $ApiMemberOfUrl -ContentType "application/json" -Headers $headers –UseBasicParsing | ConvertFrom-Json
46 | $MemberOfGroups = $MemberOfResponse.value
47 |
48 | #if the user is a member of groups, exclude the dynamic groups from the count and fill count per user
49 | ForEach ($MemberOfGroup in $MemberOfGroups) {
50 | $GroupType = ""
51 | $GroupType = $MemberOfGroup.groupTypes
52 |
53 | if ($GroupType -notcontains 'DynamicMembership') {
54 | $count = $count + 1
55 | }
56 | }
57 |
58 | #if the count is equal to 0 this means the user doesn't have group memberships so we can either disable or delete the account based on activity
59 | if ($count -eq 0) {
60 | #First we check the activity of te account and check the state of the invite of the guest user.
61 | $DaysInvited = (New-TimeSpan -Start $User.CreatedDateTime -End $Today).Days
62 | $LastSignInDateTime = if($User.signInActivity.lastSignInDateTime) { [DateTime]$User.signInActivity.lastSignInDateTime } Else {$null}
63 | $ExternalUserState = $user.ExternalUserState
64 | $accountEnabled = $user.accountEnabled
65 |
66 | #if the lastsignindatetime is empty, the user hasn't accepted their invite and this is alrady the case for 30 days, cleanup the account.
67 | If (($LastSignInDateTime -eq $null) -and ($ExternalUserState -eq "PendingAcceptance") -and ($DaysInvited -gt 30)) {
68 | $userprincipalname = $User.userPrincipalName
69 | $ApiDeleteUserUrl = "https://graph.microsoft.com/beta/users/$id"
70 | $DeleteUserResponse = Invoke-WebRequest -Method DELETE -Uri $ApiDeleteUserUrl -ContentType "application/json" -Headers $headers –UseBasicParsing | ConvertFrom-Json
71 | $DeletedUserCount = $DeletedUserCount + 1
72 | $DeletedUsers = $DeletedUsers + "$userprincipalname is invited for more than 30 days ago and hasn't accepted yet, start deletion of guest account!`n"
73 | }
74 |
75 | #If the lastsignindatetime is empty (because this value was only there for 1,5 year) and the interactive sign-in happend before that time let's make sure to put a value in the system of -200 days.
76 | if ($LastSignInDateTime) {
77 | $DaysInactive = (New-TimeSpan -Start $LastSignInDateTime -End $Today).Days
78 | }
79 | else {
80 | $LastSignInDateTime = (get-date).AddDays(-200)
81 | $DaysInactive = (New-TimeSpan -Start $LastSignInDateTime -End $Today).Days
82 | }
83 |
84 | #If the lastsignindatetime is empty, the user did accept the invite and therefore can access the system but didn't use the account for more than 150 days but less than 179 let's disable the account.
85 | if (($LastSignInDateTime -ne $null) -and ($accountEnabled -ne $False) -and ($ExternalUserState -ne "PendingAcceptance") -and ($DaysInactive -gt 150) -and ($DaysInactive -lt 179)) {
86 | $userprincipalname = $User.userPrincipalName
87 | $ApiBlockUserUrl = "https://graph.microsoft.com/beta/users/$id"
88 | $Body = @{accountEnabled = "false"}
89 | $BlockUserResponse = Invoke-WebRequest -Method PATCH -Uri $ApiBlockUserUrl -ContentType "application/json" -body ($body | convertto-json -depth 5) -Headers $headers –UseBasicParsing | ConvertFrom-Json
90 | $DisabledUserCount = $DisabledUserCount + 1
91 | $DisabledUsers = $DisabledUsers + "$userprincipalname is inactive for more than 150 days, start disablement of guest account!`n"
92 |
93 | }
94 |
95 | #If the lastsignindatetime is empty, the user did accept the invite and therefore can access the system but didn't use the account for more than 180 days let's disable the account.
96 | if (($LastSignInDateTime -ne $null) -and ($ExternalUserState -ne "PendingAcceptance") -and ($DaysInactive -gt 180)) {
97 | $userprincipalname = $User.userPrincipalName
98 | $ApiDeleteUserUrl = "https://graph.microsoft.com/beta/users/$id"
99 | $DeleteUserResponse = Invoke-WebRequest -Method DELETE -Uri $ApiDeleteUserUrl -ContentType "application/json" -Headers $headers –UseBasicParsing | ConvertFrom-Json
100 | $DeletedUserCount = $DeletedUserCount + 1
101 | $DeletedUsers = $DeletedUsers + "$userprincipalname is inactive for more than 180 days, start deletion of guest account!`n"
102 | }
103 | }
104 |
105 | #If the user still has group memberships an access review should eventually trigger the removal of that membership whereby the user falls in scope for this deletion.
106 | Else {
107 | $userprincipalname = $User.userPrincipalName
108 | $ActiveUserCount = $ActiveUserCount + 1
109 | $ActiveUsers = $ActiveUsers + "$userprincipalname still has $count group memberships, skipping guest user for deletion!`n"
110 | }
111 |
112 | $ApiMemberOfUrl=$MemberOfResponse.'@odata.nextlink'
113 | }
114 | }
115 | }
116 |
117 | $ApiUserUrl=$UserResponse.'@odata.nextlink'
118 | }
119 |
120 | #Report summarized blocked and deleted accounts within the Output
121 | Write-output "The following $DisabledUserCount accounts are disabled:"
122 | Write-output "$DisabledUsers"
123 | Write-output "The following $DeletedUserCount accounts are deleted:"
124 | Write-output "$DeletedUsers"
125 | Write-output "The following $ActiveUserCount accounts are active and therefore not deleted or blocked:"
126 | Write-output "$ActiveUsers"
127 |
--------------------------------------------------------------------------------
/IdentityLifecycleManagement/la-001-identity-provisioning-api.json:
--------------------------------------------------------------------------------
1 | {
2 | "definition": {
3 | "$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#",
4 | "actions": {
5 | "Create_Job_-_RB-001-TransformHRUserInputforAPI": {
6 | "inputs": {
7 | "body": {
8 | "properties": {
9 | "parameters": {
10 | "UserDetails": "@body('Parse_JSON_-__Get_BambooHR_Employee_Info_Output')?['employees']"
11 | }
12 | }
13 | },
14 | "host": {
15 | "connection": {
16 | "name": "@parameters('$connections')['azureautomation']['connectionId']"
17 | }
18 | },
19 | "method": "put",
20 | "path": "/subscriptions/@{encodeURIComponent('faaeca84-60af-4e98-8846-ebf0944a5fba')}/resourceGroups/@{encodeURIComponent('rg-mwf-ipa-runbooks-p-weu-01')}/providers/Microsoft.Automation/automationAccounts/@{encodeURIComponent('aa-mwf-ipa-runbooks-p-weu-01')}/jobs",
21 | "queries": {
22 | "runbookName": "RB-001-TransformHRUserInputforAPI",
23 | "wait": true,
24 | "x-ms-api-version": "2015-10-31"
25 | }
26 | },
27 | "runAfter": {
28 | "Parse_JSON_-__Get_BambooHR_Employee_Info_Output": [
29 | "Succeeded"
30 | ]
31 | },
32 | "type": "ApiConnection"
33 | },
34 | "For_each_-_File_found_in_iparun-date_directory": {
35 | "actions": {
36 | "Get_blob_content_-_JSON_to_POST_to_API": {
37 | "inputs": {
38 | "host": {
39 | "connection": {
40 | "name": "@parameters('$connections')['azureblob']['connectionId']"
41 | }
42 | },
43 | "method": "get",
44 | "path": "/v2/datasets/@{encodeURIComponent(encodeURIComponent('stmwfipapweuukreciy'))}/files/@{encodeURIComponent(encodeURIComponent(items('For_each_-_File_found_in_iparun-date_directory')['Path']))}/content",
45 | "queries": {
46 | "inferContentType": true
47 | }
48 | },
49 | "runAfter": {},
50 | "type": "ApiConnection"
51 | },
52 | "HTTP_-_Post_to_Entra_ID_Provisioning_API": {
53 | "inputs": {
54 | "authentication": {
55 | "audience": "https://graph.microsoft.com",
56 | "type": "ManagedServiceIdentity"
57 | },
58 | "body": "@body('Get_blob_content_-_JSON_to_POST_to_API')",
59 | "headers": {
60 | "Content-Type": "application/scim+json"
61 | },
62 | "method": "POST",
63 | "uri": "@variables('EntraIDProvisioningAPIEndpoint')"
64 | },
65 | "runAfter": {
66 | "Get_blob_content_-_JSON_to_POST_to_API": [
67 | "Succeeded"
68 | ]
69 | },
70 | "type": "Http"
71 | }
72 | },
73 | "foreach": "@body('Parse_JSON_-_Lists_Blob_Files_Within_Container_iparun-date')",
74 | "runAfter": {
75 | "Parse_JSON_-_Lists_Blob_Files_Within_Container_iparun-date": [
76 | "Succeeded"
77 | ]
78 | },
79 | "type": "Foreach"
80 | },
81 | "Get_Job_Output_-_RB-001-TransformHRUserInputforAPI": {
82 | "inputs": {
83 | "host": {
84 | "connection": {
85 | "name": "@parameters('$connections')['azureautomation']['connectionId']"
86 | }
87 | },
88 | "method": "get",
89 | "path": "/subscriptions/@{encodeURIComponent('faaeca84-60af-4e98-8846-ebf0944a5fba')}/resourceGroups/@{encodeURIComponent('rg-mwf-ipa-runbooks-p-weu-01')}/providers/Microsoft.Automation/automationAccounts/@{encodeURIComponent('aa-mwf-ipa-runbooks-p-weu-01')}/jobs/@{encodeURIComponent(body('Create_Job_-_RB-001-TransformHRUserInputforAPI')?['properties']?['jobId'])}/output",
90 | "queries": {
91 | "x-ms-api-version": "2015-10-31"
92 | }
93 | },
94 | "runAfter": {
95 | "Create_Job_-_RB-001-TransformHRUserInputforAPI": [
96 | "Succeeded"
97 | ]
98 | },
99 | "type": "ApiConnection"
100 | },
101 | "Get_secret_-_BambooHR": {
102 | "inputs": {
103 | "host": {
104 | "connection": {
105 | "name": "@parameters('$connections')['keyvault']['connectionId']"
106 | }
107 | },
108 | "method": "get",
109 | "path": "/secrets/@{encodeURIComponent('BambooSecret')}/value"
110 | },
111 | "runAfter": {
112 | "Initialize_variable_-_EntraIDProvisioningAPIEndpoint": [
113 | "Succeeded"
114 | ]
115 | },
116 | "type": "ApiConnection"
117 | },
118 | "HTTP_-_Get_BambooHR_Employee_Info": {
119 | "inputs": {
120 | "authentication": {
121 | "password": "notrequired",
122 | "type": "Basic",
123 | "username": "@body('Get_secret_-_BambooHR')?['value']"
124 | },
125 | "headers": {
126 | "accept": "application/json"
127 | },
128 | "method": "GET",
129 | "uri": "https://api.bamboohr.com/api/gateway.php/jacobsaa/v1/employees/directory"
130 | },
131 | "runAfter": {
132 | "Get_secret_-_BambooHR": [
133 | "Succeeded"
134 | ]
135 | },
136 | "type": "Http"
137 | },
138 | "HTTP_-_Get_BambooHR_Employee_Info_(V2)": {
139 | "inputs": {
140 | "authentication": {
141 | "password": "notrequired",
142 | "type": "Basic",
143 | "username": "@body('Get_secret_-_BambooHR')?['value']"
144 | },
145 | "body": {
146 | "fields": [
147 | "DisplayName",
148 | "LastName",
149 | "FirstName",
150 | "middleName",
151 | "JobTitle",
152 | "Department",
153 | "division",
154 | "supervisorEid",
155 | "Country",
156 | "location",
157 | "homeEmail",
158 | "mobilePhone",
159 | "workPhone",
160 | "hireDate",
161 | "terminationDate"
162 | ]
163 | },
164 | "headers": {
165 | "Content-Type": "application/json"
166 | },
167 | "method": "POST",
168 | "uri": "https://api.bamboohr.com/api/gateway.php/jacobsaa/v1/reports/custom?format=JSON&onlyCurrent=false"
169 | },
170 | "runAfter": {
171 | "HTTP_-_Get_BambooHR_Employee_Info": [
172 | "Succeeded"
173 | ]
174 | },
175 | "type": "Http"
176 | },
177 | "Initialize_variable_-_EntraIDProvisioningAPIEndpoint": {
178 | "inputs": {
179 | "variables": [
180 | {
181 | "name": "EntraIDProvisioningAPIEndpoint",
182 | "type": "string",
183 | "value": "https://graph.microsoft.com/beta/servicePrincipals/9cd12299-c5db-479f-b831-d8193fa09aae/synchronization/jobs/API2AD.ad7aaf9de4784d3f99aace450535d9cc.09eca88a-f131-4013-8be1-d97c2f22b793/bulkUpload"
184 | }
185 | ]
186 | },
187 | "runAfter": {},
188 | "type": "InitializeVariable"
189 | },
190 | "Lists_Blob_Files_-_Within_Container_iparun-date": {
191 | "inputs": {
192 | "host": {
193 | "connection": {
194 | "name": "@parameters('$connections')['azureblob']['connectionId']"
195 | }
196 | },
197 | "method": "get",
198 | "path": "/v2/datasets/@{encodeURIComponent(encodeURIComponent('stmwfipapweuukreciy'))}/foldersV2/@{encodeURIComponent(encodeURIComponent(body('Parse_JSON_-_Get_Job_Output_RB-001-TransformHRUserInputforAPI')?['containername']))}",
199 | "queries": {
200 | "nextPageMarker": "",
201 | "useFlatListing": false
202 | }
203 | },
204 | "runAfter": {
205 | "Parse_JSON_-_Get_Job_Output_RB-001-TransformHRUserInputforAPI": [
206 | "Succeeded"
207 | ]
208 | },
209 | "type": "ApiConnection"
210 | },
211 | "Parse_JSON_-_Get_Job_Output_RB-001-TransformHRUserInputforAPI": {
212 | "inputs": {
213 | "content": "@body('Get_Job_Output_-_RB-001-TransformHRUserInputforAPI')",
214 | "schema": {
215 | "properties": {
216 | "containername": {
217 | "type": "string"
218 | }
219 | },
220 | "type": "object"
221 | }
222 | },
223 | "runAfter": {
224 | "Get_Job_Output_-_RB-001-TransformHRUserInputforAPI": [
225 | "Succeeded"
226 | ]
227 | },
228 | "type": "ParseJson"
229 | },
230 | "Parse_JSON_-_Lists_Blob_Files_Within_Container_iparun-date": {
231 | "inputs": {
232 | "content": "@body('Lists_Blob_Files_-_Within_Container_iparun-date')?['value']",
233 | "schema": {
234 | "items": {
235 | "properties": {
236 | "DisplayName": {
237 | "type": "string"
238 | },
239 | "ETag": {
240 | "type": "string"
241 | },
242 | "FileLocator": {
243 | "type": "string"
244 | },
245 | "Id": {
246 | "type": "string"
247 | },
248 | "IsFolder": {
249 | "type": "boolean"
250 | },
251 | "LastModified": {
252 | "type": "string"
253 | },
254 | "LastModifiedBy": {},
255 | "MediaType": {
256 | "type": "string"
257 | },
258 | "Name": {
259 | "type": "string"
260 | },
261 | "Path": {
262 | "type": "string"
263 | },
264 | "Size": {
265 | "type": "integer"
266 | }
267 | },
268 | "required": [
269 | "Id",
270 | "Name",
271 | "DisplayName",
272 | "Path",
273 | "LastModified",
274 | "Size",
275 | "MediaType",
276 | "IsFolder",
277 | "ETag",
278 | "FileLocator",
279 | "LastModifiedBy"
280 | ],
281 | "type": "object"
282 | },
283 | "type": "array"
284 | }
285 | },
286 | "runAfter": {
287 | "Lists_Blob_Files_-_Within_Container_iparun-date": [
288 | "Succeeded"
289 | ]
290 | },
291 | "type": "ParseJson"
292 | },
293 | "Parse_JSON_-__Get_BambooHR_Employee_Info_Output": {
294 | "inputs": {
295 | "content": "@body('HTTP_-_Get_BambooHR_Employee_Info_(V2)')",
296 | "schema": {
297 | "properties": {
298 | "employees": {
299 | "items": {
300 | "properties": {
301 | "country": {
302 | "type": [
303 | "string",
304 | "null"
305 | ]
306 | },
307 | "department": {
308 | "type": [
309 | "string",
310 | "null"
311 | ]
312 | },
313 | "displayName": {
314 | "type": [
315 | "string",
316 | "null"
317 | ]
318 | },
319 | "division": {
320 | "type": [
321 | "string",
322 | "null"
323 | ]
324 | },
325 | "firstName": {
326 | "type": [
327 | "string",
328 | "null"
329 | ]
330 | },
331 | "hireDate": {
332 | "type": [
333 | "string",
334 | "null"
335 | ]
336 | },
337 | "homeEmail": {},
338 | "id": {
339 | "type": [
340 | "string",
341 | "null"
342 | ]
343 | },
344 | "jobTitle": {
345 | "type": [
346 | "string",
347 | "null"
348 | ]
349 | },
350 | "lastName": {
351 | "type": [
352 | "string",
353 | "null"
354 | ]
355 | },
356 | "location": {
357 | "type": [
358 | "string",
359 | "null"
360 | ]
361 | },
362 | "middleName": {
363 | "type": [
364 | "string",
365 | "null"
366 | ]
367 | },
368 | "mobilePhone": {
369 | "type": [
370 | "string",
371 | "null"
372 | ]
373 | },
374 | "supervisor": {
375 | "type": [
376 | "string",
377 | "null"
378 | ]
379 | },
380 | "supervisorEid": {
381 | "type": [
382 | "string",
383 | "null"
384 | ]
385 | },
386 | "terminationDate": {
387 | "type": [
388 | "string",
389 | "null"
390 | ]
391 | },
392 | "workPhone": {
393 | "type": [
394 | "string",
395 | "null"
396 | ]
397 | }
398 | },
399 | "required": [
400 | "id",
401 | "displayName",
402 | "lastName",
403 | "firstName",
404 | "middleName",
405 | "jobTitle",
406 | "department",
407 | "division",
408 | "country",
409 | "location",
410 | "homeEmail",
411 | "mobilePhone",
412 | "workPhone",
413 | "hireDate",
414 | "terminationDate"
415 | ],
416 | "type": "object"
417 | },
418 | "type": "array"
419 | },
420 | "fields": {
421 | "items": {
422 | "properties": {
423 | "id": {
424 | "type": "string"
425 | },
426 | "name": {
427 | "type": "string"
428 | },
429 | "type": {
430 | "type": "string"
431 | }
432 | },
433 | "required": [
434 | "id",
435 | "type",
436 | "name"
437 | ],
438 | "type": "object"
439 | },
440 | "type": "array"
441 | },
442 | "title": {
443 | "type": "string"
444 | }
445 | },
446 | "type": "object"
447 | }
448 | },
449 | "runAfter": {
450 | "HTTP_-_Get_BambooHR_Employee_Info_(V2)": [
451 | "Succeeded"
452 | ]
453 | },
454 | "runtimeConfiguration": {
455 | "staticResult": {
456 | "name": "Parse_JSON_AFAS_Output0",
457 | "staticResultOptions": "Disabled"
458 | }
459 | },
460 | "type": "ParseJson"
461 | }
462 | },
463 | "contentVersion": "1.0.0.0",
464 | "outputs": {},
465 | "parameters": {
466 | "$connections": {
467 | "defaultValue": {},
468 | "type": "Object"
469 | }
470 | },
471 | "staticResults": {
472 | "Parse_JSON_AFAS_Output0": {
473 | "outputs": {
474 | "body": ""
475 | },
476 | "status": "Succeeded"
477 | }
478 | },
479 | "triggers": {
480 | "Recurrence": {
481 | "evaluatedRecurrence": {
482 | "frequency": "Hour",
483 | "interval": 1,
484 | "startTime": "2023-07-24T00:30:00Z",
485 | "timeZone": "W. Europe Standard Time"
486 | },
487 | "recurrence": {
488 | "frequency": "Hour",
489 | "interval": 1,
490 | "startTime": "2023-07-24T00:30:00Z",
491 | "timeZone": "W. Europe Standard Time"
492 | },
493 | "type": "Recurrence"
494 | }
495 | }
496 | },
497 | "parameters": {
498 | "$connections": {
499 | "value": {
500 | "azureautomation": {
501 | "connectionId": "/subscriptions/faaeca84-60af-4e98-8846-ebf0944a5fba/resourceGroups/rg-mwf-ipa-core-p-weu-01/providers/Microsoft.Web/connections/azureautomation",
502 | "connectionName": "azureautomation",
503 | "connectionProperties": {
504 | "authentication": {
505 | "type": "ManagedServiceIdentity"
506 | }
507 | },
508 | "id": "/subscriptions/faaeca84-60af-4e98-8846-ebf0944a5fba/providers/Microsoft.Web/locations/westeurope/managedApis/azureautomation"
509 | },
510 | "azureblob": {
511 | "connectionId": "/subscriptions/faaeca84-60af-4e98-8846-ebf0944a5fba/resourceGroups/rg-mwf-ipa-core-p-weu-01/providers/Microsoft.Web/connections/azureblob",
512 | "connectionName": "azureblob",
513 | "connectionProperties": {
514 | "authentication": {
515 | "type": "ManagedServiceIdentity"
516 | }
517 | },
518 | "id": "/subscriptions/faaeca84-60af-4e98-8846-ebf0944a5fba/providers/Microsoft.Web/locations/westeurope/managedApis/azureblob"
519 | },
520 | "keyvault": {
521 | "connectionId": "/subscriptions/faaeca84-60af-4e98-8846-ebf0944a5fba/resourceGroups/rg-mwf-ipa-core-p-weu-01/providers/Microsoft.Web/connections/keyvault",
522 | "connectionName": "keyvault",
523 | "connectionProperties": {
524 | "authentication": {
525 | "type": "ManagedServiceIdentity"
526 | }
527 | },
528 | "id": "/subscriptions/faaeca84-60af-4e98-8846-ebf0944a5fba/providers/Microsoft.Web/locations/westeurope/managedApis/keyvault"
529 | }
530 | }
531 | }
532 | }
533 | }
534 |
--------------------------------------------------------------------------------
/LifecycleWorkflows/RB-100-EnableAccountAndMbxInAD.ps1:
--------------------------------------------------------------------------------
1 | param (
2 | [Parameter (Mandatory = $true)]
3 | [object]$UserPrincipalname
4 | )
5 |
6 | #Import Modules
7 | Import-Module ActiveDirectory
8 | Add-PSSnapin *RecipientManagement
9 |
10 | #Set date time
11 | $datetime = Get-Date
12 |
13 | #Retrieve the user details
14 | $User = Get-AdUser -Filter {UserPrincipalName -eq $UserPrincipalname}
15 |
16 | #Enable account and set description
17 | Set-AdUser -identity $User.SamAccountName -Enabled $true
18 | Set-AdUser -identity $User.SamAccountName -Description "Enabled by LifeCycle Workflows onboarding Flow on: $datetime"
19 |
20 | $pos = $UserPrincipalname.IndexOf("@")
21 | $mailvalue = $UserPrincipalname.Substring(0, $pos)
22 |
23 | #Provision mailbox details in on-prem AD
24 | Enable-RemoteMailbox -identity $user.SamAccountName -RemoteRoutingAddress "$mailvalue@jacobsaa.mail.onmicrosoft.com" -alias $mailvalue -PrimarySMTPAddress $UserPrincipalname
25 |
--------------------------------------------------------------------------------
/LifecycleWorkflows/RB-400-ConvertMailboxEXO.ps1:
--------------------------------------------------------------------------------
1 | param (
2 | [Parameter (Mandatory = $true)]
3 | [object]$UserPrincipalname,
4 | [Parameter (Mandatory = $true)]
5 | [object]$ManagerMail
6 | )
7 |
8 | #Import Modules
9 | Import-Module ExchangeOnlineManagement
10 |
11 | #Connect to Exchange Online with Managed Identity
12 | Connect-ExchangeOnline -ManagedIdentity -Organization jacobsaa.onmicrosoft.com
13 |
14 | #Convert mailbox in Exchange Online to shared
15 | Set-Mailbox -identity $UserPrincipalname -type shared
16 | Write-output "Mailbox converted to shared"
17 |
18 | #Add manager to user mailbox in Exchange Online with 'Full Access'
19 | Add-MailboxPermission -Identity $UserPrincipalname -User $ManagerMail -AccessRights FullAccess -InheritanceType All
20 | Write-output "Manager added to mailbox"
21 |
--------------------------------------------------------------------------------
/LifecycleWorkflows/RB-400-DisableAccountAndConvertMbxInAD.ps1:
--------------------------------------------------------------------------------
1 | param (
2 | [Parameter (Mandatory = $true)]
3 | [object]$UserPrincipalname
4 | )
5 |
6 | #Import Modules
7 | Import-Module ActiveDirectory
8 | Add-PSSnapin *RecipientManagement
9 |
10 | #Define Variables
11 | $datetime = Get-Date
12 |
13 | #Disable user account and configure description
14 | $User = Get-AdUser -Filter {UserPrincipalName -eq $UserPrincipalname}
15 | Set-AdUser -identity $User.SamAccountName -Enabled $false
16 | Set-AdUser -identity $User.SamAccountName -Description "Disabled by LifeCycle Workflows offboarding Flow on: $datetime"
17 |
18 | #Convert mailbox in on-premises Active Directory to shared
19 | Set-RemoteMailbox -identity $user.SamAccountName -Type Shared
20 |
--------------------------------------------------------------------------------
/LifecycleWorkflows/RB-500-DeleteAccountInAD.ps1:
--------------------------------------------------------------------------------
1 | param (
2 | [Parameter (Mandatory = $true)]
3 | [object]$UserPrincipalname
4 | )
5 |
6 | #Import Modules
7 | Import-Module ActiveDirectory
8 |
9 | #Retrieve & Delete user account
10 | $User = Get-AdUser -Filter {UserPrincipalName -eq $UserPrincipalname}
11 | Remove-ADUser -Identity $User.SamAccountName -Confirm:$False
12 |
--------------------------------------------------------------------------------
/NordicVirtualSummit/LA-99-NordicsVirtualSummit:
--------------------------------------------------------------------------------
1 | {
2 | "definition": {
3 | "$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#",
4 | "actions": {
5 | "Compose_Variables": {
6 | "inputs": {
7 | "AdaptiveCardReceivers": "Pim.Jacobs@jacobsaa.nl",
8 | "AdaptiveCardTitle": "Cleanup of expired client secrets or certificates is required!",
9 | "AdaptiveCardTitle2": "ACTION REQUIRED: Your Service Principal needs attention!"
10 | },
11 | "runAfter": {},
12 | "type": "Compose"
13 | },
14 | "Main": {
15 | "actions": {
16 | "Create_Job_RB-99-NordicsVirtualSummit": {
17 | "inputs": {
18 | "host": {
19 | "connection": {
20 | "name": "@parameters('$connections')['azureautomation']['connectionId']"
21 | }
22 | },
23 | "method": "put",
24 | "path": "/subscriptions/@{encodeURIComponent('faaeca84-60af-4e98-8846-ebf0944a5fba')}/resourceGroups/@{encodeURIComponent('RG-Generic')}/providers/Microsoft.Automation/automationAccounts/@{encodeURIComponent('IdentityMan-Automation')}/jobs",
25 | "queries": {
26 | "runbookName": "RB-99-NordicsVirtualSummit",
27 | "wait": true,
28 | "x-ms-api-version": "2015-10-31"
29 | }
30 | },
31 | "runAfter": {},
32 | "type": "ApiConnection"
33 | },
34 | "For_each_App": {
35 | "actions": {
36 | "Condition": {
37 | "actions": {
38 | "Post_adaptive_card_for_Already_Expired_secret_or_certificate": {
39 | "inputs": {
40 | "body": {
41 | "messageBody": "{\n \"type\": \"AdaptiveCard\",\n \"body\": [\n {\n \"type\": \"TextBlock\",\n \"size\": \"Large\",\n \"weight\": \"Bolder\",\n \"text\": \"@{outputs('Compose_Variables')['AdaptiveCardTitle']}\",\n \"wrap\": true,\n \"style\": \"heading\"\n },\n {\n \"type\": \"TextBlock\",\n \"wrap\": true,\n \"text\": \"The application @{items('For_each_App')?['AppDisplayName']} has client secrets or certificates which are already expired.\",\n \"weight\": \"Default\"\n },\n {\n \"type\": \"TextBlock\",\n \"text\": \"The following information has been detected for this app:\",\n \"wrap\": true,\n \"weight\": \"Bolder\"\n },\n {\n \"type\": \"FactSet\",\n \"facts\": [\n {\n \"title\": \"Display Name:\",\n \"value\": \"@{items('For_each_App')?['AppDisplayName']}\"\n },\n {\n \"title\": \"Application ID:\",\n \"value\": \"@{items('For_each_App')?['AppId']}\"\n },\n {\n \"title\": \"Secret ID:\",\n \"value\": \"@{items('For_each_App')?['SecretId']}\"\n },\n {\n \"title\": \"Certificate ID:\",\n \"value\": \"@{items('For_each_App')?['CertificateId']}\"\n },\n {\n \"title\": \"Expiration Date:\",\n \"value\": \"@{items('For_each_App')?['Expires']}\"\n }\n ]\n },\n {\n \"type\": \"TextBlock\",\n \"wrap\": true,\n \"text\": \"It's time to get your environment cleaned up, please click on the button below to go directly to the affected Service Principal and cleanup the expired secret(s) or certificate(s)!\"\n }\n ],\n \"actions\": [\n {\n \"type\": \"Action.OpenUrl\",\n \"title\": \"Go to the Service Principal\",\n \"data\": false,\n \"url\": \"https://portal.azure.com/#blade/Microsoft_AAD_RegisteredApps/ApplicationMenuBlade/Credentials/appId/@{items('For_each_App')?['AppId']}/isMSAApp/\",\n \"iconUrl\": \"https://www.freeiconspng.com/uploads/rocket-icon-png-21.png\"\n }\n ],\n \"$schema\": \"http://adaptivecards.io/schemas/adaptive-card.json\",\n \"version\": \"1.4\"\n}",
42 | "recipient": "@{outputs('Compose_Variables')['AdaptiveCardReceivers']}"
43 | },
44 | "host": {
45 | "connection": {
46 | "name": "@parameters('$connections')['teams']['connectionId']"
47 | }
48 | },
49 | "method": "post",
50 | "path": "/v1.0/teams/conversation/adaptivecard/poster/Flow bot/location/@{encodeURIComponent('Chat with Flow bot')}"
51 | },
52 | "runAfter": {},
53 | "type": "ApiConnection"
54 | }
55 | },
56 | "else": {
57 | "actions": {
58 | "Post_adaptive_card_for_secret_or_certificate_which_is_due_to_expire": {
59 | "inputs": {
60 | "body": {
61 | "messageBody": "{\n \"type\": \"AdaptiveCard\",\n \"body\": [\n {\n \"type\": \"TextBlock\",\n \"size\": \"Large\",\n \"weight\": \"Bolder\",\n \"text\": \"@{outputs('Compose_Variables')['AdaptiveCardTitle2']}\",\n \"wrap\": true,\n \"style\": \"heading\",\n \"color\": \"Attention\"\n },\n {\n \"type\": \"TextBlock\",\n \"wrap\": true,\n \"text\": \"The application @{items('For_each_App')?['AppDisplayName']} has expiring client secrets or certificates, it therefore urgently requires your attention.\",\n \"weight\": \"Default\"\n },\n {\n \"type\": \"TextBlock\",\n \"text\": \"The following information has been detected for this app:\",\n \"wrap\": true,\n \"weight\": \"Bolder\"\n },\n {\n \"type\": \"FactSet\",\n \"facts\": [\n {\n \"title\": \"Display Name:\",\n \"value\": \"@{items('For_each_App')?['AppDisplayName']}\"\n },\n {\n \"title\": \"Application ID:\",\n \"value\": \"@{items('For_each_App')?['AppId']}\"\n },\n {\n \"title\": \"Secret ID:\",\n \"value\": \"@{items('For_each_App')?['SecretId']}\"\n },\n {\n \"title\": \"Certificate ID:\",\n \"value\": \"@{items('For_each_App')?['CertificateId']}\"\n },\n {\n \"title\": \"Expiration Date:\",\n \"value\": \"@{items('For_each_App')?['Expires']}\"\n }\n ]\n },\n {\n \"type\": \"TextBlock\",\n \"wrap\": true,\n \"text\": \"Please click on the button below to go directly to the affected Service Principal and make sure measurements are taken!\"\n }\n ],\n \"actions\": [\n {\n \"type\": \"Action.OpenUrl\",\n \"title\": \"Go to the Service Principal\",\n \"data\": false,\n \"url\": \"https://portal.azure.com/#blade/Microsoft_AAD_RegisteredApps/ApplicationMenuBlade/Credentials/appId/@{items('For_each_App')?['AppId']}/isMSAApp/\",\n \"iconUrl\": \"https://www.freeiconspng.com/uploads/rocket-icon-png-21.png\"\n }\n ],\n \"$schema\": \"http://adaptivecards.io/schemas/adaptive-card.json\",\n \"version\": \"1.4\"\n}",
62 | "recipient": "@{outputs('Compose_Variables')['AdaptiveCardReceivers']}"
63 | },
64 | "host": {
65 | "connection": {
66 | "name": "@parameters('$connections')['teams']['connectionId']"
67 | }
68 | },
69 | "method": "post",
70 | "path": "/v1.0/teams/conversation/adaptivecard/poster/Flow bot/location/@{encodeURIComponent('Chat with Flow bot')}"
71 | },
72 | "runAfter": {},
73 | "type": "ApiConnection"
74 | }
75 | }
76 | },
77 | "expression": {
78 | "and": [
79 | {
80 | "equals": [
81 | "@items('For_each_app')?['AlreadyExpired']",
82 | true
83 | ]
84 | }
85 | ]
86 | },
87 | "runAfter": {},
88 | "type": "If"
89 | },
90 | "Delay_untill_next_for_each_for_2_seconds_(due_to_throttling)": {
91 | "inputs": {
92 | "interval": {
93 | "count": 5,
94 | "unit": "Second"
95 | }
96 | },
97 | "runAfter": {
98 | "Condition": [
99 | "Succeeded"
100 | ]
101 | },
102 | "type": "Wait"
103 | }
104 | },
105 | "foreach": "@body('Parse_JSON_RB-99-NordicsVirtualSummit_Content')?['results']",
106 | "runAfter": {
107 | "Parse_JSON_RB-99-NordicsVirtualSummit_Content": [
108 | "Succeeded"
109 | ]
110 | },
111 | "runtimeConfiguration": {
112 | "concurrency": {
113 | "repetitions": 1
114 | }
115 | },
116 | "type": "Foreach"
117 | },
118 | "Get_blob_content_from_Job_Output": {
119 | "inputs": {
120 | "host": {
121 | "connection": {
122 | "name": "@parameters('$connections')['azureblob']['connectionId']"
123 | }
124 | },
125 | "method": "get",
126 | "path": "/v2/datasets/@{encodeURIComponent(encodeURIComponent('AccountNameFromSettings'))}/GetFileContentByPath",
127 | "queries": {
128 | "inferContentType": false,
129 | "path": "/nvsjson/@{body('Parse_JSON_from_Job_output')?['blobfile']}",
130 | "queryParametersSingleEncoded": true
131 | }
132 | },
133 | "runAfter": {
134 | "Parse_JSON_from_Job_output": [
135 | "Succeeded"
136 | ]
137 | },
138 | "type": "ApiConnection"
139 | },
140 | "Get_job_output_RB-99-NordicsVirtualSummit": {
141 | "inputs": {
142 | "host": {
143 | "connection": {
144 | "name": "@parameters('$connections')['azureautomation']['connectionId']"
145 | }
146 | },
147 | "method": "get",
148 | "path": "/subscriptions/@{encodeURIComponent('faaeca84-60af-4e98-8846-ebf0944a5fba')}/resourceGroups/@{encodeURIComponent('RG-Generic')}/providers/Microsoft.Automation/automationAccounts/@{encodeURIComponent('IdentityMan-Automation')}/jobs/@{encodeURIComponent(body('Create_Job_RB-99-NordicsVirtualSummit')?['properties']?['jobId'])}/output",
149 | "queries": {
150 | "x-ms-api-version": "2015-10-31"
151 | }
152 | },
153 | "runAfter": {
154 | "Create_Job_RB-99-NordicsVirtualSummit": [
155 | "Succeeded"
156 | ]
157 | },
158 | "type": "ApiConnection"
159 | },
160 | "Parse_JSON_RB-99-NordicsVirtualSummit_Content": {
161 | "inputs": {
162 | "content": "@json(decodeBase64(body('Get_blob_content_from_Job_Output')['$content']))",
163 | "schema": {
164 | "properties": {
165 | "results": {
166 | "items": [
167 | {
168 | "properties": {
169 | "AlreadyExpired": {
170 | "type": "boolean"
171 | },
172 | "AppDisplayName": {
173 | "type": "string"
174 | },
175 | "AppId": {
176 | "type": "string"
177 | },
178 | "CertificateID": {
179 | "type": "string"
180 | },
181 | "Expires": {
182 | "type": "string"
183 | },
184 | "SecretId": {
185 | "type": "string"
186 | }
187 | },
188 | "required": [
189 | "AppDisplayName",
190 | "AppId",
191 | "AlreadyExpired",
192 | "SecretId",
193 | "CertificateID",
194 | "Expires"
195 | ],
196 | "type": "object"
197 | }
198 | ],
199 | "type": "array"
200 | }
201 | },
202 | "required": [
203 | "results"
204 | ]
205 | }
206 | },
207 | "runAfter": {
208 | "Get_blob_content_from_Job_Output": [
209 | "Succeeded"
210 | ]
211 | },
212 | "type": "ParseJson"
213 | },
214 | "Parse_JSON_from_Job_output": {
215 | "inputs": {
216 | "content": "@concat('{',split(body('Get_job_output_RB-99-NordicsVirtualSummit'),'{')[1])",
217 | "schema": {
218 | "properties": {
219 | "blobfile": {
220 | "type": "string"
221 | }
222 | },
223 | "type": "object"
224 | }
225 | },
226 | "runAfter": {
227 | "Get_job_output_RB-99-NordicsVirtualSummit": [
228 | "Succeeded"
229 | ]
230 | },
231 | "type": "ParseJson"
232 | }
233 | },
234 | "runAfter": {
235 | "Compose_Variables": [
236 | "Succeeded"
237 | ]
238 | },
239 | "type": "Scope"
240 | }
241 | },
242 | "contentVersion": "1.0.0.0",
243 | "outputs": {},
244 | "parameters": {
245 | "$connections": {
246 | "defaultValue": {},
247 | "type": "Object"
248 | }
249 | },
250 | "triggers": {
251 | "Recurrence": {
252 | "evaluatedRecurrence": {
253 | "frequency": "Month",
254 | "interval": 3
255 | },
256 | "recurrence": {
257 | "frequency": "Month",
258 | "interval": 3
259 | },
260 | "type": "Recurrence"
261 | }
262 | }
263 | },
264 | "parameters": {
265 | "$connections": {
266 | "value": {
267 | "azureautomation": {
268 | "connectionId": "/subscriptions/faaeca84-60af-4e98-8846-ebf0944a5fba/resourceGroups/RG-NordicsVirtualSummit/providers/Microsoft.Web/connections/azureautomation",
269 | "connectionName": "azureautomation",
270 | "id": "/subscriptions/faaeca84-60af-4e98-8846-ebf0944a5fba/providers/Microsoft.Web/locations/westeurope/managedApis/azureautomation"
271 | },
272 | "azureblob": {
273 | "connectionId": "/subscriptions/faaeca84-60af-4e98-8846-ebf0944a5fba/resourceGroups/RG-NordicsVirtualSummit/providers/Microsoft.Web/connections/azureblob",
274 | "connectionName": "azureblob",
275 | "id": "/subscriptions/faaeca84-60af-4e98-8846-ebf0944a5fba/providers/Microsoft.Web/locations/westeurope/managedApis/azureblob"
276 | },
277 | "teams": {
278 | "connectionId": "/subscriptions/faaeca84-60af-4e98-8846-ebf0944a5fba/resourceGroups/RG-NordicsVirtualSummit/providers/Microsoft.Web/connections/teams",
279 | "connectionName": "teams",
280 | "id": "/subscriptions/faaeca84-60af-4e98-8846-ebf0944a5fba/providers/Microsoft.Web/locations/westeurope/managedApis/teams"
281 | }
282 | }
283 | }
284 | }
285 | }
286 |
--------------------------------------------------------------------------------
/NordicVirtualSummit/RB-99-NordicsVirtualSummit-Saml:
--------------------------------------------------------------------------------
1 | #Define variables
2 | $tenantId = Get-AutomationVariable -Name 'TenantName'
3 | $AppID = Get-AutomationVariable -Name 'NVSApplicationID'
4 | $AuthCertificate = Get-AutomationCertificate -Name 'NVS Service Principal'
5 | $StorageAccountName = Get-AutomationVariable -Name 'NVSStorageAccountName'
6 | $StorageAccountKey = Get-AutomationVariable -Name 'NVSStorageAccountKey'
7 | $datetimerun = Get-Date -Format "yyyyMMddHHmm"
8 | $CurrentDateTime = get-date
9 | $DateIn30Days = (get-date).AddDays(+30)
10 |
11 | #define arrays
12 | $objOut = @()
13 |
14 | #Connect to Graph
15 | Connect-MgGraph -CertificateThumbprint $AuthCertificate.thumbprint -TenantId $tenantId -ClientId $AppID
16 | #Select-MgProfile -Name "beta"
17 |
18 | #Retrieve all Service Principals
19 | $ServicePrincipals = Get-MgServicePrincipal -aLL
20 |
21 | Foreach ($ServicePrincipal in $ServicePrincipals) {
22 | $ServicePrincpalDisplayName = $ServicePrincipal.displayname
23 | $ServicePrincpalId = $ServicePrincipal.AppId
24 | $AppObjectId = $ServicePrincipal.Id
25 | $PasswordCredentials = $ServicePrincipal.passwordcredentials
26 |
27 | if ($PasswordCredentials) {
28 | Foreach ($PasswordCredential in $PasswordCredentials) {
29 | $FederatedSSOCertDateTime = $PasswordCredential.EndDateTime
30 | $FederatedSSOCertDateTime = [DateTime]$FederatedSSOCertDateTime
31 | $FederatedSSOCertId = $PasswordCredential.KeyId
32 | if ($ServicePrincipal.AccountEnabled -eq $true) {
33 | if ($FederatedSSOCertDateTime -lt $DateIn30Days) {
34 | if ($FederatedSSOCertDateTime -lt $CurrentDateTime) {
35 | $AlreadyExpired = $True
36 | # Create a JSON object for output to next step in Logic App workflow
37 | $objOut += [pscustomobject]@{
38 | AppDisplayName = $ServicePrincpalDisplayName
39 | AppId = $ServicePrincpalId
40 | AlreadyExpired = $AlreadyExpired
41 | Expires = $FederatedSSOCertDateTime
42 | CertificateID = $FederatedSSOCertId
43 | AppObjectId = $AppObjectId
44 | }
45 | #write-output "$ServicePrincpalDisplayName has an expired Federated SSO Certificate with ID $FederatedSSOCertId!"
46 | }
47 | else {
48 | $AlreadyExpired = $False
49 | # Create a JSON object for output to next step in Logic App workflow
50 | $objOut += [pscustomobject]@{
51 | AppDisplayName = $ServicePrincpalDisplayName
52 | AppId = $ServicePrincpalId
53 | AlreadyExpired = $AlreadyExpired
54 | SecretId = $AppSecretId
55 | Expires = $FederatedSSOCertDateTime
56 | CertificateID = $FederatedSSOCertId
57 | AppObjectId = $AppObjectId
58 | }
59 | #write-output "$ServicePrincpalDisplayName has an Federated SSO Certificate active with ID $FederatedSSOCertId which will expire in 30 days!"
60 | }
61 | }
62 | }
63 | }
64 | }
65 | }
66 |
67 | $objOutTotal = [pscustomobject]@{
68 | results = $objOut
69 | }
70 |
71 | $objOutTotalFile = New-TemporaryFile
72 |
73 | $Context = New-AzureStorageContext -StorageAccountName $StorageAccountName -StorageAccountKey $StorageAccountKey
74 | $MyRawString = $objOutTotal | ConvertTo-Json -Depth 99
75 | $Utf8NoBomEncoding = New-Object System.Text.UTF8Encoding $False
76 | [System.IO.File]::WriteAllLines($objOutTotalFile.FullName, $MyRawString, $Utf8NoBomEncoding)
77 |
78 | $filenameblob = "rb-99-NordicsVirtualSummit-Saml"+$datetimerun+".json"
79 | $storeblob = Set-AzureStorageBlobContent -Context $Context -Container nvsjson -File $objOutTotalFile.FullName -Blob $filenameblob -Properties @{"ContentEncoding" = "UTF-8"}
80 |
81 | $params = @{
82 | "blobfile"=$filenameblob;
83 | }
84 |
85 | Write-Output ( $params | ConvertTo-Json -Depth 99)
86 |
--------------------------------------------------------------------------------
/NordicVirtualSummit/RB-99-NordicsVirtualSummit-Saml.ps1:
--------------------------------------------------------------------------------
1 | #Define variables
2 | $tenantId = Get-AutomationVariable -Name 'TenantName'
3 | $AppID = Get-AutomationVariable -Name 'NVSApplicationID'
4 | $AuthCertificate = Get-AutomationCertificate -Name 'NVS Service Principal'
5 | $StorageAccountName = Get-AutomationVariable -Name 'NVSStorageAccountName'
6 | $StorageAccountKey = Get-AutomationVariable -Name 'NVSStorageAccountKey'
7 | $datetimerun = Get-Date -Format "yyyyMMddHHmm"
8 | $CurrentDateTime = get-date
9 | $DateIn30Days = (get-date).AddDays(+30)
10 |
11 | #define arrays
12 | $objOut = @()
13 |
14 | #Connect to Graph
15 | Connect-MgGraph -CertificateThumbprint $AuthCertificate.thumbprint -TenantId $tenantId -ClientId $AppID
16 | #Select-MgProfile -Name "beta"
17 |
18 | #Retrieve all Service Principals
19 | $ServicePrincipals = Get-MgServicePrincipal -aLL
20 |
21 | Foreach ($ServicePrincipal in $ServicePrincipals) {
22 | $ServicePrincpalDisplayName = $ServicePrincipal.displayname
23 | $ServicePrincpalId = $ServicePrincipal.AppId
24 | $AppObjectId = $ServicePrincipal.Id
25 | $PasswordCredentials = $ServicePrincipal.passwordcredentials
26 |
27 | if ($PasswordCredentials) {
28 | Foreach ($PasswordCredential in $PasswordCredentials) {
29 | $FederatedSSOCertDateTime = $PasswordCredential.EndDateTime
30 | $FederatedSSOCertDateTime = [DateTime]$FederatedSSOCertDateTime
31 | $FederatedSSOCertId = $PasswordCredential.KeyId
32 | if ($ServicePrincipal.AccountEnabled -eq $true) {
33 | if ($FederatedSSOCertDateTime -lt $DateIn30Days) {
34 | if ($FederatedSSOCertDateTime -lt $CurrentDateTime) {
35 | $AlreadyExpired = $True
36 | # Create a JSON object for output to next step in Logic App workflow
37 | $objOut += [pscustomobject]@{
38 | AppDisplayName = $ServicePrincpalDisplayName
39 | AppId = $ServicePrincpalId
40 | AlreadyExpired = $AlreadyExpired
41 | Expires = $FederatedSSOCertDateTime
42 | CertificateID = $FederatedSSOCertId
43 | AppObjectId = $AppObjectId
44 | }
45 | #write-output "$ServicePrincpalDisplayName has an expired Federated SSO Certificate with ID $FederatedSSOCertId!"
46 | }
47 | else {
48 | $AlreadyExpired = $False
49 | # Create a JSON object for output to next step in Logic App workflow
50 | $objOut += [pscustomobject]@{
51 | AppDisplayName = $ServicePrincpalDisplayName
52 | AppId = $ServicePrincpalId
53 | AlreadyExpired = $AlreadyExpired
54 | SecretId = $AppSecretId
55 | Expires = $FederatedSSOCertDateTime
56 | CertificateID = $FederatedSSOCertId
57 | AppObjectId = $AppObjectId
58 | }
59 | #write-output "$ServicePrincpalDisplayName has an Federated SSO Certificate active with ID $FederatedSSOCertId which will expire in 30 days!"
60 | }
61 | }
62 | }
63 | }
64 | }
65 | }
66 |
67 | $objOutTotal = [pscustomobject]@{
68 | results = $objOut
69 | }
70 |
71 | $objOutTotalFile = New-TemporaryFile
72 |
73 | $Context = New-AzureStorageContext -StorageAccountName $StorageAccountName -StorageAccountKey $StorageAccountKey
74 | $MyRawString = $objOutTotal | ConvertTo-Json -Depth 99
75 | $Utf8NoBomEncoding = New-Object System.Text.UTF8Encoding $False
76 | [System.IO.File]::WriteAllLines($objOutTotalFile.FullName, $MyRawString, $Utf8NoBomEncoding)
77 |
78 | $filenameblob = "rb-99-NordicsVirtualSummit-Saml"+$datetimerun+".json"
79 | $storeblob = Set-AzureStorageBlobContent -Context $Context -Container nvsjson -File $objOutTotalFile.FullName -Blob $filenameblob -Properties @{"ContentEncoding" = "UTF-8"}
80 |
81 | $params = @{
82 | "blobfile"=$filenameblob;
83 | }
84 |
85 | Write-Output ( $params | ConvertTo-Json -Depth 99)
86 |
--------------------------------------------------------------------------------
/NordicVirtualSummit/RB-99-NordicsVirtualSummit.ps1:
--------------------------------------------------------------------------------
1 | #Define variables
2 | $tenantId = Get-AutomationVariable -Name 'TenantName'
3 | $AppID = Get-AutomationVariable -Name 'NVSApplicationID'
4 | $AuthCertificate = Get-AutomationCertificate -Name 'NVS Service Principal'
5 | $StorageAccountName = Get-AutomationVariable -Name 'NVSStorageAccountName'
6 | $StorageAccountKey = Get-AutomationVariable -Name 'NVSStorageAccountKey'
7 | $datetimerun = Get-Date -Format "yyyyMMddHHmm"
8 | $CurrentDateTime = get-date
9 | $DateIn30Days = (get-date).AddDays(+30)
10 |
11 | #define arrays
12 | $objOut = @()
13 |
14 | #Connect to Graph
15 | Connect-MgGraph -CertificateThumbprint $AuthCertificate.thumbprint -TenantId $tenantId -ClientId $AppID
16 | #Select-MgProfile -Name "beta"
17 |
18 | #Retrieve all applications
19 | $Applications = Get-MgApplication -All
20 |
21 | Foreach ($Application in $Applications) {
22 | $AppDisplayName = $Application.displayname
23 | $AppId = $Application.AppId
24 | $PasswordCredentials = $Application.passwordcredentials
25 | $KeyCredentials = $Application.KeyCredentials
26 |
27 | if ($PasswordCredentials) {
28 | Foreach ($PasswordCredential in $PasswordCredentials) {
29 | $AppSecretDateTime = $PasswordCredential.EndDateTime
30 | $AppSecretDateTime = [DateTime]$AppSecretDateTime
31 | $AppSecretId = $PasswordCredential.KeyId
32 | if ($AppSecretDateTime -lt $DateIn30Days) {
33 |
34 | if ($AppSecretDateTime -lt $CurrentDateTime) {
35 | $AlreadyExpired = $True
36 | # Create a JSON object for output to next step in Logic App workflow
37 | $objOut += [pscustomobject]@{
38 | AppDisplayName = $AppDisplayName
39 | AppId = $AppId
40 | AlreadyExpired = $AlreadyExpired
41 | SecretId = $AppSecretId
42 | Expires = $AppSecretDateTime
43 | CertificateID = "NA"
44 | }
45 | #write-host "$AppDisplayName has expired client secret with ID $AppSecretId!"
46 | }
47 | else {
48 | $AlreadyExpired = $False
49 | # Create a JSON object for output to next step in Logic App workflow
50 | $objOut += [pscustomobject]@{
51 | AppDisplayName = $AppDisplayName
52 | AppId = $AppId
53 | AlreadyExpired = $AlreadyExpired
54 | SecretId = $AppSecretId
55 | Expires = $AppSecretDateTime
56 | CertificateID = "NA"
57 | }
58 | #write-host "$AppDisplayName client secret with ID $AppSecretId will expire in 30 days!"
59 | }
60 | }
61 | }
62 | }
63 |
64 | if ($KeyCredentials) {
65 | Foreach ($KeyCredential in $KeyCredentials) {
66 | $AppCertificateDateTime = $KeyCredential.EndDateTime
67 | $AppCertificateDateTime = [DateTime]$AppCertificateDateTime
68 | $AppCertificateId = $KeyCredential.KeyId
69 | if ($AppCertificateDateTime -lt $DateIn30Days) {
70 |
71 | if ($AppCertificateDateTime -lt $CurrentDateTime) {
72 | $AlreadyExpired = $True
73 | # Create a JSON object for output to next step in Logic App workflow
74 | $objOut += [pscustomobject]@{
75 | AppDisplayName = $AppDisplayName
76 | AppId = $AppId
77 | AlreadyExpired = $AlreadyExpired
78 | SecretId = "NA"
79 | CertificateID = $AppCertificateId
80 | Expires = $AppCertificateDateTime
81 | }
82 | #write-host "$AppDisplayName has expired authentication certificate with ID $AppCertificateId!"
83 | }
84 | else {
85 | $AlreadyExpired = $False
86 | # Create a JSON object for output to next step in Logic App workflow
87 | $objOut += [pscustomobject]@{
88 | AppDisplayName = $AppDisplayName
89 | AppId = $AppId
90 | AlreadyExpired = $AlreadyExpired
91 | SecretId = "NA"
92 | CertificateID = $AppCertificateId
93 | Expires = $AppCertificateDateTime
94 | }
95 | #write-host "$AppDisplayName authentication certificate with ID $AppCertificateId will expire in 30 days!"
96 | }
97 | }
98 | }
99 | }
100 |
101 | }
102 |
103 | $objOutTotal = [pscustomobject]@{
104 | results = $objOut
105 | }
106 |
107 | $objOutTotalFile = New-TemporaryFile
108 |
109 | $Context = New-AzureStorageContext -StorageAccountName $StorageAccountName -StorageAccountKey $StorageAccountKey
110 | $MyRawString = $objOutTotal | ConvertTo-Json -Depth 99
111 | $Utf8NoBomEncoding = New-Object System.Text.UTF8Encoding $False
112 | [System.IO.File]::WriteAllLines($objOutTotalFile.FullName, $MyRawString, $Utf8NoBomEncoding)
113 |
114 | $filenameblob = "rb-99-NordicsVirtualSummit"+$datetimerun+".json"
115 | $storeblob = Set-AzureStorageBlobContent -Context $Context -Container nvsjson -File $objOutTotalFile.FullName -Blob $filenameblob -Properties @{"ContentEncoding" = "UTF-8"}
116 |
117 | $params = @{
118 | "blobfile"=$filenameblob;
119 | }
120 |
121 | Write-Output ( $params | ConvertTo-Json -Depth 99)
122 |
--------------------------------------------------------------------------------
/Pre-ConfigureAuthMethods V2/AuthMethodsImport.csv:
--------------------------------------------------------------------------------
1 | UPN,Mobile,OfficePhone,AlternateMobile,ForcedUpdate,ForcedRemoval
2 | Johny.bravo@identity-man.eu,+310612345678,+310485123456,+310618765432,True,True
3 | donald.duck@identity-man.eu,+310687654321,+310486123456,+310623456789,True,True
4 |
--------------------------------------------------------------------------------
/Pre-ConfigureAuthMethods V2/Pre-ConfigureAuthMethods.ps1:
--------------------------------------------------------------------------------
1 | #install Microsoft.Graph module and dependancies and import required modules
2 | Install-Module Microsoft.Graph -AllowClobber -Force
3 | Import-Module Microsoft.Graph.Authentication
4 |
5 | #Connect based on a service principal
6 | Connect-MgGraph -ClientId 00000000-0000-0000-0000-000000000000 -TenantId 00000000-0000-0000-0000-000000000000 -CertificateThumbprint 0000000000000000000000000000000000000000
7 | Select-MgProfile -Name "beta"
8 |
9 | #if you want to connect interactive, comment the above two lines and use the ones mentioned below
10 | #Connect-MgGraph -Scopes "UserAuthenticationMethod.ReadWrite.All"
11 | #Select-MgProfile -Name "beta"
12 |
13 | #import-csv file
14 | $users = Import-Csv -Path "C:\Temp\AuthMethodsImport.csv" -Delimiter ","
15 |
16 | Foreach ($User in $Users) {
17 | Write-Host "Configuring authentication methods for user" $user.upn -ForegroundColor Green
18 | $results = Get-MgUserAuthenticationPhoneMethod -UserId $user.upn
19 |
20 | #Retrieve mobileresults from Results
21 | $mobileresult = $results | Where-Object {$_.phonetype -eq "Mobile"}
22 |
23 | #Reconfigure mobile field if a new value is presented.
24 | if ($User.Mobile) {
25 | if ($mobileresult.PhoneType -eq "Mobile") {
26 | if ($User.ForcedUpdate -eq $true) {
27 | Try {
28 | Remove-MgUserAuthenticationPhoneMethod -UserId $User.UPN -PhoneAuthenticationMethodId $mobileresult.Id -Erroraction Stop
29 | New-MgUserAuthenticationPhoneMethod -UserId $User.UPN -PhoneType Mobile -PhoneNumber $User.Mobile | Out-Null
30 | }
31 | Catch {
32 | Write-Host "Failed to update mobile authentication method as it's configured as the default for" $user.upn -ForegroundColor Yellow
33 | }
34 | }
35 | }
36 | Else {
37 | #NOTE: If the user is enabled for SMS Sign-in this number is automatically enabled for SMS Sign-in.
38 | New-MgUserAuthenticationPhoneMethod -UserId $User.UPN -PhoneType Mobile -PhoneNumber $User.Mobile | Out-Null
39 | }
40 | }
41 |
42 | Else {
43 | if (($User.ForcedRemoval -eq $true) -and ($mobileresult)) {
44 | Try {
45 | Remove-MgUserAuthenticationPhoneMethod -UserId $User.UPN -PhoneAuthenticationMethodId $mobileresult.Id -Erroraction Stop
46 | }
47 | Catch {
48 | Write-Host "Failed to delete mobile authentication method as it's configured as the default for" $user.upn -ForegroundColor Yellow
49 | }
50 |
51 | }
52 | }
53 |
54 | #Retrieve Alternatemobileresults from Results
55 | $Alternatemobileresult = $results | Where-Object {$_.phonetype -eq "AlternateMobile"}
56 |
57 | #Reconfigure Alternatemobile field if a new value is presented.
58 | if ($User.AlternateMobile) {
59 | if ($User.mobile) {
60 | if ($Alternatemobileresult.PhoneType -eq "AlternateMobile") {
61 | if ($User.ForcedUpdate -eq $true) {
62 | Try {
63 | Remove-MgUserAuthenticationPhoneMethod -UserId $User.UPN -PhoneAuthenticationMethodId $Alternatemobileresult.Id -Erroraction Stop
64 | New-MgUserAuthenticationPhoneMethod -UserId $User.UPN -PhoneType AlternateMobile -PhoneNumber $User.AlternateMobile | out-null
65 | }
66 | Catch {
67 | Write-Host "Failed to update Alternate Mobile authentication method as it's configured as the default for" $user.upn -ForegroundColor Yellow
68 | }
69 | }
70 | }
71 | Else {
72 | New-MgUserAuthenticationPhoneMethod -UserId $User.UPN -PhoneType AlternateMobile -PhoneNumber $User.AlternateMobile | Out-Null
73 | }
74 | }
75 | Else {
76 | Write-Host "Failed to update Alternate Mobile method as Mobile method is mandatory and not set for" $user.upn -ForegroundColor Yellow
77 | }
78 | }
79 |
80 | Else {
81 | if (($User.ForcedRemoval -eq $true) -and ($Alternatemobileresult)) {
82 | Try {
83 | Remove-MgUserAuthenticationPhoneMethod -UserId $User.UPN -PhoneAuthenticationMethodId $Alternatemobileresult.Id -Erroraction Stop
84 | }
85 | Catch {
86 | Write-Host "Failed to delete Alternate Mobile authentication method as it's configured as the default for" $user.upn -ForegroundColor Yellow
87 | }
88 | }
89 | }
90 |
91 | #Retrieve OfficePhoneResults from Results
92 | $OfficePhoneResults = $results | Where-Object {$_.phonetype -eq "Office"}
93 |
94 | #Reconfigure Office field if a new value is presented.
95 | if ($User.OfficePhone) {
96 | if ($OfficePhoneResults.PhoneType -eq "Office") {
97 | if ($User.ForcedUpdate -eq $true) {
98 | Try {
99 | Remove-MgUserAuthenticationPhoneMethod -UserId $User.UPN -PhoneAuthenticationMethodId $OfficePhoneResults.Id -Erroraction Stop
100 | New-MgUserAuthenticationPhoneMethod -UserId $User.UPN -PhoneType Office -PhoneNumber $User.OfficePhone | Out-Null
101 | }
102 | Catch {
103 | Write-Host "Failed to update Office Phone authentication method as it's configured as the default for" $user.upn -ForegroundColor Yellow
104 | }
105 | }
106 | }
107 | Else {
108 | New-MgUserAuthenticationPhoneMethod -UserId $User.UPN -PhoneType Office -PhoneNumber $User.OfficePhone | Out-Null
109 | }
110 | }
111 |
112 | Else {
113 | if (($User.ForcedRemoval -eq $true) -and ($OfficePhoneResults)) {
114 | Try {
115 | Remove-MgUserAuthenticationPhoneMethod -UserId $User.UPN -PhoneAuthenticationMethodId $OfficePhoneResults.Id -Erroraction Stop
116 | }
117 | Catch {
118 | Write-Host "Failed to delete Office Phone authentication method as it's configured as the default for" $user.upn -ForegroundColor Yellow
119 | }
120 | }
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/WPNinjas-Demo-GuestLifeCycle:
--------------------------------------------------------------------------------
1 | #Retrieve variables from automation account
2 | $tenantId = Get-AutomationVariable -Name 'TenantID'
3 | $AppID = Get-AutomationVariable -Name 'ApplicationID'
4 | $AuthCertificate = Get-AutomationCertificate -Name 'WPNinjas Service Principal'
5 |
6 | #Connect to environment
7 | Connect-AzAccount -ServicePrincipal -Tenant $tenantId -ApplicationId $AppID -CertificateThumbprint $AuthCertificate.thumbprint
8 | Connect-AzureAD -TenantId $tenantId -ApplicationId $AppID -CertificateThumbprint $AuthCertificate.thumbprint
9 |
10 | #Retrieve all user and get todays current date
11 | $GuestUsers = Get-AzureADUser -All $true | where {(($_.usertype -like "guest") -or ($_.userPrincipalName -like "*#EXT#@*")) -and ($_.UserState -eq "Accepted")}
12 | $Today = Get-Date
13 |
14 | Foreach ($GuestUser in $GuestUsers) {
15 | $UserGroupMembership = Get-AzureADUserMembership -ObjectId $GuestUser.ObjectId
16 | $UserGroupMembership = $UserGroupMembership.count
17 | If ($UserGroupMembership -eq '0') {
18 | $userprincipalname = $GuestUser.mail
19 | [string]$WorkspaceID = 'd4c05fa0-1652-45e8-9ed0-bda450c81ee2'
20 | $QuerySignInCount = 'SigninLogs | where TimeGenerated > ago(60d) | where UserPrincipalName == "' + $UserPrincipalName + '" | order by TimeGenerated desc nulls last | limit 1'
21 | $ResultsSignInCount = Invoke-AzOperationalInsightsQuery -WorkspaceId $WorkspaceID -Query $QuerySignInCount
22 | $AADSigninDate = $ResultsSignInCount.Results.TimeGenerated
23 |
24 | if ($AADSigninDate -like "") {
25 | $AADSigninDate = get-date
26 | $AADSigninDate = $AADSigninDate.AddDays(-62)
27 | }
28 |
29 | #Gather differences
30 | $DaysInactive = (New-TimeSpan -Start $AADSigninDate -End $Today).Days
31 | #write-output "Account $UserPrincipalname is inactive for $DaysInactive days"
32 |
33 | if (($DaysInactive -gt 30) -and ($DaysInactive -lt 60)) {
34 | write-output "Account $UserPrincipalname is inactive for $DaysInactive days, disabling the account"
35 | #Set-AzureADUser -ObjectId $guestuser.ObjectId -AccountEnabled $false
36 | }
37 |
38 | if ($DaysInactive -gt 60) {
39 | write-output "Account $UserPrincipalname is inactive for $DaysInactive days, removing the account"
40 | #Remove-AzureADUser -ObjectId $guestuser.ObjectId
41 | }
42 | }
43 | Else {
44 | write-output "Account $UserPrincipalname still has group memberships, skipping."
45 | }
46 | }
47 |
--------------------------------------------------------------------------------