PowerShell Gallery | azure-ad-license-status.psm1 1.2.6 (2023)

azure-ad-license-status.psm1

`
`
`
"
if($results['SKU'][$SKU]['availableCount']/$results['SKU'][$SKU]['minimumCount']*100-ge$SKUWarningThreshold_basic){
Add-Exit-Production""
}
otherif($results['SKU'][$SKU]['availableCount']/$results['SKU'][$SKU]['minimumCount']*100- The$SKUCriticalThreshold_basic){
$critical=$truth
Add-Exit-Production""
}
elsewhere{
Add-Exit-Production""
}
Add-Exit-Production""
}
Add-Exit-Production"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
Set-StrictMode-Version3.0

#region: CSS configuration
$style=@"

#endregion

#region: Helper functions
modeInitialization-Variables{
# Common
$script:outputs=[System.Text.StringBuilder]::young()
$script:results=@{}
$script:skuTranslate=[series]::young([char[]]((Invoke-WebRequest-Houri'https://download.microsoft.com/download/e/3/e/e3e9faf2-f28b-490a-9ada-c6089a1fc5b0/Product%20names%20and%20service%20plan%20identifiers%20for%csvlicens'-You are in BasicParsing).Content))|ConvertFrom-Csv
# Exchange Online
$script:EXOCmdlets=@("Receive-recipient",
"Get-DistributionGroupMember",
"Get-UnifiedGroupLinks",
"Get-ATPBuiltInProtectionRule",
"Get-ATPPprotectionPolicyRule",
"Get-AntiPhishRule",
"Get-AntiPhishPolicy",
'Get-SafeAttachmentRule',
"Get-SafeAttachment Policy",
'Get-SafeLinksRule',
"Get-SafeLinksPolicy")
$script:EXOPproperties=@('ExchangeObjectId',
'ExternalDirectoryObjectId',
"PrimarySmtpAddress",
'RecipientTypeDetails')
$script:EXOTtypes_group=@('GroupMailbox',
'MailUniversalDistributionGroup',
'MailUniversalSecurityGroup')
$script:EXOTtypes_user=@("Shared Mailbox",
'UserMailbox')
# Graph
$script:pageSize=500
# Processing, process
$script:nestingLevel=0
}

modeWrite a message{
param(
[Parameter(Imperative=$truth)]
[ValidateNotNullOrEmpty()]
[series]$Message,
[ValidateSet('Error','polylogue')]
[series]$Type
)

$formattedMessage="[$([datetime]::Now.ToString('yyyy-MM-dd HH:mm:dd'))] $([string]::new('-', $nestingLevel)) $Message"
if($Type-ex'Error'){
Registration-Error-Message$formattedMessage-CategoryAuthentication error
}
otherif($Type-ex'polylogue'){
Write-Verbose-Message$formattedMessage
}
elsewhere{
$formattedMessage|Registration-Exit
}
}

modeAdd-Exit{
param(
[Parameter(Imperative=$truth)]
[ValidateNotNullOrEmpty()]
[series]$Exit
)

# Logging
$nestingLevel++
Write a message"Add Output"-TypeA polyglot
# Processing
$outputs.AppendLine($Exit)|Out-Null
$nestingLevel--
}

modeAdd-Result{
param(
[Parameter(Imperative=$truth,ParameterSetName='SKU')]
[ValidateNotNullOrEmpty()]
[guide]$SKIN,
[Parameter(Imperative=$truth,ParameterSetName='SKU')]
[ValidateNotNullOrEmpty()]
[UIint32]$AvailableCount,
[Parameter(Imperative=$truth,ParameterSetName='SKU')]
[ValidateNotNullOrEmpty()]
[UIint32]$MinimumCount,
[Parameter(Imperative=$truth,ParameterSetName='User')]
[ValidateNotNullOrEmpty()]
[series]$UserPrincipalName,
[Parameter(Imperative=$truth,ParameterSetName='User')]
[ValidateSet('Exchangeable',"Optimizable",'Transposable')]
[series]$ConflictType,
[Parameter(Imperative=$truth,ParameterSetName='User')]
[ValidateNotNullOrEmpty()]
[guide[]]$ConflictSKU,
[Parameter(Imperative=$truth,ParameterSetName='Advanced')]
[ValidateNotNullOrEmpty()]
[series]$PlanName,
[Parameter(Imperative=$truth,ParameterSetName='Advanced')]
[ValidateNotNullOrEmpty()]
[UIint32]$EnabledCount,
[Parameter(Imperative=$truth,ParameterSetName='Advanced')]
[ValidateNotNullOrEmpty()]
[UIint32]$NeededCount
)

# Logging
$nestingLevel++
Write a message"Add-Result"-TypeA polyglot
# Processing
if(-not$results.ContainsKey($PSCmdlet.ParameterSetName)){
$results.Addition($PSCmdlet.ParameterSetName,@{})
}
switch($PSCmdlet.ParameterSetName){
'Advanced'{
if(-not$results[$PSCmdlet.ParameterSetName].ContainsKey($PlanName)){
$results[$PSCmdlet.ParameterSetName].Addition($PlanName,@{
"enabledCount"=$EnabledCount;
'neededCount'=$NeededCount
})
}
}
'SKU'{
if(-not$results[$PSCmdlet.ParameterSetName].ContainsKey($SKIN)){
$results[$PSCmdlet.ParameterSetName].Addition($SKIN,@{
'availableCount'=$AvailableCount;
'minimumCount'=$MinimumCount
})
}
}
'User'{
if(-not$results[$PSCmdlet.ParameterSetName].ContainsKey($UserPrincipalName)){
$results[$PSCmdlet.ParameterSetName].Addition($UserPrincipalName,@{})
}
if(-not$results[$PSCmdlet.ParameterSetName][$UserPrincipalName].ContainsKey($ConflictType)){
$results[$PSCmdlet.ParameterSetName][$UserPrincipalName].Addition($ConflictType,$ConflictSKU)
}
}
}
$nestingLevel--
}

modeGet-AADGroupMembers{
[Output type([guide[]])]
param(
[Parameter(Imperative=$truth)]
[ValidateNotNullOrEmpty()]
[guide[]]$GroupIDs
)

# Logging
$nestingLevel++
Write a message"Get-AADGroupMembers"-TypeA polyglot
# Processing
$groupMembers=[System.Collections.Generic.List[hashtable]]::young()
for each($groupIDin$GroupIDs){
$URI='https://graph.microsoft.com/v1.0/groups/{0}/transitiveMembers?$select=id&$top={1}'- eat$groupID,$pageSize
while($null- is$URI){
$data=Invoke-MgGraphRequest-MethodI GET-Houri$URI
$groupMembers.AddRange([hashtable[]]($data.value))
$URI=$data['@odata.nextLink']
}
}
$groupMembers_unique=@($groupMembers.ID card|Option-Object-Unique)
Write a message"Found $($groupMembers_unique.Count) members"-TypeA polyglot
$nestingLevel--
Registration-Exit([guide[]]$groupMembers_unique)- No enumeration
}

modeGet-EXOGroupMembers{
[Output type([pscustomobject[]])]
param(
[Parameter(Imperative=$truth)]
[ValidateNotNullOrEmpty()]
[guide[]]$GroupIDs
)

# Logging
$nestingLevel++
Write a message"Get-EXOGroupMembers"-TypeA polyglot
# Processing
$groupMembers=[System.Collections.Generic.List[pscustomobject]]::young()
for each($groupIDin$GroupIDs){
if($null- is($team=[pscustomobject](Get-EXORrecipient$groupID.Guide-Recipient Type Details$EXOTtypes_group-Properties$EXOPProperties)|Option-Object-Property$EXOPProperties)){
switch($team.RecipientTypeDetails){
'GroupMailbox'{
$members=@(Get-UnifiedGroupLinks$team.ExchangeObjectId.Guide-Type of linkMembers-Result sizeUnlimited|Option-Object-Property$EXOPProperties)
}
Predefined{
$members=@(Get-DistributionGroupMember$team.ExchangeObjectId.Guide-Result sizeUnlimited|Option-Object-Property$EXOPProperties)
}
}
for each($memberin$members){
switch($member.RecipientTypeDetails){
{$_-in$EXOTypes_user}{
$groupMembers.Addition($member)
}
{$_-in$EXOTtypes_group}{
$groupMembers.AddRange((Get-EXOGroupMembers-GroupIDs$member.ExchangeObjectId))
}
}
}
}
}
$groupMembers_unique=@($groupMembers|Option-Object-Unique)
Write a message"Found $($groupMembers_unique.Count) members"-TypeA polyglot
$nestingLevel--
Registration-Exit([pscustomobject[]]$groupMembers_unique)- No enumeration
}

modeResolve-ATPRecipients{
[Output type([pscustomobject[]])]
param(
[Parameter(Imperative=$truth)]
[AllowNull()]
[series[]]$Users,
[Parameter(Imperative=$truth)]
[AllowNull()]
[series[]]$Groups,
[Parameter(Imperative=$truth)]
[AllowNull()]
[series[]]$Domains
)

# Logging
$nestingLevel++
Write a message"Resolve-ATPRecipients"-TypeA polyglot
# Processing
$categoryCount=0
$affectedAsUser=[System.Collections.Generic.List[pscustomobject]]::young()
$affectedAsGroup=[System.Collections.Generic.List[pscustomobject]]::young()
$affectedAsDomain=[System.Collections.Generic.List[pscustomobject]]::young()
if($null- is$Users){
$categoryCount++
if($null- is($recipients=[pscustomobject[]]@(Get-EXORrecipient-Recipient Type Details$EXOTypes_user-Properties$EXOPProperties-Result sizeUnlimited)|Option-Object-Property$EXOPProperties|Where-Object{$_.PrimarySmtpAddress-in$Users})){
$affectedAsUser.AddRange([pscustomobject[]]@($recipients))
}
}
Write a message"Found $($affectedAsUser.Count) recipients from users"-TypeA polyglot
if($null- is$Groups){
$categoryCount++
if($null- is($recipients=[pscustomobject[]]@(Get-EXORrecipient-Recipient Type Details$EXOTtypes_group-Properties$EXOPProperties-Result sizeUnlimited)|Option-Object-Property$EXOPProperties|Where-Object{$_.PrimarySmtpAddress-in$Groups})){
$affectedAsGroup.AddRange((Get-EXOGroupMembers-GroupIDs$recipients.ExchangeObjectId))
}
}
Write a message"Found $($affectedAsGroup.Count) recipients per groups"-TypeA polyglot
if($null- is$Domains){
$categoryCount++
if($null- is($recipients=[pscustomobject[]]@(Get-EXORrecipient-Recipient Type Details$EXOTypes_user-Properties$EXOPProperties-Result sizeUnlimited)|Option-Object-Property$EXOPProperties|Where-Object{$_.PrimarySmtpAddress.Division('@')[1]-in$Domains})){
$affectedAsDomain.AddRange([pscustomobject[]]@($recipients))
}
if($null- is($recipients=[pscustomobject[]]@(Get-EXORrecipient-Recipient Type Details$EXOTtypes_group-Properties$EXOPProperties-Result sizeUnlimited)|Option-Object-Property$EXOPProperties|Where-Object{$_.PrimarySmtpAddress.Division('@')[1]-in$Domains})){
$affectedAsDomain.AddRange((Get-EXOGroupMembers-GroupIDs$recipients.ExchangeObjectId))
}
}
Write a message"Found $($affectedAsDomain.Count) recipients by domains"-TypeA polyglot
if($null- is($resolvedUsers=@($affectedAsUser|Option-Object-Unique)+@($affectedAsGroup|Option-Object-Unique)+@($affectedAsDomain|Option-Object-Unique)|Group-Object-PropertyExchangeObjectId|Where-Object{$_.count-ex$categoryCount})){
$resolvedUsers_unique=@($resolvedUsers.Club|Option-Object-Unique)
Write a message"Found $($resolvedUsers_unique.Count) recipients per combination"-TypeA polyglot
$nestingLevel--
Registration-Exit([pscustomobject[]]$resolvedUsers_unique)- No enumeration
}
elsewhere{
Write a message"Found 0 recipients per combination"-TypeA polyglot
$nestingLevel--
Registration-Exit@([pscustomobject[]]::young(0))- No enumeration
}
}

modeGet-ATPRrecipients{
[Output type([pscustomobject[]])]
param(
[Parameter(Imperative=$truth)]
[AllowNull()]
[series[]]$IncludedUsers,
[Parameter(Imperative=$truth)]
[AllowNull()]
[series[]]$IncludedGroups,
[Parameter(Imperative=$truth)]
[AllowNull()]
[series[]]$IncludedDomains,
[Parameter(Imperative=$truth)]
[AllowNull()]
[series[]]$ExcludedUsers,
[Parameter(Imperative=$truth)]
[AllowNull()]
[series[]]$ExcludedGroups,
[Parameter(Imperative=$truth)]
[AllowNull()]
[series[]]$ExcludedDomains
)

# Logging
$nestingLevel++
Write a message"Get-ATPRecipients"-TypeA polyglot
# Processing
Write a message"Check Including Recipients"-TypeA polyglot
if($null-ex$IncludedUsers-and
$null-ex$IncludedGroups-and
$null-ex$IncludedDomains){
$userRecipients=[pscustomobject[]]@(Get-EXORrecipient-Recipient Type Details$EXOTypes_user-Properties$EXOPProperties-Result sizeUnlimited)|Option-Object-Property$EXOPProperties
$groupRecipients=Get-EXOGroupMembers-GroupIDs([pscustomobject[]]@(Get-EXORrecipient-Recipient Type Details$EXOTtypes_group-Properties$EXOPProperties-Result sizeUnlimited)).ExchangeObjectId
$includedRecipients=$userRecipients+$groupRecipients|Option-Object-Unique
}
elsewhere{
$includedRecipients=Resolve-ATPRecipients- Users$IncludedUsers-Teams$IncludedGroups- Sectors$IncludedDomains
}
Write a message"Found $($includedRecipients.Count) included recipients"-TypeA polyglot
Write a message"Check Blocked Recipients"-TypeA polyglot
if($null-ex$ExcludedUsers-and
$null-ex$ExcludedGroups-and
$null-ex$ExcludedDomains){
$excludedRecipients=@()
}
elsewhere{
$excludedRecipients=Resolve-ATPRecipients- Users$ExcludedUsers-Teams$ExcludedGroups- Sectors$ExcludedDomains
}
Write a message"Found $($excludedRecipients.Count) excluded recipients"-TypeA polyglot
Write a message"Check Affected Recipients"-TypeA polyglot
$affectedRecipients=[System.Collections.Generic.List[pscustomobject]]::young()
if($null- is($affectedRecipientComparison=Comparison-Object-Reference object$includedRecipients-Object of difference$excludedRecipients)){
if($null- is($affectedRecipientResults=$affectedRecipientComparison|Where-Object{$_.Side marker-ex'<='})){
$affectedRecipients.AddRange([pscustomobject[]]@($affectedRecipientResults.Input object))
}
}
$affectedRecipients_unique=@($affectedRecipients|Option-Object-Unique)
Write a message"Found $($affectedRecipients_unique.Count) affected recipients"-TypeA polyglot
$nestingLevel--
Registration-Exit([pscustomobject[]]$affectedRecipients_unique)- No enumeration
}

modeGet-SKUName{
[Output type([series])]
param(
[Parameter(Imperative=$truth)]
[ValidateNotNullOrEmpty()]
[guide]$SKIN
)

# Logging
$nestingLevel++
Write a message"Get-SKUName"-TypeA polyglot
# Processing
if($null- is($skuName=($skuTranslate|Where-Object{$_.GUIDE-ex$SKIN}).Product_Display_Name|Option-Object-Unique)){
$skuName=[cultureinfo]::young('to us').TextInfo.ToTitleCase($skuName.To reduce())
}
elsewhere{
$skuName=$SKIN
}
$nestingLevel--
Registration-Exit$skuName
}
#endregion

modeGet-AzureADLicenseStatus{
<#
.SUMMARY
Generates an Azure AD license report based on license assignments and consumption
.DESCRIPTION
This script is intended to combat the side effects of semi-automatic license assignments for Microsoft services in Azure AD, i.e. the combination of group-based licenses with manual management of group members, regularly reporting on both the number of available licenses per SKU and any conflicting permission assignments per user account. This allows for somewhat easier license management without either implementing a comprehensive software asset management solution or hiring a licensing service provider.

SKU IDs and names are as per https://learn.microsoft.com/en-us/azure/active-directory/enterprise-users/licensing-service-plan-reference
.PARAMETER Directory ID
Specifies the directory to connect to
.PARAMETER ApplicationID
Specifies the application in the target directory for authentication
.PARAMETER SubscriptionID
Specifies the subscription to the target directory to access
.PARAMETER KeyVaultName
Specifies the key vault in the destination subscription to access
.PARAMETER Name of certificate
Specifies the name of the certificate in the target keystore to use for authentication
.Certificate PARAMETER
Specifies the certificate to use for authentication
.PARAMETER CertificateThumb
Specifies the certificate thumbprint in the local certificate store to use for authentication
.PARAMETER Sender address
Specifies the sender address that will be used to deliver the report
.PARAMETER RecipientAddresses_normal
Specifies the recipient addresses that will be used to deliver the report
.PARAMETER RecipientAddresses_critical
Specifies the additional recipient addresses to be used for critical case report delivery
.PARAMETER SKUIgnoreThreshold
Specifies the minimum activated license limit for SKUs to be considered for the report
.PARAMETER SKUPPercentageThreshold_normal
Specifies the minimum available license percentage limit for SKUs to be included in the report
.SKU PARAMETERTotalThreshold_normal
Specifies the minimum available license amount limit for SKUs to be included in the report
.PARAMETER SKUPpercentageThreshold_important
Specifies the minimum available license percentage limit for SKUs to be included in the report
.SKU PARAMETERTotalThreshold_important
Specifies the minimum available license amount limit for SKUs to be included in the report
.PARAMETER SKUWarningThreshold_basic
Specifies the warning rate threshold to use when reporting for key controls
.PARAMETER SKUCriticalThreshold_basic
Specifies the critical percentage threshold to use when reporting for key controls
.PARAMETER SKUWarningThreshold_advanced
Specifies the warning rate threshold to use when generating a report for advanced checks
.PARAMETER SKUCriticalThreshold_advanced
Specifies the critical percentage threshold to use when generating a report for advanced checks
.PARAMETER ImportantSKU
It determines which SKUs are considered significant, so different thresholds are used for the calculation
.PARAMETERS InterchangeableSKU
Defines a list of SKUs that are considered interchangeable, e.g. Office 365 E1 and Office 365 E3
.PARAMETER LicensingURL
Specifies a licensing portal URL to link to in the report, it refers to the Microsoft License Volume Service Center by default
.PARAMETER AdvancedChecups
Specifies whether advanced license checks should be performed
CAUTION: Advanced checks require additional access permissions and may increase the duration of the check
.EXAMPLE
Get-AzureADLicenseStatus -DirectoryID '00000000-0000-0000-0000-0000000000000' -ApplicationID '00000000-0000-0000-0000-00000000000-0000-0000-00000000000-0000-0000-00000000000-0000-0000-00000000000-0000-0000-00000000000-0000-0000-0000000000000000-0000-00000000000 QRSTUVWXYZ' -SenderAddress 'sender@example.com' -RecipientAddresses_normal @('recipient_1@ example.com", "recipient_2@example.com")

Prepares a status report with default values ​​using only the parameters necessary to authenticate and deliver the report
.EXAMPLE
Get-AzureADLicenseStatus -DirectoryID '00000000-0000-0000-0000-0000000000000' -ApplicationID '00000000-0000-0000-0000-00000000000-0000-0000-00000000000-0000-0000-00000000000-0000-0000-00000000000-0000-0000-00000000000-0000-0000-0000000000000000-0000-00000000000 QRSTUVWXYZ' -SenderAddress 'sender@example.com' -RecipientAddresses_normal @('recipient_1@ example.com', 'recipient_2@example.com') -RecipientAddresses_critical @('recipient_3@example.com', 'recipient_4@example.com') -SKUPercentageThreshold_normal 1 -SKUTotalThreshold_normal 100 -SKUPotalThreshold_normal 100 -SKUPportimtalThreshold_SKUPer 500

Prepares a status report with custom thresholds for larger organizations and additional recipients for when license counts reach critical levels
.EXAMPLE
Get-AzureADLicenseStatus -DirectoryID '00000000-0000-0000-0000-0000000000000' -Αναγνωριστικό εφαρμογής '00000000-0000-0000-0000-00000000000-00000000000000000000000 000-0000-000000000000' -KeyVaultName 'MyKeyVault' -CertificateName' MyCertificate' -SenderAddress 'sender@example.com' -RecipientAddresses_normal @('recipient_1@example.com', 'recipient_2@example.com') -RecipientAddresses_critical @('recipient_3@example.com', 'recipient. ) -SKUPercentageThreshold_normal 1 -SKUTotalThreshold_normal 100 -SKUPercentageThreshold_important 1 -SKUTotalThreshold_important 500 -ImportantSKUs @('18181a46-0d4e-411e-74e-608 f-b296-42f0-b197-1e91e994b900') -Εναλλάξιμα SKU @('4b585984-651b- 448a-9e53-3b10f069cf7f', '18181a46-0d4e-45cd-891e-60aabd171b4e', '6fd2c87f-b296-42f0-b197-1e90f0-B197-1e91ef07-2006, 191e994, 2006, 2006 578-5b5392b571df') -AdvancedChecups

Prepares a status report using an Azure certificate for automation purposes, identifying both important and interchangeable SKUs and enabling advanced checks
#>

[CmdletBinding(Seat reservation=$false)]
param(
[Parameter(Imperative=$truth)]
[ValidateNotNullOrEmpty()]
[guide]$DirectoryID,
[Parameter(Imperative=$truth)]
[ValidateNotNullOrEmpty()]
[guide]$ApplicationID,
[Parameter(Imperative=$truth,ParameterSetName="Azure Certificate")]
[ValidateNotNullOrEmpty()]
[guide]$SubscriptionID,
[Parameter(Imperative=$truth,ParameterSetName="Azure Certificate")]
[ValidateNotNullOrEmpty()]
[series]$KeyVaultName,
[Parameter(Imperative=$truth,ParameterSetName="Azure Certificate")]
[ValidateNotNullOrEmpty()]
[series]$CertificateName,
[Parameter(Imperative=$truth,ParameterSetName="Local Certificate")]
[ValidateNotNullOrEmpty()]
[System.Security.Cryptography.X509Certificates.X509Certificate2]$Certificate,
[Parameter(Imperative=$truth,ParameterSetName='LocalCertificateThumbprint')]
[ValidateNotNullOrEmpty()]
[series]$CertificateThumbprint,
[Parameter(Imperative=$truth)]
[ValidateNotNullOrEmpty()]
[series]$SenderAddress,
[Parameter(Imperative=$truth)]
[ValidateNotNullOrEmpty()]
[series[]]$RecipientAddresses_normal,
[ValidateNotNullOrEmpty()]
[series[]]$RecipientAddresses_critical,
[ValidateNotNullOrEmpty()]
[UIint32]$SKUIgnoreThreshold=10,
[ValidateRange(0,100)]
[UIint16]$SKUPercentageThreshold_normal=5,
[ValidateNotNullOrEmpty()]
[UIint32]$SKUTotalThreshold_normal=10,
[ValidateRange(0,100)]
[UIint16]$SKUPercentageThreshold_important=5,
[ValidateNotNullOrEmpty()]
[UIint32]$SKUTotalThreshold_important=50,
[ValidateScript({$_-in1..99-and$_-gt$SKUCriticalThreshold_basic})]
[UIint16]$SKUWarningThreshold_basic=80,
[ValidateScript({$_-in1..99-and$_- l$SKUWarningThreshold_basic})]
[UIint16]$SKUCriticalThreshold_basic=20,
[ValidateScript({$_-in1..99-and$_-gt$SKUCriticalThreshold_advanced})]
[UIint16]$SKUWarningThreshold_advanced=99,
[ValidateScript({$_-in1..99-and$_- l$SKUWarningThreshold_advanced})]
[UIint16]$SKUCriticalThreshold_advanced=95,
[ValidateNotNullOrEmpty()]
[guide[]]$ImportantSKU=@(),
[ValidateNotNullOrEmpty()]
[guide[]]$InterchangeableSKU=@(),
[ValidateNotNullOrEmpty()]
[series]$LicensingURL="https://www.microsoft.com/licensing/servicecenter",
[switch]$AdvancedCheckups
)

Initialization-Variables
try it{
switch($PSCmdlet.ParameterSetName){
"Azure Certificate"{
Login-AzAccount-ID card-Subscription$SubscriptionID
$azureCertificateSecret=Get-AzKeyVaultSecret-VaultName$KeyVaultName-Name$CertificateName-AsPlainText
$azure certificate=[System.Security.Cryptography.X509Certificates.X509Certificate2]::young([Convert]::FromBase64String($azureCertificateSecret))
Logout-AzAccount
Connect-MgGraph-Certificate$azure certificate-TenantId$DirectoryID-Customer ID$ApplicationID-ErrorActionTo stop|Out-Null
}
"Local Certificate"{
Connect-MgGraph-Certificate$Certificate-TenantId$DirectoryID-Customer ID$ApplicationID-ErrorActionTo stop|Out-Null
}
'LocalCertificateThumbprint'{
Connect-MgGraph-Certificate imprint$CertificateThumbprint-TenantId$DirectoryID-Customer ID$ApplicationID-ErrorActionTo stop|Out-Null
}
}
$graphAuthentication=$truth
Write a message"Successfully authenticated with Graph"-TypeA polyglot
}
arrest{
$graphAuthentication=$false
Write a message-Message"Failed to authenticate with Graph"-TypeError
}
if($graphAuthentication){
#region: SKU
# Get SKU
$organizationSKU=[System.Collections.Generic.List[hashtable]]::young()
$URI="https://graph.microsoft.com/v1.0/subscribedSkus?$select=skuId,prepaidUnits,consumedUnits,servicePlans"
while($null- is$URI){
$data=Invoke-MgGraphRequest-MethodI GET-Houri$URI
$organizationSKU.AddRange([hashtable[]]($data.value))
$URI=$data['@odata.nextLink']
}
# SKU analysis
for each($SKUin$organizationSKU|Where-Object{$_.prepaid units.activated-gt$SKUIgnoreThreshold}){
$totalCount=$SKU.prepaid units.activated
$availableCount=$SKU.prepaid units.activated-$SKU.Units are consumed
if($SKU.skuId-in$ImportantSKU){
$percentageThreshold=$SKUPercentageThreshold_important
$totalThreshold=$SKUTotalThreshold_important
}
elsewhere{
$percentageThreshold=$SKUPercentageThreshold_normal
$totalThreshold=$SKUTotalThreshold_normal
}
$minimumCount=(@([System.Math]::Roof($totalCount*$percentageThreshold/100),$totalThreshold)|Measure-Object-Minimum).Minimum
if($availableCount- l$minimumCount){
Add-Result- SKIN$SKU.skuId-Available number$availableCount- Minimum crowd$minimumCount
}
}
$superiorSKUs_organization=@{}
for each($referenceSKUin$organizationSKU){
for each($differenceSKUin$organizationSKU|Where-Object{$_.skuId- is$referenceSKU.skuId}){
if($null- is($referenceServicePlans=$referenceSKU.Service Plans|Where-Object{$_.applies to-ex'User'})-and
$null- is($differenceServicePlans=$differenceSKU.Service Plans|Where-Object{$_.applies to-ex'User'})){
if($null- is($comparisonSKU=Comparison-Object-Reference object$referenceServicePlans.servicePlanId-Object of difference$differenceServicePlans.servicePlanId-Include equal)-and
$comparisonSKU.Side marker- contains'=='-and
$comparisonSKU.Side marker-does not contain'=>'){
if(-not$superiorSKUs_organization.ContainsKey($differenceSKU.skuId)){
$superiorSKUs_organization.Addition($differenceSKU.skuId,[System.Collections.Generic.List[driver]]::young())
}
$superiorSKUs_organization[$differenceSKU.skuId].Addition($referenceSKU.skuId)
}
}
}
}
Write a message"Found $($superiorSKUs_organization.Count) SKU matches for organization, excluding $($organizationSKUs.Count) SKUs"
#endregion

#region: Users
$userCount=0
$URI='https://graph.microsoft.com/v1.0/users?$select=id,licenseAssignmentStates,userPrincipalName&$top={0}'- eat$pageSize
while($null- is$URI){
# Retrieve users
$data=Invoke-MgGraphRequest-MethodI GET-Houri$URI
$users=[System.Collections.Generic.List[hashtable]]::young([hashtable[]]($data.value))
$userCount+=$users.count
$URI=$data['@odata.nextLink']
# Analyze users
for each($userin$users){
if($user.LicenseAssignmentStates.count-gt0){
if($null- is($userSKUAsignments=$user.LicenseAssignmentStates|Where-Object{$_.condition-ex'Active'-the$_.error-in@('Count Violation','Mutually Exclusive Violation')})){
$userSKU=$userSKUAsignments.skuId
}
elsewhere{
$userSKU=@()
}
if($null- is($count Violations=$user.LicenseAssignmentStates|Where-Object{$_.error-ex'Count Violation'})){
for each($count Violationin$count Violations.skuId|Option-Object-Unique){
$results['SKU'][$count Violation]['availableCount']-=1
}
}
# Identify interchangeable SKUs, based on specifications
$userSKUs_interchangeable=@()
if($null- is$userSKU){
if($null- is($comparison_interchangeable=Comparison-Object-Reference object$userSKU-Object of difference$InterchangeableSKU-They are excluded otherwise-Include equal)){
$userSKUs_interchangeable=@($comparison_interchangeable.Input object)
}
}
# Identify SKUs that can be optimized, based on organization-wide calculations
if($null- is($comparison_replaceableOrganization=$userSKU|Where-Object{$_-in$superiorSKUs_organization.Keys}|ForEach-Object{$superiorSKUs_organization[$_]})){
$userSKUs_optimizable=Comparison-Object-Reference object$userSKU-Object of difference$comparison_replaceableOrganization-They are excluded otherwise-Include equal|ForEach-Object{$superiorSKU=$_.Input object;$superiorSKUs_organization.Keys|Where-Object{$superiorSKUs_organization[$_]- contains$superiorSKU}}|Where-Object{$_-in$userSKU}|Option-Object-Unique
}
elsewhere{
$userSKUs_optimizable=$null
}
# Determine removable SKUs, based on user-level calculations
$skuid_enabledPlans=@{}
for each($ howeverin$user.LicenseAssignmentStates.however|Where-Object{$organizationSKU.skuId- contains$_}|Option-Object-Unique){
if(-not$skuid_enabledPlans.ContainsKey($ however)){
$skuid_enabledPlans.Addition($ however,[System.Collections.Generic.List[driver]]::young())
}
for each($assignmentin$user.LicenseAssignmentStates|Where-Object{$_.however-ex$ however}){
$skuid_enabledPlans[$ however].AddRange([guide[]]@((($organizationSKU|Where-Object{$_.however-ex$ however}).Service Plans|Where-Object{$_.servicePlanId- swimming$assignment.disabledPlans-and$_.applies to-ex'User'}).servicePlanId))
}
}
$superiorSKUs_user=@{}
for each($referenceSKUin$skuid_enabledPlans.Keys){
for each($differenceSKUin$skuid_enabledPlans.Keys|Where-Object{$_- is$referenceSKU}){
if($null- is($referenceServicePlans=$skuid_enabledPlans[$referenceSKU])-and
$null- is($differenceServicePlans=$skuid_enabledPlans[$differenceSKU])){
if($null- is($comparisonSKU=Comparison-Object-Reference object$referenceServicePlans-Object of difference$differenceServicePlans-Include equal)-and
$comparisonSKU.Side marker- contains'=='-and
$comparisonSKU.Side marker-does not contain'=>'){
if(-not$superiorSKUs_user.ContainsKey($differenceSKU)){
$superiorSKUs_user.Addition($differenceSKU,[System.Collections.Generic.List[driver]]::young())
}
$superiorSKUs_user[$differenceSKU].Addition($referenceSKU)
}
}
}
}
if($null- is($comparison_replaceableUser=$userSKU|Where-Object{$_-in$superiorSKUs_user.Keys}|ForEach-Object{$superiorSKUs_user[$_]})){
$userSKUs_removable=Comparison-Object-Reference object$userSKU-Object of difference$comparison_replaceableUser-They are excluded otherwise-Include equal|ForEach-Object{$superiorSKU=$_.Input object;$superiorSKUs_user.Keys|Where-Object{$superiorSKUs_user[$_]- contains$superiorSKU}}|Where-Object{$_-in$userSKU}|Option-Object-Unique
}
elsewhere{
$userSKUs_removable=$null
}
# Add results
if($userSKUs_interchangeable.count-gt1){
Write a message"Found $($userSKUs_interchangeable.Count) interchangeable SKUs for user $($user.userPrincipalName)"
Add-Result-UserPrincipalName$user.userPrincipalName-ConflictTypeExchangeable-ConflictSKU$userSKUs_interchangeable
}
if($null- is$userSKUs_optimizable){
Write a message"Found $(@($userSKUs_optimizable).Count) optimizeable SKUs for user $($user.userPrincipalName)"
Add-Result-UserPrincipalName$user.userPrincipalName-ConflictTypeOptimizeable-ConflictSKU$userSKUs_optimizable
}
if($null- is$userSKUs_removable){
Write a message"Found $(@($userSKUs_removable).Count) removable SKUs for user $($user.userPrincipalName)"
Add-Result-UserPrincipalName$user.userPrincipalName-ConflictTypeTransposable-ConflictSKU$userSKUs_removable
}
}
}
}
Write a message"Analyze $userCount users"
#endregion

#region: Advanced
if($AdvancedCheckups){
$AADP1Users=[System.Collections.Generic.List[driver]]::young()
$AADP2 Users=[System.Collections.Generic.List[driver]]::young()
$ATPUsers=[System.Collections.Generic.List[driver]]::young()
# Azure AD P1 based on groups using dynamic user subscription
$dynamicGroupCount=0
$URI='https://graph.microsoft.com/v1.0/groups?$filter=groupTypes/any(x:x eq ''DynamicMembership'')&$select=id,membershipRule&$top={0}'- eat$pageSize
while($null- is$URI){
# Retrieve dynamic groups
$data=Invoke-MgGraphRequest-MethodI GET-Houri$URI
$dynamicGroups=[System.Collections.Generic.List[hashtable]]::young([hashtable[]]($data.value))
$dynamicGroupCount+=$dynamicGroups.count
$URI=$data['@odata.nextLink']
# Analyze dynamic groups
if($null- is($dynamicUserGroups=$dynamicGroups|Where-Object{$_.Membership rule-like'*user.*'})){
$AADP1Users.AddRange((Get-AADGroupMembers-GroupIDs$dynamicUserGroups.ID card))
}
}
Write a message"Analyze dynamic groups $dynamicGroupCount"
# Azure AD P1 based applications that use group-based delegation
$applicationCount=0
$URI='https://graph.microsoft.com/v1.0/servicePrincipals?$filter=accountEnabled eq true και appRoleAssignmentRequired eq true και servicePrincipalType eq ''Application''&$top={0}&$count=true'- eat$pageSize
while($null- is$URI){
# Recover applications
$data=Invoke-MgGraphRequest-MethodI GET-Houri$URI-Heads@{'Consistency Level'='contingent'}
$apps=[System.Collections.Generic.List[hashtable]]::young([hashtable[]]($data.value))
$applicationCount+=$apps.count
$URI=$data['@odata.nextLink']
# Analyze applications
for each($applicationin$apps){
$applicationData=Invoke-MgGraphRequest-MethodI GET-Houri('https://graph.microsoft.com/v1.0/servicePrincipals/{0}?$expand=appRoleAssignedTo&$select=id,appRoleAssignedTo'- eat$application.ID card)
if($null- is($applicationGroups=$applicationData.appRoleAssignedTo|Where-Object{$_.Mr. Press-ex'Club'})){
$AADP1Users.AddRange((Get-AADGroupMembers-GroupIDs$applicationGroups.principalId))
}
}
}
Write a message"$applicationCount analyzed applications"
# Azure AD P1/P2 based on users covered by Conditional Access
$CAAADP1Policies=[System.Collections.Generic.List[driver]]::young()
$CAAADP2Policies=[System.Collections.Generic.List[driver]]::young()
$conditionalAccessPolicyCount=0
$URI='https://graph.microsoft.com/v1.0/identity/conditionalAccess/policies?$select=id,conditions,state'
while($null- is$URI){
# Retrieve conditional access policies
$data=Invoke-MgGraphRequest-MethodI GET-Houri$URI
$conditionalAccessPolicies=[System.Collections.Generic.List[hashtable]]::young([hashtable[]]($data.value))
$conditionalAccessPolicyCount+=$conditionalAccessPolicies.count
$URI=$data['@odata.nextLink']
# Analyze conditional access policies
if($null- is($CAPolicies=$conditionalAccessPolicies|Where-Object{$_.condition-ex"activated"-and$_.conditions.userRiskLevels.count-ex0-and$_.conditions.signInRiskLevels.count-ex0})){
$CAAADP1Policies.AddRange([guide[]]$CAPolicies.ID card)
}
if($null- is($CAPolicies=$conditionalAccessPolicies|Where-Object{$_.condition-ex"activated"-and($_.conditions.userRiskLevels.count-gt0-the$_.conditions.signInRiskLevels.count-gt0)})){
$CAAADP2Policies.AddRange([guide[]]$CAPolicies.ID card)
}
}
Write a message"Found $(@($CAAADP1Policies).Count) conditional access policies from $conditionalAccessPolicyCount policies"
Write a message"Found $(@($CAAADP2Policies).Count) risk-based conditional access policies, from $conditionalAccessPolicyCount policies"
$CAAADP1Users=[System.Collections.Generic.List[driver]]::young()
$CAAADP2Users=[System.Collections.Generic.List[driver]]::young()
$signInCount=0
$today=[date time]::Today
if(($today.Day of the WEEK-[System.DayOfWeek]::Friday)- l1){
$secondTimespanEnd=$today.AddDays(-($today.Day of the WEEK-[System.DayOfWeek]::Friday+7))
}
elsewhere{
$secondTimespanEnd=$today.AddDays(-($today.Day of the WEEK-[System.DayOfWeek]::Friday))
}
$secondTimespanStart=$secondTimespanEnd.AddDays(-4)
$firstTimespanEnd=$secondTimespanEnd.AddDays(-14)
$firstTimespanStart=$secondTimespanEnd.AddDays(-18)
$URI='https://graph.microsoft.com/v1.0/auditLogs/signIns?$filter=(conditionalAccessStatus eq ''success'' or conditionalAccessStatus eq ''failure'') and ((createdDateTime ge {0} and createdDateTime le {1}) or (createdDateTime ge {2} and createdDateTime le {3}))&$top={4}'- eat$firstTimespanStart.ToString('yyyy-MM-ddT12:00:00H'),$firstTimespanEnd.ToString('yyyy-MM-ddT12:00:00H'),$secondTimespanStart.ToString('yyyy-MM-ddT12:00:00H'),$secondTimespanEnd.ToString('yyyy-MM-ddT12:00:00H'),$pageSize
while($null- is$URI){
# Retrieve conditional access links
$data=Invoke-MgGraphRequest-MethodI GET-Houri$URI
$signIns=[System.Collections.Generic.List[hashtable]]::young([hashtable[]]($data.value))
$signInCount+=$signIns.count
$URI=$data['@odata.nextLink']
# Parse conditional access connections
for each($signInin$signIns){
if($null- is($appliedCAPolicies=$signIn.Conditional Access Policies applied|Where-Object{$_.result-in@('success','failure')})){
if($null- is$CAAADP1Policies){
if($null- is(Comparison-Object-Reference object$appliedCAPolicies.ID card-Object of difference$CAAADP1Policies-They are excluded otherwise-Include equal)){
$CAAADP1Users.Addition($signIn.user ID)
}
}
if($null- is$CAAADP2Policies){
if($null- is(Comparison-Object-Reference object$appliedCAPolicies.ID card-Object of difference$CAAADP2Policies-They are excluded otherwise-Include equal)){
$CAAADP2Users.Addition($signIn.user ID)
}
}
}
}
}
Write a message"Found $(@($CAAADP1Users | Select-Object -Unique).Count) users with basic conditional access logins, based on $signInCount logins"
Write a message"Found $(@($CAAADP2Users | Select-Object -Unique).Count) users with risk-based conditional access logins, based on $signInCount logins"
$AADP1Users.AddRange([guide[]]@($CAAADP1Users|Option-Object-Unique))
$AADP2 Users.AddRange([guide[]]@($CAAADP2Users|Option-Object-Unique))
Subtraction-Variable'CAAADP1Policies','CAAADP2Policies','CAAADP1 Users','CAAADP2Users'-Power
# Azure AD P2 based on users in Privileged Identity Management scope
$eligibleRoleMembers=[System.Collections.Generic.List[hashtable]]::young()
$URI="https://graph.microsoft.com/v1.0/roleManagement/directory/roleEligibilitySchedules?$select=principalId,scheduleInfo"
while($null- is$URI){
$data=Invoke-MgGraphRequest-MethodI GET-Houri$URI
$eligibleRoleMembers.AddRange([hashtable[]]($data.value))
$URI=$data['@odata.nextLink']
}
if($eligibleRoleMembers.count-gt0){
if($null- is($actuallyEligibleRoleMembers=$eligibleRoleMembers|Where-Object{$_.schedule Information.startDateTime- The[date time]::Today-and($_.schedule Information.expiry.endDateTime-ge[date time]::Today-the$_.schedule Information.expiry.type-ex'No expiration')})){
$AADP2 Users.AddRange([guide[]]@($actuallyEligibleRoleMembers.principalId))
}
}
Write a message"Resolved $($eligibleRoleMembers.Count) eligible role assignments"
# Defender for Office 365 P1/P2 based on https://learn.microsoft.com/office365/servicedescriptions/office-365-advanced-threat-protection-service-description#licensing-terms
$orgDomain=(Invoke-MgGraphRequest-MethodI GET-Houri"https://graph.microsoft.com/v1.0/organization?$select=verifiedDomains").value.verified domains|Where-Object{$_.is Initial-ex$truth}
try it{
switch($PSCmdlet.ParameterSetName){
"Azure Certificate"{
Connect-ExchangeOnline-AppId$ApplicationID-Certificate$azure certificate-Organization$orgDomain.name-CommandName$EXOCmdlets-ShowBanner:$false-ErrorActionTo stop
}
"Local Certificate"{
Connect-ExchangeOnline-AppId$ApplicationID-Certificate$Certificate-Organization$orgDomain.name-CommandName$EXOCmdlets-ShowBanner:$false-ErrorActionTo stop
}
'LocalCertificateThumbprint'{
Connect-ExchangeOnline-AppId$ApplicationID-Certificate imprint$CertificateThumbprint-Organization$orgDomain.name-CommandName$EXOCmdlets-ShowBanner:$false-ErrorActionTo stop
}
}
$exchangeAuthentication=$truth
Write a message"Authentication success with Exchange Online"-TypeA polyglot
}
arrest{
$exchangeAuthentication=$false
Write a message-Message"Authentication with Exchange Online failed"-TypeError
}
if($exchangeAuthentication){
if($null- is(Comparison-Object-Reference object$organizationSKU.Service Plans.servicePlanId-Object of difference@('f20fedf3-f3c3-43c3-8267-2bfdd51c0939','8e0c0a52-6a6c-4d40-8370-dd62790dcd70')-They are excluded otherwise-Include equal)){
# Protected mailboxes
if($null- is($organizationSKU|Where-Object{@($_.Service Plans.servicePlanId)- contains'8e0c0a52-6a6c-4d40-8370-dd62790dcd70'})){
$ATPvariant="DfOP2"
Write a message"Defender tenant specified for Office P2"
if($null- is($recipients=[pscustomobject[]]@(Get-EXORrecipient-Recipient Type Details$EXOTypes_user-Properties$EXOPProperties-Result sizeUnlimited)|Option-Object-Property$EXOPProperties)){
$ATPUsers.AddRange([guide[]]@($recipients.ExternalDirectoryObjectId))
Write a message"Found $($recipients.Count) affected/protected recipients"
}
}
elsewhere{
$ATPvariant="DfOP1"
Write a message"Found a Defender tenant for Office P1"
# Order of precedence according to https://learn.microsoft.com/en-us/microsoft-365/security/office-365-security/preset-security-policies?view=o365-worldwide#order-of-precedence- for -predefined-security-policies-and-other-policies
$matchedRecipients=[System.Collections.Generic.List[driver]]::young()
# Handle strict protection rules
if($null- is($strictProtectionRule=Get-ATPPprotectionPolicyRule-ID card"Strict predefined security policy"-ConditionEnabled-ErrorActionSilently Continue)){
Write a message"Strict ATP Rule"
if($null- is($recipients=Get-ATPRrecipients-IncludedUsers$strictProtectionRule.They are shipped-Included groups$strictProtectionRule.SentToMemberOf-IncludedDomains$strictProtectionRule.RecipientDomainIs-ExcludedUsers$strictProtectionRule.Unless Shipped-Excluded Groups$strictProtectionRule.Unless SentToMemberOf-ExcludedDomains$strictProtectionRule.ExceptIfRecipientDomainIs|Where-Object{$_.ExternalDirectoryObjectId- swimming$matchedRecipients})){
$matchedRecipients.AddRange([guide[]]@($recipients.ExternalDirectoryObjectId))
$ATPUsers.AddRange([guide[]]@($recipients.ExternalDirectoryObjectId))
Write a message"Found $($recipients.Count) affected/protected recipients"
}
}
# Handle standard protection rule
if($null- is($standardProtectionRule=Get-ATPPprotectionPolicyRule-ID card"Standard Default Security Policy"-ConditionEnabled-ErrorActionSilently Continue)){
Write a message"ATP Standard Rule"
if($null- is($recipients=Get-ATPRrecipients-IncludedUsers$standardProtectionRule.They are shipped-Included groups$standardProtectionRule.SentToMemberOf-IncludedDomains$standardProtectionRule.RecipientDomainIs-ExcludedUsers$standardProtectionRule.Unless Shipped-Excluded Groups$standardProtectionRule.Unless SentToMemberOf-ExcludedDomains$standardProtectionRule.ExceptIfRecipientDomainIs|Where-Object{$_.ExternalDirectoryObjectId- swimming$matchedRecipients})){
$matchedRecipients.AddRange([guide[]]@($recipients.ExternalDirectoryObjectId))
$ATPUsers.AddRange([guide[]]@($recipients.ExternalDirectoryObjectId))
Write a message"Found $($recipients.Count) affected/protected recipients"
}
}
# Handle custom protection rules
for each($customAntiPhishPolicyinGet-AntiPhishPolicy|Where-Object{$_.ID card- is"Office 365 AntiPhish Default"-and$_.RecommendedPolicyType- swimming@('Role model','Strict')}){
if(($customAntiPhishRule=Get-AntiPhishRule|Where-Object{$_.AntiPhishPolicy-ex$customAntiPhishPolicy.ID card}).condition-ex'Enabled'){
Write a message"Custom ATP Anti-Phishing Policy "$($customAntiPhishPolicy.Name)""
if($null- is($recipients=Get-ATPRrecipients-IncludedUsers$customAntiPhishRule.They are shipped-Included groups$customAntiPhishRule.SentToMemberOf-IncludedDomains$customAntiPhishRule.RecipientDomainIs-ExcludedUsers$customAntiPhishRule.Unless Shipped-Excluded Groups$customAntiPhishRule.Unless SentToMemberOf-ExcludedDomains$customAntiPhishRule.ExceptIfRecipientDomainIs|Where-Object{$_.ExternalDirectoryObjectId- swimming$matchedRecipients})){
$matchedRecipients.AddRange([guide[]]@($recipients.ExternalDirectoryObjectId))
if($customAntiPhishPolicy.Enabled){
$ATPUsers.AddRange([guide[]]@($recipients.ExternalDirectoryObjectId))
Write a message"Found $($recipients.Count) affected, $($recipients.Count) protected recipients"
}
elsewhere{
Write a message"Found $($recipients.Count) affected, 0 protected recipients"
}
}
}
}
for each($customSafeAttachmentPolicyinGet-SafeAttachment Policy|Where-Object{$_.IsBuiltInProtection-ex$false-and$_.RecommendedPolicyType- swimming@('Role model','Strict')}){
if(($customSafeAttachmentRule=Get-SafeAttachmentRule|Where-Object{$_.SafeAttachmentPolicy-ex$customSafeAttachmentPolicy.ID card}).condition-ex'Enabled'){
Write a message"Custom ATP Safe Attachment Policy "$($customSafeAttachmentPolicy.Name)""
if($null- is($recipients=Get-ATPRrecipients-IncludedUsers$customSafeAttachmentRule.They are shipped-Included groups$customSafeAttachmentRule.SentToMemberOf-IncludedDomains$customSafeAttachmentRule.RecipientDomainIs-ExcludedUsers$customSafeAttachmentRule.Unless Shipped-Excluded Groups$customSafeAttachmentRule.Unless SentToMemberOf-ExcludedDomains$customSafeAttachmentRule.ExceptIfRecipientDomainIs|Where-Object{$_.ExternalDirectoryObjectId- swimming$matchedRecipients})){
$matchedRecipients.AddRange([guide[]]@($recipients.ExternalDirectoryObjectId))
if($customSafeAttachmentPolicy.allow){
$ATPUsers.AddRange([guide[]]@($recipients.ExternalDirectoryObjectId))
Write a message"Found $($recipients.Count) affected, $($recipients.Count) protected recipients"
}
elsewhere{
Write a message"Found $($recipients.Count) affected, 0 protected recipients"
}
}
}
}
for each($customSafeLinksPolicyinGet-SafeLinksPolicy|Where-Object{$_.IsBuiltInProtection-ex$false-and$_.RecommendedPolicyType- swimming@('Role model','Strict')}){
if(($customSafeLinksRule=Get-SafeLinksRule|Where-Object{$_.SafeLinksPolicy-ex$customSafeLinksPolicy.ID card}).condition-ex'Enabled'){
Write a message"ATP Custom Safe Links Policy "$($customSafeLinksPolicy.Name)""
if($null- is($recipients=Get-ATPRrecipients-IncludedUsers$customSafeLinksRule.They are shipped-Included groups$customSafeLinksRule.SentToMemberOf-IncludedDomains$customSafeLinksRule.RecipientDomainIs-ExcludedUsers$customSafeLinksRule.Unless Shipped-Excluded Groups$customSafeLinksRule.Unless SentToMemberOf-ExcludedDomains$customSafeLinksRule.ExceptIfRecipientDomainIs|Where-Object{$_.ExternalDirectoryObjectId- swimming$matchedRecipients})){
$matchedRecipients.AddRange([guide[]]@($recipients.ExternalDirectoryObjectId))
if($customSafeLinksPolicy.EnableSafeLinksForEmail-the$customSafeLinksPolicy.EnableSafeLinksForOffice-the$customSafeLinksPolicy.EnableSafeLinksForTeams){
$ATPUsers.AddRange([guide[]]@($recipients.ExternalDirectoryObjectId))
Write a message"Found $($recipients.Count) affected, $($recipients.Count) protected recipients"
}
elsewhere{
Write a message"Found $($recipients.Count) affected, 0 protected recipients"
}
}
}
}
# Handle built-in protection rule
Write a message"Built-in ATP Rule"
$builtinProtectionRule=Get-ATPBuiltInProtectionRule
if($null- is($recipients=Get-ATPRrecipients-IncludedUsers$builtinProtectionRule.They are shipped-Included groups$builtinProtectionRule.SentToMemberOf-IncludedDomains$builtinProtectionRule.RecipientDomainIs-ExcludedUsers$builtinProtectionRule.Unless Shipped-Excluded Groups$builtinProtectionRule.Unless SentToMemberOf-ExcludedDomains$builtinProtectionRule.ExceptIfRecipientDomainIs|Where-Object{$_.ExternalDirectoryObjectId- swimming$matchedRecipients})){
$matchedRecipients.AddRange([guide[]]@($recipients.ExternalDirectoryObjectId))
$ATPUsers.AddRange([guide[]]@($recipients.ExternalDirectoryObjectId))
Write a message"Found $($recipients.Count) affected/protected recipients"
}
}
}
elsewhere{
$ATPvariant='EEA'
Write a message"Exchange Online Protection tenant specified"
}
Disconnect-ExchangeOnline-Confirm:$false
}
# Add results
if($AADP1Users.count-gt0){
if($null- is($AADP1SKU=@($organizationSKU|Where-Object{@($_.Service Plans.servicePlanId)- contains'41781fb2-bc02-4b7c-bd55-b576c07bb09d'}))){
$AADP1Permissions=($AADP1SKU.prepaid units.activated|Measure-Object-Sum).sum
}
elsewhere{
$AADP1Permissions=0
}
$neededCount=($AADP1Users|Option-Object-Unique).count
Write a message"Found $neededCount needed, $AADP1Licenses activated AADP1 licenses"
if($AADP1Permissions- l$neededCount){
Add-Result-Design name'Azure Active Directory Premium P1'-EnabledCount$AADP1Permissions-NeededCount$neededCount
}
}
if($AADP2 Users.count-gt0){
if($null- is($AADP2SKU=@($organizationSKU|Where-Object{@($_.Service Plans.servicePlanId)- contains'eec0eb4f-6444-4f95-aba0-50c24d67f998'}))){
$AADP2 Permissions=($AADP2SKU.prepaid units.activated|Measure-Object-Sum).sum
}
elsewhere{
$AADP2 Permissions=0
}
$neededCount=($AADP2 Users|Option-Object-Unique).count
Write a message"Found $neededCount needed, $AADP2Licenses activated AADP2 licenses"
if($AADP2 Permissions- l$neededCount){
Add-Result-Design name'Azure Active Directory Premium P2'-EnabledCount$AADP2 Permissions-NeededCount$neededCount
}
}
if($ATPUsers.count-gt0){
$neededCount=($ATPUsers|Option-Object-Unique).count
switch($ATPvariant){
"DfOP1"{
$ATPSKA=@($organizationSKU|Where-Object{@($_.Service Plans.servicePlanId)- contains'f20fedf3-f3c3-43c3-8267-2bfdd51c0939'})
$ATPLicenses=($ATPSKA.prepaid units.activated|Measure-Object-Sum).sum
Write a message"Found $neededCount needed, $ATPLicenses activated DfOP2 licenses"
if($ATPLicenses- l$neededCount){
Add-Result-Design name"Microsoft Defender for Office 365 P1"-EnabledCount$ATPLicenses-NeededCount$neededCount
}
}
"DfOP2"{
$ATPSKA=@($organizationSKU|Where-Object{@($_.Service Plans.servicePlanId)- contains'8e0c0a52-6a6c-4d40-8370-dd62790dcd70'})
$ATPLicenses=($ATPSKA.prepaid units.activated|Measure-Object-Sum).sum
Write a message"Found $neededCount needed, $ATPLicenses activated DfOP2 licenses"
if($ATPLicenses- l$neededCount){
Add-Result-Design name"Microsoft Defender for Office 365 P2"-EnabledCount$ATPLicenses-NeededCount$neededCount
}
}
}
}
}
#endregion

#region: Reference
if($results.values.count-gt0){
Add-Exit-Production$style
$critical=$false
# Extract key SKU results
Add-Exit-Production'

Basic control - Products

'
if($results.ContainsKey('SKU')){
Add-Exit-Production"

Check the number of licenses for the following product SKUs andholdadditional licenses:

(Video) [How To] Connect PS3 Controller To PC On Windows 10 (SCP DS3 Method)

`

"
License typeAvailable crowdMinimum crowdDifference


for each($SKUin$results['SKU'].Keys){
$differenceCount=$results['SKU'][$SKU]['availableCount']-$results['SKU'][$SKU]['minimumCount']
Add-Exit-Production"
$(Get-SKUName -SKUID $SKU)$($results['SKU'][$SKU]['availableCount'])$($results['SKU'][$SKU]['minimumCount'])
$differenceCount$differenceCount$differenceCount
`

The following criteria were used during the audit:

    `
  • Check products with >$SKUIgnoreThreshold total permissions
  • `
  • Report normal products that have both <$SKUTotalThreshold_normal licenses and <$SKUPercentageThreshold_normal% of their total available licenses
  • `
  • Report important products that have both <$SKUTotalThreshold_important licenses and <$SKUPercentageThreshold_important% of their total available licenses
"
}
elsewhere{
Add-Exit-Production'Nothing to report'
}
# Export advanced SKU results
Add-Exit-Production'

Advanced control - Products

(Video) How to install ionic in angular?

'

if($results.ContainsKey('Advanced')){
Add-Exit-Production"

Check the number of licenses for the following product SKUs andholdadditional licenses:

`

"
License typeActivated crowdRequired crowdDifference

for each($planin$results['Advanced'].Keys){
$differenceCount=$results['Advanced'][$plan]["enabledCount"]-$results['Advanced'][$plan]['neededCount']
Add-Exit-Production"`
$plan`
$($results['Advanced'][$plan]['enabledCount'])`
$($results['Advanced'][$plan]['neededCount'])"

if($results['Advanced'][$plan]["enabledCount"]/$results['Advanced'][$plan]['neededCount']*100-ge$SKUWarningThreshold_advanced){
Add-Exit-Production"$differenceCount"
}
otherif($results['Advanced'][$plan]["enabledCount"]/$results['Advanced'][$plan]['neededCount']*100- The$SKUCriticalThreshold_advanced){
$critical=$truth
Add-Exit-Production"$differenceCount"
}
elsewhere{
Add-Exit-Production"$differenceCount"
}
Add-Exit-Production""
}
Add-Exit-Production

The following criteria were used during the review:

(Video) [Video] Windows | ¿Qué es Windows 10 como Servicio? - 02/11/2015


  • check itAzure AD P1based on groups using dynamic user subscription

  • check itAzure AD P1based on applications that use group-based assignment

  • check itAzure AD P1based on users covered by Conditional Access

  • check itAzure AD P2based on users in the Privileged Identity Management field

  • Select itDefender for Office 365 P1/P2based on Exchange Online protected recipients
'

}
elsewhere{
Add-Exit-Production'Nothing to report'
}
# Extract basic user results
Add-Exit-Production'

Basic control - Users

'

if($results.ContainsKey('User')){
Add-Exit-Production«

Review the permission assignments for the following user accounts and mitigate the impact:


(Video) WSL: What WSL Is and Why You Should Care? Webcasti

'
AccountInterchangeableOptimizeableRemoved

for each($userin$results['User'].Keys|Classification-Object){
Add-Exit-Production"`
$user`
$(($results['User'][$user]['Inchangeable'] |
Where-Object{$null -ne $_} |
ForEach-Object{Get-SKUName -SKUID $_} |
Sort-Object) -join '
')`
$(($results['User'][$user]['Optimizable'] |
Where-Object{$null -ne $_} |
ForEach-Object{Get-SKUName -SKUID $_} |
Sort-Object) -join '
')`
$(($results['User'][$user]['Removable'] |
Where-Object{$null -ne $_} |
ForEach-Object{Get-SKUName -SKUID $_} |
Sort-Object) -join '
')`
"

}
Add-Exit-Production

The following criteria were used during the review:


  • Check accounts with any number of assigned licenses

  • Report theoretically exclusive licenses asinterchangeable, based on specified SKUs

  • Please refer to practice licenses asoptimisable, based on available SKU features

  • Do list the licenses asremovable, based on enabled SKU features
'

}
elsewhere{
Add-Exit-Production'Nothing to report'
}
# Configure and send email
$email=@{
'message'=@{
'theme'="Azure AD permissions need attention";
'significant'='normal';
'body'=@{
'Content Type'='HTML';
'content'=$outputs.ToString()
};
}
}
$email['message'].Addition("to Recipients",[System.Collections.Generic.List[hashtable]]::young())
for each($recipientAddressin$RecipientAddresses_normal){
$email['message']["to Recipients"].Addition(@{
'email adress'=@{
'address'=$recipientAddress
}
})
}
if($critical){
$email['message']['theme']="Azure AD licenses need urgent attention"
$email['message']['significant']='high'
$email['message'].Addition('ccRecipients',[System.Collections.Generic.List[hashtable]]::young())
for each($recipientAddressin$RecipientAddresses_critical){
$email['message']['ccRecipients'].Addition(@{
'email adress'=@{
'address'=$recipientAddress
}
})
}
}
Invoke-MgGraphRequest-MethodPOSITION-Houri("https://graph.microsoft.com/v1.0/users/{0}/sendMail"- eat$SenderAddress)-Body$email-Type of content'application/json'
}
#endregion

Disconnect-MgGraph|Out-Null
}
}

(Video) How To - Adding HTML Files into NGINX Web Server!

FAQs

What is P1 and P2 license in Azure? ›

A standalone Azure Premium P1 license costs $6 per user / per month, whereas Azure Premium P2 license cost $9 per user / per month. All member user accounts in the Azure AD tenant must be licensed. If your organization licenses Microsoft 365, then Microsoft 365 E3 licenses include Azure Active Directory Premium P1.

What is the minimum version of Microsoft Azure Active Directory AD for MFA? ›

To continue utilizing Azure Active Directory authentication with MFA, you need SSMS 18.6 or later.

What's the difference between Azure AD Premium P1 vs P2? ›

Azure AD Premium P2 includes all of the features of Azure AD Premium P1 - plus a few more useful capabilities, such as Identity Protection and Privileged Identity Management (PIM).

How do I check my Azure AD license status? ›

Sign in to the Azure portal using a License administrator account in your Azure AD organization. Select Azure Active Directory, and then select Licenses. Select All products to view the All Products page and to see the Total, Assigned, Available, and Expiring soon numbers for your license plans.

Which PowerShell command is used to authenticate to Azure? ›

To sign in interactively, use the Connect-AzAccount cmdlet. This cmdlet presents an interactive browser based login prompt by default.

Does every user need a P1 license? ›

Yes, the requirement is that the Azure AD Premium P1 license is applied to all users who make use of the feature. Azure AD has always been licensed per user and this applies to all Azure AD features. A proper license is required if a user benefits directly or indirectly from any feature covered by that license.

What is the difference between P1 and P2 license? ›

Once you've held your P1 licence for at least 12 months, the next step is getting your P2 licence (green Ps). You'll still have restrictions on your green Ps, but some of those restrictions will be different. You can either: apply for a P2 licence onlinelaunch.

How do I activate my P2 license in Azure? ›

If you have access to the free Azure AD Premium P2, go to the Directory + subscription filter > select the correct Azure Active Directory tenant. Then go to Azure Active Directory > Licenses > Overview. In the top right under "Quick tasks" select "Get a free trial." Select Azure AD Premium P2 and select Activate.

How many Azure AD licenses do I need? ›

Generally, you need to purchase one license per user, which allows up to 10 services, so if you have 100 users in your organization, then you would need 100 licenses. If you need more than 10 services, then you will need to purchase additional licenses.

Does Azure AD require MFA to join? ›

To secure user sign-in events in Azure AD, you can require multi-factor authentication (MFA). Enabling Azure AD Multi-Factor Authentication using Conditional Access policies is the recommended approach to protect users.

What is the highest role in Azure AD? ›

The Azure AD roles include: Global administrator – the highest level of access, including the ability to grant administrator access to other users and to reset other administrator's passwords.

Does every user need a Azure P2 license? ›

For more info. Note: Many Azure Active Directory (Azure AD) services require you to license each of your users or groups (and associated members) for that service. Only users with active licenses will be able to access and use the licensed Azure AD services for which that's true.

What does P1 license include? ›

Azure AD Premium P1 offers the following features: All of the features listed for Azure AD Microsoft 365 apps. SSO for an unlimited number of pre-integrated SaaS applications. Self-service application assignment to enable users to self-discover and request access to applications; this enables cloud app discovery.

How do I upgrade my Azure AD P1 to P2? ›

How to Add Azure AD Premium P1 or P2
  1. Purchase Azure AD P1 or P2 licenses. Select Billing --> Purchase services. ...
  2. Once you have purchased the correct number of addon licenses, you must assign them to a particular account. Go back to the admin console and select Users --> Active users. ...
  3. You're done!

How do I get the list of licensed users in powershell? ›

To list all of the users in your subscription, use the Get-AzureAdUser -All $true command.

How do I check my Microsoft license status? ›

Click Settings, and then under My app settings, choose Office 365. On the My account page, choose Subscriptions. You'll see the services that you're licensed to use, such as the latest desktop version of Microsoft 365, SharePoint in Microsoft 365 or OneDrive for work or school, and Exchange Online.

How do I check my license server status? ›

Open command prompt as administrator and type following command:
  1. SystemInfo.
  2. Type SLMGR and type all commands your query needs.
Jan 6, 2019

How do I check Azure AD join status in PowerShell? ›

Open Windows PowerShell. Enter dsregcmd /status . Verify that both AzureAdJoined and DomainJoined are set to YES. You can use the DeviceId and compare the status on the service using either the Azure portal or PowerShell.

How do I authenticate in PowerShell? ›

To sign in interactively, use the Connect-SecMgmtAccount cmdlet. When you run this cmdlet it will open a browser where you will be able to authenticate. If the system you are using to connect does not a browser, then you will be provided a code.

What are Azure PowerShell commands? ›

Manage VMs
TaskCommand
Start a VMStart-AzVM -ResourceGroupName $myResourceGroup -Name $myVM
Stop a VMStop-AzVM -ResourceGroupName $myResourceGroup -Name $myVM
Restart a running VMRestart-AzVM -ResourceGroupName $myResourceGroup -Name $myVM
Delete a VMRemove-AzVM -ResourceGroupName $myResourceGroup -Name $myVM
Aug 23, 2021

How do I get my Azure AD P1 license? ›

The Office365 Login Analyzer requires an Azure P1 or P2 account. How do I get that?
  1. Purchase Azure AD P1 or P2 licenses. Select Billing --> Purchase services. ...
  2. Once you have purchased the correct number of addon licenses, you must assign them to a particular account. ...
  3. You're done!

What are the different types of Azure licenses? ›

Azure Active Directory comes in four editions—Free, Office 365 apps, Premium P1, and Premium P2. The Free edition is included with a subscription of a commercial online service, e.g. Azure, Dynamics 365, Intune and Power Platform.

Is Azure Active Directory Premium P1 per user? ›

In this example, you can meet the requirement by using dynamic group memberships to automatically assign user licenses and security permissions to users, based on their group membership. An Azure Active Directory Premium P1 license should be assigned to each user affected by dynamic group membership.

Is P1 higher than P2? ›

To answer the question, P1 and P2 refer to the level at which you passed the specific subject at. For example, P1 means pass at Level 1 and therefore P2 means pass at Level 2.

What is the difference between P and P1? ›

There are two kinds of probationary licence in Victoria: P1 (red P plate), which lasts for at least the first 12 months of probationary driving. P2 (green P plate), which normally starts 12 months from when you get your P1 licence and lasts for at least three years.

Does P1 license include teams? ›

Microsoft is adding Azure AD Premium P1 to the Microsoft Teams Room standard and Premium licences at no additional cost.

How do I know if my Azure AD is P1 or P2? ›

To check which edition your Azure AD is capable of, log in to the Azure AD Portal and look in the overview section: Azure AD Free indicates you only have the basic features. Azure AD Premium P1 or P2 will indicate advanced features are available.

How do I upgrade my Azure AD to P2 for free? ›

Upgrade your Azure free account
  1. Sign in to the Azure portal.
  2. Search for Subscriptions.
  3. Select the subscription that was created when you signed up for Azure free account.
  4. In the subscription overview, select Upgrade subscription in the command bar.
Apr 5, 2023

What is included in Azure P2 license? ›

Application proxy for on-premises, header-based, and Integrated Windows Authentication. MDM auto-enrollment, self-service BitLocker recovery, additional local admin tooling to Windows Pro devices via Azure AD Join. Role-based access control (RBAC) Risk-based Identity Protection.

What is the limit of Azure AD free? ›

By default, a maximum of 50,000 Azure AD resources can be created in a single tenant by users of the Azure Active Directory Free edition. If you have at least one verified domain, the default Azure AD service quota for your organization is extended to 300,000 Azure AD resources.

Do I need a license to join a PC to Azure AD? ›

You must have an Intune license to use Intune to manage the devices. Users must have licenses for Windows, Intune, Azure AD, and Windows 365 to use their Cloud PC.

What are the user limits in Azure AD? ›

An Azure AD organization can have a maximum of 5,000 dynamic groups and dynamic administrative units combined. A maximum of 500 role-assignable groups can be created in a single Azure AD organization (tenant). A maximum of 100 users can be owners of a single group.

What license is required for Azure AD join? ›

If you just want just basic Azure AD join for your computers, a regular Azure AD or Office 365 subscription is all you need. You can use an existing subscription or set up a new one. That said, if you want to do anything beyond just joining, you will need additional licenses for Endpoint Management/Intune.

What license is required for Azure AD Connect? ›

You must have an Azure AD Global Administrator account or Hybrid Identity Administrator account for the Azure AD tenant you want to integrate with.

Does Azure AD require authentication? ›

Azure AD Multi-Factor Authentication works by requiring two or more of the following authentication methods: Something you know, typically a password. Something you have, such as a trusted device that is not easily duplicated, like a phone or hardware key. Something you are - biometrics like a fingerprint or face scan.

What is the high salary of Azure? ›

Azure Cloud Engineer salary in India ranges between ₹ 3.4 Lakhs to ₹ 14.0 Lakhs with an average annual salary of ₹ 5.9 Lakhs.

What are the three types of Azure AD? ›

Azure Active Directory comes in four editions—Free, Office 365 apps, Premium P1, and Premium P2.

What are the 3 types of roles in Microsoft Azure? ›

Account Administrator, Service Administrator, and Co-Administrator are the three classic subscription administrator roles in Azure.

Can I bring my own license to Azure? ›

Yes, if you have Software Assurance (SA) you can use License Mobility or Azure Hybrid Benefits to "bring-your-own-license" for all Virtual Machines supported server products.

What is the difference between license and subscription in Azure? ›

For Microsoft's SaaS cloud offerings, a license allows a specific user account to use the services of the cloud offering. You are charged a fixed monthly fee as part of your subscription. Administrators assign licenses to individual user accounts in the subscription.

Do Azure admins need a license? ›

To use Privileged Identity Management (PIM) in Azure Active Directory (Azure AD), part of Microsoft Entra, a tenant must have a valid license. Licenses must also be assigned to the administrators and relevant users.

Is Azure P1 included in E3? ›

EMS E3, Microsoft 365 E3, and Microsoft 365 Business Premium includes Azure AD Premium P1. EMS E5 or Microsoft 365 E5 includes Azure AD Premium P2. You can use the same Conditional Access features noted in the following sections to provide multi-factor authentication to users.

What is the difference between Active Directory and Azure Active Directory? ›

AD is great at managing traditional on-premise infrastructure and applications. Azure AD is great at managing user access to cloud applications. You can use both together, or if you want to have a purely cloud based environment you can just use Azure AD.

What is the difference between P2 and E5 license? ›

Enterprise Mobility + Security E5

It also offers multifactor authentication (MFA) conditional access and advanced security reporting. P2 includes all of that plus offers Identity Protection and Privileged Identity Management (PIM) and advanced capability concerning identity protection. E5 of course is more expensive.

How do I activate my Azure AD license? ›

To activate your license plan

Open the confirmation email you received from Microsoft after you signed up, and then select either Sign In or Sign Up. Sign in. Choose this link if you have an existing tenant, and then sign in using your existing administrator account.

Can I change the UPN in Azure AD? ›

You can also change a user's UPN in the Azure AD admin center by changing their username. And you can change a UPN by using Microsoft PowerShell. A user's UPN (used for signing in) and email address can be different. If you just need to add a new email address for a user, you can add an alias without changing the UPN.

What license includes Azure AD P1? ›

Azure AD Premium P1, included with Microsoft 365 E3, offers a free 30-day trial.

How do I authenticate API with Azure AD? ›

  1. Step 1: Create a protected web API. Create a new web API project. ...
  2. Step 2: Install the dependencies. ...
  3. Step 3: Initiate the authentication library. ...
  4. Step 4: Add the endpoints. ...
  5. Step 5: Configure the web server. ...
  6. Step 6: Configure the web API. ...
  7. Step 7: Run and test the web API. ...
  8. Step 8: Call the web API from your app.
Mar 7, 2023

How do I connect to Azaccount in PowerShell? ›

To add an authenticated account for use with Service Management, use the Add-AzureAccount cmdlet from the Azure PowerShell module. If no context is found for the current user, the user's context list is populated with a context for each of their first 25 subscriptions.

How do I pass credentials in PowerShell script? ›

The first way to create a credential object is to use the PowerShell cmdlet Get-Credential . When you run without parameters, it prompts you for a username and password. Or you can call the cmdlet with some optional parameters.

How to check Windows activation status using PowerShell? ›

3. How to Check Windows 11 Activation State With Command Prompt or PowerShell
  1. Use one of the many ways to open Command Prompt or PowerShell on Windows.
  2. In the console, type slmgr /xpr and press Enter.
  3. In the dialog box, check Windows 11's activation status.
Apr 5, 2023

How do I pass credentials in PowerShell without prompt? ›

Now, let's find a solution to use the PowerShell credentials without being prompted for a password.
  1. Step 1: Create a Secure String. First of all, create a secure string in order to safely store the password in it: ...
  2. Step 2: Create a PSCredential Object. ...
  3. Step 3: Use the Credentials Without Being Prompted for a Password.

Which three authentication methods can Azure AD? ›

Available verification methods
  • Microsoft Authenticator.
  • Authenticator Lite (in Outlook)
  • Windows Hello for Business.
  • FIDO2 security key.
  • OATH hardware token (preview)
  • OATH software token.
  • SMS.
  • Voice call.
Mar 14, 2023

How do I enable basic authentication in PowerShell? ›

-AllowBasicAuthActiveSync
  1. To allow Basic authentication for the protocol, use this switch without a value.
  2. To block Basic authentication for the protocol, use this exact syntax: -AllowBasicAuthActiveSync:$false .

What authentication methods does remote PowerShell use? ›

By default, PowerShell Remoting uses Kerberos (if available) or NTLM for authentication. Both of these protocols authenticate to the remote machine without sending credentials to it.

How do I set up Azure AD subscription in PowerShell? ›

To change the azure subscription using PowerShell, we can use the Select-AZSubscription command. When you use this command, you can use either the subscription ID, Subscription Name, or the Tenant ID.

How do I Connect to Azure using Windows PowerShell? ›

Sign in to Azure

Sign in interactively with the Connect-AzAccount cmdlet. Skip this step if you use Cloud Shell. Your Azure Cloud Shell session is already authenticated for the environment, subscription, and tenant that launched the Cloud Shell session. Beginning with Az PowerShell module version 5.0.

How to install Azure AD Connect in PowerShell? ›

Install the Azure AD Connect provisioning agent by using PowerShell cmdlets
  1. Sign in to the server you use with enterprise admin permissions.
  2. Sign in to the Azure portal, and then go to Azure Active Directory.
  3. On the menu on the left, select Azure AD Connect.
  4. Select Manage cloud sync.
  5. At the top, click Download agent.
May 4, 2023

How to check connectivity using PowerShell? ›

  1. Run Windows Powershell.
  2. type the following command: tnc <Server> - port <PortNumber>
  3. You need to check the value of TcpTestSucceeded. It gives True if the port is open and false if the port is closed.

Top Articles
Latest Posts
Article information

Author: Otha Schamberger

Last Updated: 07/13/2023

Views: 5739

Rating: 4.4 / 5 (55 voted)

Reviews: 94% of readers found this page helpful

Author information

Name: Otha Schamberger

Birthday: 1999-08-15

Address: Suite 490 606 Hammes Ferry, Carterhaven, IL 62290

Phone: +8557035444877

Job: Forward IT Agent

Hobby: Fishing, Flying, Jewelry making, Digital arts, Sand art, Parkour, tabletop games

Introduction: My name is Otha Schamberger, I am a vast, good, healthy, cheerful, energetic, gorgeous, magnificent person who loves writing and wants to share my knowledge and understanding with you.