diff --git a/.gitignore b/.gitignore
index 81c7661b6f91..8b0fa84f2d41 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,4 +5,5 @@ chocoapps.cache
*.log
.vscode/
Cache_BestPracticeAnalyser/
+Cache_DomainAnalyser/
ExcludedTenants
diff --git a/AddAPDevice/run.ps1 b/AddAPDevice/run.ps1
index e9c400b6b699..a09a2e208628 100644
--- a/AddAPDevice/run.ps1
+++ b/AddAPDevice/run.ps1
@@ -11,9 +11,16 @@ Log-Request -user $request.headers.'x-ms-client-principal' -API $APINAME -messa
Write-Host "PowerShell HTTP trigger function processed a request."
$TenantFilter = (Get-Content Tenants.cache.json | ConvertFrom-Json | Where-Object { $_.defaultdomainname -eq $Request.body.TenantFilter }).customerid
$GroupName = if ($Request.body.Groupname) { $Request.body.Groupname } else { New-Guid }
-$rawDevices = ($Request.body.Devices | ConvertFrom-Csv -Header "SerialNumber", "oemManufacturerName", "modelName", "productKey", "hardwareHash" -Delimiter ",")
+$rawDevices = if ($Request.body.devices -like "Device serial number,Windows product ID,Hardware hash,Manufacturer name,Device Model*") {
+ Write-Host "csvupload"
+ ($Request.body.Devices | ConvertFrom-Csv -Delimiter "," -Header "SerialNumber", "productKey", "hardwareHash", "oemManufacturerName", "modelName") | Select-Object -Skip 1
+}
+else {
+ Write-Host "Standard table request"
+ ($Request.body.Devices | ConvertFrom-Csv -Header "SerialNumber", "oemManufacturerName", "modelName", "productKey", "hardwareHash" -Delimiter ",")
+}
$Devices = ConvertTo-Json @($rawDevices)
-
+Write-Host $Devices
$Result = try {
$CurrentStatus = (New-GraphgetRequest -uri "https://api.partnercenter.microsoft.com/v1/customers/$tenantfilter/DeviceBatches" -scope 'https://api.partnercenter.microsoft.com/user_impersonation')
if ($groupname -in $CurrentStatus.items.id) { throw "This device batch name already exists. Please try with another name." }
diff --git a/AddPolicy/run.ps1 b/AddPolicy/run.ps1
index e65c11e592fa..ca61ccb80c75 100644
--- a/AddPolicy/run.ps1
+++ b/AddPolicy/run.ps1
@@ -12,12 +12,23 @@ $displayname = $request.body.Displayname
$description = $request.body.Description
$AssignTo = if ($request.body.Assignto -ne "on") { $request.body.Assignto }
$RawJSON = $Request.body.RawJSON
+
$results = foreach ($Tenant in $tenants) {
try {
- $CreateBody = '{"description":"' + $description + '","displayName":"' + $displayname + '","roleScopeTagIds":["0"]}'
- $CreateRequest = New-GraphPOSTRequest -uri "https://graph.microsoft.com/beta/deviceManagement/groupPolicyConfigurations" -tenantid $tenant -type POST -body $CreateBody
- $UpdateRequest = New-GraphPOSTRequest -uri "https://graph.microsoft.com/beta/deviceManagement/groupPolicyConfigurations('$($CreateRequest.id)')/updateDefinitionValues" -tenantid $tenant -type POST -body $RawJSON
-
+ switch ($Request.body.TemplateType) {
+ "Admin" {
+ $CreateBody = '{"description":"' + $description + '","displayName":"' + $displayname + '","roleScopeTagIds":["0"]}'
+ $CreateRequest = New-GraphPOSTRequest -uri "https://graph.microsoft.com/beta/deviceManagement/groupPolicyConfigurations" -tenantid $tenant -type POST -body $CreateBody
+ $UpdateRequest = New-GraphPOSTRequest -uri "https://graph.microsoft.com/beta/deviceManagement/groupPolicyConfigurations('$($CreateRequest.id)')/updateDefinitionValues" -tenantid $tenant -type POST -body $RawJSON
+ }
+ "Device" {
+ $CreateRequest = New-GraphPOSTRequest -uri "https://graph.microsoft.com/beta/deviceManagement/deviceConfigurations" -tenantid $tenant -type POST -body $RawJSON
+ }
+ "Catalog" {
+ $CreateRequest = New-GraphPOSTRequest -uri "https://graph.microsoft.com/beta/deviceManagement/configurationPolicies" -tenantid $tenant -type POST -body $RawJSON
+ }
+
+ }
Log-Request -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $($Tenant) -message "Added policy $($Displayname)" -Sev "Error"
if ($AssignTo) {
$AssignBody = if ($AssignTo -ne "AllDevicesAndUsers") { '{"assignments":[{"id":"","target":{"@odata.type":"#microsoft.graph.' + $($AssignTo) + 'AssignmentTarget"}}]}' } else { '{"assignments":[{"id":"","target":{"@odata.type":"#microsoft.graph.allDevicesAssignmentTarget"}},{"id":"","target":{"@odata.type":"#microsoft.graph.allLicensedUsersAssignmentTarget"}}]}' }
diff --git a/AddUser/run.ps1 b/AddUser/run.ps1
index 5f36a07f148f..9380e484d282 100644
--- a/AddUser/run.ps1
+++ b/AddUser/run.ps1
@@ -11,7 +11,7 @@ $userobj = $Request.body
Write-Host "PowerShell HTTP trigger function processed a request."
try {
$licenses = ($userobj | Select-Object "License_*").psobject.properties.value
- $aliasses = ($userobj.AddedAliasses).Split([Environment]::NewLine)
+ $Aliases = ($userobj.AddedAliases).Split([Environment]::NewLine)
$password = if ($userobj.password) { $userobj.password } else { -join ('abcdefghkmnrstuvwxyzABCDEFGHKLMNPRSTUVWXYZ23456789$%&*#'.ToCharArray() | Get-Random -Count 12) }
$UserprincipalName = "$($UserObj.username)@$($UserObj.domain)"
$BodyToship = [pscustomobject] @{
@@ -27,7 +27,7 @@ try {
"password" = $password
}
} | ConvertTo-Json
- $GraphRequest = New-GraphPostRequest -uri "https://graph.microsoft.com/beta/users" -tenantid $Userobj.tenantid-type POST -body $BodyToship -verbose
+ $GraphRequest = New-GraphPostRequest -uri "https://graph.microsoft.com/beta/users" -tenantid $Userobj.tenantid -type POST -body $BodyToship -verbose
Log-Request -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $($userobj.tenantid) -message "Created user $($userobj.displayname) with id $($GraphRequest.id) " -Sev "Info"
$results.add("Created user.")
$results.add("Username: $($UserprincipalName)")
@@ -60,19 +60,19 @@ catch {
}
try {
- if ($aliasses) {
- foreach ($Alias in $aliasses) {
+ if ($Aliases) {
+ foreach ($Alias in $Aliases) {
Write-Host $Alias
New-GraphPostRequest -uri "https://graph.microsoft.com/beta/users/$($GraphRequest.id)" -tenantid $Userobj.tenantid -type "patch" -body "{`"mail`": `"$Alias`"}" -verbose
}
New-GraphPostRequest -uri "https://graph.microsoft.com/beta/users/$($GraphRequest.id)" -tenantid $Userobj.tenantid -type "patch" -body "{`"mail`": `"$UserprincipalName`"}" -verbose
Log-Request -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $($userobj.tenantid) -message "Added alias $($Alias) to $($userobj.displayname)" -Sev "Info"
- $body = $results.add("Added aliasses: $($aliasses -join ',')")
+ $body = $results.add("Added Aliases: $($Aliases -join ',')")
}
}
catch {
Log-Request -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $($userobj.tenantid) -message "Alias API failed. $($_.Exception.Message)" -Sev "Error"
- $body = $results.add("We've failed to create the aliasses: $($_.Exception.Message)")
+ $body = $results.add("We've failed to create the Aliases: $($_.Exception.Message)")
}
if ($Request.body.CopyFrom -ne "") {
$MemberIDs = "https://graph.microsoft.com/v1.0/directoryObjects/" + (New-GraphGetRequest -uri "https://graph.microsoft.com/beta/users/$($GraphRequest.id)" -tenantid $Userobj.tenantid).id
diff --git a/Applications_Upload/run.ps1 b/Applications_Upload/run.ps1
index f3e156ac44a1..3ebe684a113b 100644
--- a/Applications_Upload/run.ps1
+++ b/Applications_Upload/run.ps1
@@ -29,7 +29,7 @@ $EncBody = @{
Try {
$ApplicationList = (New-graphGetRequest -Uri $baseuri -tenantid $Tenant) | Where-Object { $_.DisplayName -eq $ChocoApp.ApplicationName }
if ($ApplicationList.displayname.count -ge 1) {
- Log-Request -api "ChocoAppUpload" -API "ChocoApp" -tenant $($Tenant) -message "$($ChocoApp.ApplicationName) exists. Skipping this application" -Sev "Warning"
+ Log-Request -api "ChocoAppUpload" -tenant $($Tenant) -message "$($ChocoApp.ApplicationName) exists. Skipping this application" -Sev "Warning"
continue
}
$NewApp = New-GraphPostRequest -Uri $baseuri -Body ($intuneBody | ConvertTo-Json) -Type POST -tenantid $tenant
diff --git a/BestPracticeAnalyser_All/run.ps1 b/BestPracticeAnalyser_All/run.ps1
index 91c14a33b104..7c2996c2d853 100644
--- a/BestPracticeAnalyser_All/run.ps1
+++ b/BestPracticeAnalyser_All/run.ps1
@@ -44,11 +44,13 @@ $Result = [PSCustomObject]@{
UnusedLicensesCount = ""
UnusedLicensesResult = ""
UnusedLicenseList = ""
+ SecureScoreCurrent = ""
+ SecureScoreMax = ""
+ SecureScorePercentage = ""
}
# Starting the Best Practice Analyser
-Log-request -API "BestPracticeAnalyser" -tenant $tenant -message "Started Best Practice Analyser Durable Function on $($tenant)" -sev "Info"
-
+
# Get the Secure Default State
try {
$SecureDefaultsState = (New-GraphGetRequest -Uri "https://graph.microsoft.com/beta/policies/identitySecurityDefaultsEnforcementPolicy" -tenantid $tenant)
@@ -257,6 +259,18 @@ catch {
Log-request -API "BestPracticeAnalyser" -tenant $tenant -message "Unused Licenses on $($tenant). Error: $($_.exception.message)" -sev "Error"
}
+# Get Secure Score
+try {
+ $SecureScore = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/security/secureScores?`$top=1" -tenantid $tenant -noPagination $true
+ $Result.SecureScoreCurrent = $SecureScore.currentScore
+ $Result.SecureScoreMax = $SecureScore.maxScore
+ $Result.SecureScorePercentage = [int](($SecureScore.currentScore / $SecureScore.maxScore) * 100)
+ Log-request -API "BestPracticeAnalyser" -tenant $tenant -message "Secure Score on $($tenant) is $($Result.SecureScoreCurrent) / $($Result.SecureScoreMax)" -sev "Debug"
+}
+catch {
+ Log-request -API "BestPracticeAnalyser" -tenant $tenant -message "Secure Score Retrieval on $($tenant). Error: $($_.exception.message)" -sev "Error"
+}
+
# Send Output of all the Results to the Stream
$Result
\ No newline at end of file
diff --git a/BestPracticeAnalyser_List/run.ps1 b/BestPracticeAnalyser_List/run.ps1
index 151450611eed..e27cf5bc09b2 100644
--- a/BestPracticeAnalyser_List/run.ps1
+++ b/BestPracticeAnalyser_List/run.ps1
@@ -11,7 +11,12 @@ Log-Request -user $request.headers.'x-ms-client-principal' -API $APINAME -messa
Write-Host "PowerShell HTTP trigger function processed a request."
# Get all the things
-$Results = Get-ChildItem ".\Cache_BestPracticeAnalyser\*.json" | ForEach-Object{Get-Content $_.FullName | Out-String | ConvertFrom-Json}
+$UnfilteredResults = Get-ChildItem ".\Cache_BestPracticeAnalyser\*.json" | ForEach-Object{Get-Content $_.FullName | Out-String | ConvertFrom-Json}
+
+# Need to apply exclusion logic
+$Skiplist = Get-Content "ExcludedTenants" | ConvertFrom-Csv -Delimiter "|" -Header "Name", "User", "Date"
+
+$Results = $UnfilteredResults | Where-Object {$_.Tenant -notin $Skiplist.Name}
# Associate values to output bindings by calling 'Push-OutputBinding'.
Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
diff --git a/DomainAnalyser_All/function.json b/DomainAnalyser_All/function.json
new file mode 100644
index 000000000000..2d4ea9094b24
--- /dev/null
+++ b/DomainAnalyser_All/function.json
@@ -0,0 +1,9 @@
+{
+ "bindings": [
+ {
+ "name": "tenant",
+ "direction": "in",
+ "type": "activityTrigger"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/DomainAnalyser_All/run.ps1 b/DomainAnalyser_All/run.ps1
new file mode 100644
index 000000000000..5b3e11615aa8
--- /dev/null
+++ b/DomainAnalyser_All/run.ps1
@@ -0,0 +1,290 @@
+param($tenant)
+
+function Get-GoogleDNSQuery {
+ [CmdletBinding()]
+ param (
+ [Parameter()]
+ [string]
+ $Domain,
+
+ [Parameter()]
+ [string]
+ $RecordType,
+
+ [Parameter()]
+ [bool]
+ $FullResultRecord = $False
+ )
+
+ try {
+ $Results = Invoke-RestMethod -Uri "https://dns.google/resolve?name=$($Domain)&type=$($RecordType)" -Method Get
+ }
+ catch {
+ Log-request -API "DomainAnalyser" -tenant $tenant.tenant -message "Get Google DNS Query Failed with $($_.Exception.Message)" -sev Debug
+ }
+
+ # Domain does not exist
+ if ($Results.Status -ne 0) {
+ return $null
+ }
+
+ if (($Results.Answer | Measure-Object | Select-Object -ExpandProperty Count) -eq 0) {
+ return $null
+ }
+ else {
+ if ($RecordType -eq 'MX') {
+ $FinalClean = $Results.Answer | ForEach-Object { $_.Data.Split(' ')[1] }
+ return $FinalClean
+ }
+ if (!$FullResultRecord) {
+ return $Results.Answer
+ }
+ else {
+ return $Results
+ }
+ }
+}
+
+$Domain = $Tenant.Domain
+
+Log-request -API "DomainAnalyser" -tenant $tenant.tenant -message "Starting Processing of $($Tenant.Domain)" -sev Debug
+$Result = [PSCustomObject]@{
+ Tenant = $tenant.tenant
+ GUID = $($Tenant.Domain.Replace('.', ''))
+ LastRefresh = $(Get-Date (Get-Date).ToUniversalTime() -UFormat '+%Y-%m-%dT%H:%M:%S.000Z')
+ Domain = $Domain
+ AuthenticationType = $Tenant.authenticationType
+ IsAdminManaged = $Tenant.isAdminManaged
+ IsDefault = $Tenant.isDefault
+ IsInitial = $Tenant.isInitial
+ IsRoot = $Tenant.isRoot
+ IsVerified = $Tenant.isVerified
+ SupportedServices = $Tenant.supportedServices
+ ExpectedSPFRecord = ""
+ ActualSPFRecord = ""
+ SPFPassTest = ""
+ SPFPassAll = ""
+ ExpectedMXRecord = ""
+ ActualMXRecord = ""
+ MXPassTest = ""
+ DMARCPresent = ""
+ DMARCFullPolicy = ""
+ DMARCActionPolicy = ""
+ DMARCReportingActive = ""
+ DMARCPercentagePass = ""
+ DNSSECPresent = ""
+ MailProvider = ""
+ DKIMEnabled = ""
+ Score = ""
+ MaximumScore = 160
+ ScorePercentage = ""
+ ScoreExplanation = ""
+}
+
+$Scores = [PSCustomObject]@{
+ SPFPresent = 10
+ SPFMSRecommended = 10
+ SPFCorrectAll = 10
+ MXMSRecommended = 10
+ DMARCPresent = 10
+ DMARCSetQuarantine = 20
+ DMARCSetReject = 30
+ DMARCReportingActive = 20
+ DMARCPercentageGood = 20
+ DNSSECPresent = 20
+ DKIMActiveAndWorking = 20
+}
+
+$ScoreDomain = 0
+# Setup Score Explanation
+[System.Collections.ArrayList]$ScoreExplanation = @()
+
+# Get 365 Service Configuration Records
+$ServiceConfigRecords = New-GraphGetRequest -uri "https://graph.microsoft.com/v1.0/domains/$($domain)/serviceConfigurationRecords" -tenantid $tenant.tenant
+
+$Result.ExpectedMXRecord = $ServiceConfigRecords | Where-Object { $_.RecordType -eq "MX" } | Select-Object -ExpandProperty MailExchange
+$Result.ExpectedSPFRecord = $ServiceConfigRecords | Where-Object { ($_.RecordType -eq "Txt") -and ($_.Text -like "*spf*") } | Select-Object -ExpandProperty Text
+
+# Get SPF Record
+try {
+ $Results = Get-GoogleDNSQuery -Domain $domain -RecordType "TXT"
+ $SPFResults = $Results | Where-Object { $_.Data -like '*spf1*' } | Select-Object -ExpandProperty Data
+ if ($SPFResults.Count -gt 0) {
+ $Result.ActualSPFRecord = $SPFResults | Where-Object { $_ -like '*spf1*' }
+ $ScoreDomain += $Scores.SPFPresent
+ }
+ else {
+ $Result.ActualSPFRecord = "No SPF Record"
+ $ScoreExplanation.Add("No SPF Record Found") | Out-Null
+ }
+}
+catch {
+ Log-request -API "DomainAnalyser" -tenant $tenant.tenant -message "Exception and Error while getting SPF Record with $($_.Exception.Message)" -sev Error
+}
+
+# Check SPF Record
+$SPFMatch = $Result.ActualSPFRecord | Where-Object { $_ -like "$($Result.ExpectedSPFRecord)" }
+If (($SPFMatch | Measure-Object | Select-Object -ExpandProperty Count) -gt 0) {
+ $ScoreDomain += $Scores.SPFMSRecommended
+ $Result.SPFPassTest = $true
+ if ($SPFMatch -like '*-all*') {
+ $ScoreDomain += $Scores.SPFCorrectAll
+ $Result.SPFPassAll = $true
+ }
+ else {
+ $Result.SPFPassAll = $false
+ $ScoreExplanation.Add("SPF Record is not set to hard Fail") | Out-Null
+ }
+}
+else {
+ if ($Result.ActualSPFRecord -like '*include:spf.protection.outlook.com*') {
+ $ScoreDomain += $Scores.SPFMSRecommended
+ $Result.SPFPassTest = $true
+ if ($Result.ActualSPFRecord -like '*-all*') {
+ $ScoreDomain += $Scores.SPFCorrectAll
+ $Result.SPFPassAll = $true
+ }
+ else {
+ $Result.SPFPassAll = $false
+ }
+ }
+ else {
+ $ScoreExplanation.Add("SPF Record is Misconfigured") | Out-Null
+ $Result.SPFPassTest = $false
+ $Result.SPFPassAll = $false
+ }
+
+}
+
+# Get MX Record
+try {
+ $MXResults = Get-GoogleDNSQuery -domain $domain -RecordType "MX"
+ $Result.ActualMXRecord = ($MXResults) -join ","
+}
+catch {
+ Log-request -API "DomainAnalyser" -tenant $tenant.tenant -message "Exception and Error while getting MX Record with $($_.Exception.Message)" -sev Error
+}
+
+# Check MX Record
+$MXMatch = $Result.ActualMXRecord | Where-Object { $_ -like "*$($Result.ExpectedMXRecord)*" }
+If (($MXMatch | Measure-Object | Select-Object -ExpandProperty Count) -gt 0) {
+ $Result.MXPassTest = $true
+ $ScoreDomain += $Scores.MXMSRecommended
+}
+else {
+ $Result.MXPassTest = $false
+ $ScoreExplanation.Add("MX Record does not match Microsoft's suggestion") | Out-Null
+}
+
+# Get DMARC Record
+try {
+ #$DMARCResults = Resolve-DnsName -Name "_dmarc.$($domain)" -Type TXT -ErrorAction SilentlyContinue | Where-Object { $_.Type -eq "TXT" }
+ $DMARCResults = Get-GoogleDNSQuery -Domain "_dmarc.$($domain)" -RecordType "TXT"
+ If ([string]::IsNullOrEmpty($DMARCResults.data)) {
+ $Result.DMARCPresent = $false
+ $ScoreExplanation.Add("No DMARC Records Found") | Out-Null
+ }
+ else {
+ $Result.DMARCPresent = $true
+ $ScoreDomain += $Scores.DMARCPresent
+
+ $Result.DMARCFullPolicy = $DMARCResults.data
+ if (($Result.DMARCFullPolicy.replace(' ', '') -like '*;p=reject*')) {
+ $Result.DMARCActionPolicy = "Reject"
+ $ScoreDomain += $Scores.DMARCSetReject
+ }
+ if ($Result.DMARCFullPolicy -like '*p=n*') {
+ $Result.DMARCActionPolicy = "None"
+ $ScoreExplanation.Add("DMARC is not being enforced") | Out-Null
+ }
+ if ($Result.DMARCFullPolicy -like '*p=qua*') {
+ $Result.DMARCActionPolicy = "Quarantine"
+ $ScoreDomain += $Scores.DMARCSetQuarantine
+ $ScoreExplanation.Add("DMARC Partially Enforced with quarantine") | Out-Null
+ }
+ if (($Result.DMARCFullPolicy -like '*rua*') -or ($Result.DMARCHFullPolicy -like '*ruf*')) {
+ $Result.DMARCReportingActive = $true
+ $ScoreDomain += $Scores.DMARCReportingActive
+ }
+ else {
+ $Result.DMARCReportingActive = $False
+ $ScoreExplanation.Add("DMARC Reporting not Configured") | Out-Null
+ }
+ if ($Result.DMARCFullPolicy -like '*pct=*') {
+ if ($Result.DMARCFullPolicy -like '*pct=100*') {
+ $Result.DMARCPercentagePass = $true
+ $ScoreDomain += $Scores.DMARCPercentageGood
+ }
+ else {
+ $Result.DMARCPercentagePass = $false
+ $ScoreExplanation.Add("DMARC Not Checking All Messages") | Out-Null
+ }
+ }
+ else {
+ $Result.DMARCPercentagePass = $true
+ $ScoreDomain += $Scores.DMARCPercentageGood
+ }
+ }
+}
+catch {
+ Log-request -API "DomainAnalyser" -tenant $tenant.tenant -message "Exception and Error while getting DMARC Record with $($_.Exception.Message)" -sev Error
+}
+
+# DNS Sec Check
+try {
+ $DNSSECResult = Get-GoogleDNSQuery -Domain "$($domain)" -RecordType "SOA" -FullResultRecord $true
+ if ($DNSSECResult.AD) {
+ $Result.DNSSECPresent = $true
+ $ScoreDomain += $Scores.DNSSECPresent
+ }
+ else {
+ $Result.DNSSECPresent = $false
+ $ScoreExplanation.Add("DNSSEC Not Configured or Enabled") | Out-Null
+ }
+}
+catch {
+ Log-request -API "DomainAnalyser" -tenant $tenant.tenant -message "Exception and Error while getting DNSSEC with $($_.Exception.Message)" -sev Error
+}
+
+# DKIM Check
+try {
+ # We can only really do Google and 365 DKIM so lets work out whether we are on Google or 365
+ if ($Result.ActualMXRecord -like '*protection.outlook.com*') {
+ $Result.MailProvider = "Microsoft 365"
+ $DKIMSelector = "selector1._domainkey.$($domain)"
+ }
+
+ if ($Result.ActualMXRecord -like '*l.google.com*') {
+ $Result.MailProvider = "Google"
+ $DKIMSelector = "google._domainkey.$($domain)"
+ }
+
+ if ([string]::IsNullOrEmpty($Result.MailProvider)) {
+ $Result.MailProvider = "Unknown"
+ }
+
+ if ($Result.MailProvider -ne 'Unknown') {
+ $DKIMResult = Get-GoogleDNSQuery -Domain $DKIMSelector -RecordType "TXT"
+ if ($DKIMResult.Data -like '*v=DKIM1*') {
+ $Result.DKIMEnabled = $true
+ $ScoreDomain += $Scores.DKIMActiveAndWorking
+ }
+ else {
+ $Result.DKIMEnabled = $false
+ $ScoreExplanation.Add("DKIM Not Configured") | Out-Null
+ }
+ }
+
+}
+catch {
+ Log-request -API "DomainAnalyser" -tenant $tenant.tenant -message "DKIM Lookup Failed with $($_.Exception.Message)" -sev Error
+}
+# Final Score
+$Result.Score = $ScoreDomain
+$Result.ScorePercentage = [int](($Result.Score / $Result.MaximumScore)*100)
+$Result.ScoreExplanation = ($ScoreExplanation) -join ", "
+
+# Final Write to Output
+Log-request -API "DomainAnalyser" -tenant $tenant.tenant -message "DNS Analyser Finished For $($Result.Domain)" -sev Info
+Write-Output $Result
+
diff --git a/DomainAnalyser_GetQueue/function.json b/DomainAnalyser_GetQueue/function.json
new file mode 100644
index 000000000000..b31f1ad21352
--- /dev/null
+++ b/DomainAnalyser_GetQueue/function.json
@@ -0,0 +1,9 @@
+{
+ "bindings": [
+ {
+ "name": "name",
+ "type": "activityTrigger",
+ "direction": "in"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/DomainAnalyser_GetQueue/run.ps1 b/DomainAnalyser_GetQueue/run.ps1
new file mode 100644
index 000000000000..175276caef7d
--- /dev/null
+++ b/DomainAnalyser_GetQueue/run.ps1
@@ -0,0 +1,28 @@
+param($name)
+
+$Tenants = Get-Tenants
+
+$object = foreach ($Tenant in $Tenants) {
+ # Get Domains to Lookup
+ try {
+ $Domains = New-GraphGetRequest -uri 'https://graph.microsoft.com/v1.0/domains' -tenantid $Tenant.defaultDomainName | Where-Object { ($_.id -notlike '*.onmicrosoft.com') -and ($_.supportedServices -contains 'Email') }
+ foreach ($d in $domains) {
+ [PSCustomObject]@{
+ Tenant = $Tenant.defaultDomainName
+ Domain = $d.id
+ AuthenticationType = $d.authenticationType
+ IsAdminManaged = $d.isAdminManaged
+ IsDefault = $d.isDefault
+ IsInitial = $d.isInitial
+ IsRoot = $d.isRoot
+ IsVerified = $d.isVerified
+ SupportedServices = $d.supportedServices
+ }
+ }
+ }
+ catch {
+ Log-request -API 'DomainAnalyser' -tenant $tenant.defaultDomainName -message "DNS Analyser GraphGetRequest Exception: $($_.Exception.Message)" -sev Error
+ }
+}
+
+$object
\ No newline at end of file
diff --git a/AccessChecks/function.json b/DomainAnalyser_List/function.json
similarity index 100%
rename from AccessChecks/function.json
rename to DomainAnalyser_List/function.json
diff --git a/NotifyTeams/run.ps1 b/DomainAnalyser_List/run.ps1
similarity index 53%
rename from NotifyTeams/run.ps1
rename to DomainAnalyser_List/run.ps1
index e1a20af4be1a..4645ba4e5021 100644
--- a/NotifyTeams/run.ps1
+++ b/DomainAnalyser_List/run.ps1
@@ -10,18 +10,15 @@ Log-Request -user $request.headers.'x-ms-client-principal' -API $APINAME -messa
# Write to the Azure Functions log stream.
Write-Host "PowerShell HTTP trigger function processed a request."
-# Interact with query parameters or the body of the request.
-$TenantFilter = $Request.Query.TenantFilter
-if ($TenantFilter) {
- $GraphRequest = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/users?`$top=999" -tenantid $TenantFilter
-}
-else {
- $GraphRequest = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/users?`$top=999"
-}
+# Get all the things
+$UnfilteredResults = Get-ChildItem ".\Cache_DomainAnalyser\*.json" | ForEach-Object { Get-Content $_.FullName | Out-String | ConvertFrom-Json }
+# Need to apply exclusion logic
+$Skiplist = Get-Content "ExcludedTenants" | ConvertFrom-Csv -Delimiter "|" -Header "Name", "User", "Date"
+$Results = $UnfilteredResults | ForEach-Object { $_.GUID = $_.GUID -replace '[^a-zA-Z-]', ''; $_ } | Where-Object { $_.Tenant -notin $Skiplist.Name }
# Associate values to output bindings by calling 'Push-OutputBinding'.
Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
StatusCode = [HttpStatusCode]::OK
- Body = $GraphRequest
+ Body = @($Results)
})
\ No newline at end of file
diff --git a/DomainAnalyser_Orchestration/function.json b/DomainAnalyser_Orchestration/function.json
new file mode 100644
index 000000000000..7326b39c184d
--- /dev/null
+++ b/DomainAnalyser_Orchestration/function.json
@@ -0,0 +1,9 @@
+{
+ "bindings": [
+ {
+ "name": "Context",
+ "type": "orchestrationTrigger",
+ "direction": "in"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/DomainAnalyser_Orchestration/run.ps1 b/DomainAnalyser_Orchestration/run.ps1
new file mode 100644
index 000000000000..04aa5b8758b8
--- /dev/null
+++ b/DomainAnalyser_Orchestration/run.ps1
@@ -0,0 +1,21 @@
+param($Context)
+
+New-Item "Cache_DomainAnalyser" -ItemType Directory -ErrorAction SilentlyContinue
+New-Item "Cache_DomainAnalyser\CurrentlyRunning.txt" -ItemType File -Force
+$Batch = (Invoke-ActivityFunction -FunctionName 'DomainAnalyser_GetQueue' -Input 'LetsGo')
+$ParallelTasks = foreach ($Item in $Batch) {
+ Invoke-DurableActivity -FunctionName "DomainAnalyser_All" -Input $item -NoWait
+}
+
+$Outputs = Wait-ActivityFunction -Task $ParallelTasks
+Log-request -API "DomainAnalyser" -tenant $tenant -message "Outputs found count = $($Outputs.count)" -sev Info
+
+foreach ($item in $Outputs) {
+ Write-Host $Item | Out-String
+ $Object = $Item | ConvertTo-Json
+
+ Set-Content "Cache_DomainAnalyser\$($item.domain).DomainAnalysis.json" -Value $Object -Force
+}
+
+Log-request -API "DomainAnalyser" -tenant $tenant -message "Domain Analyser has Finished" -sev Info
+Remove-Item "Cache_DomainAnalyser\CurrentlyRunning.txt" -Force
\ No newline at end of file
diff --git a/DomainAnalyser_OrchestrationStarter/function.json b/DomainAnalyser_OrchestrationStarter/function.json
new file mode 100644
index 000000000000..14c44f4f0217
--- /dev/null
+++ b/DomainAnalyser_OrchestrationStarter/function.json
@@ -0,0 +1,24 @@
+{
+ "bindings": [
+ {
+ "authLevel": "anonymous",
+ "name": "Request",
+ "type": "httpTrigger",
+ "direction": "in",
+ "methods": [
+ "post",
+ "get"
+ ]
+ },
+ {
+ "type": "http",
+ "direction": "out",
+ "name": "Response"
+ },
+ {
+ "name": "starter",
+ "type": "durableClient",
+ "direction": "in"
+ }
+ ]
+}
diff --git a/DomainAnalyser_OrchestrationStarter/run.ps1 b/DomainAnalyser_OrchestrationStarter/run.ps1
new file mode 100644
index 000000000000..cbab5a8092db
--- /dev/null
+++ b/DomainAnalyser_OrchestrationStarter/run.ps1
@@ -0,0 +1,22 @@
+using namespace System.Net
+
+param($Request, $TriggerMetadata)
+$CurrentlyRunning = Get-Item "Cache_DomainAnalyser\CurrentlyRunning.txt" -ErrorAction SilentlyContinue | Where-Object -Property LastWriteTime -GT (Get-Date).AddHours(-24)
+if ($CurrentlyRunning) {
+ $Results = [pscustomobject]@{"Results" = "Already running. Please wait for the current instance to finish" }
+ Log-request -API "DomainAnalyser" -message "Attempted to start domain analysis but an instance was already running." -sev Info
+}
+else {
+ $InstanceId = Start-NewOrchestration -FunctionName 'DomainAnalyser_Orchestration'
+ Write-Host "Started orchestration with ID = '$InstanceId'"
+ $Orchestrator = New-OrchestrationCheckStatusResponse -Request $Request -InstanceId $InstanceId
+ Log-request -API "DomainAnalyser" -message "Started retrieving domain information" -sev Info
+ $Results = [pscustomobject]@{"Results" = "Started running analysis" }
+}
+Write-Host ($Orchestrator | ConvertTo-Json)
+
+
+Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
+ StatusCode = [HttpStatusCode]::OK
+ Body = $results
+ })
\ No newline at end of file
diff --git a/Domain_OrchestrationStarterTimer/function.json b/Domain_OrchestrationStarterTimer/function.json
new file mode 100644
index 000000000000..f5df3587c4c8
--- /dev/null
+++ b/Domain_OrchestrationStarterTimer/function.json
@@ -0,0 +1,15 @@
+{
+ "bindings": [
+ {
+ "name": "Timer",
+ "type": "timerTrigger",
+ "direction": "in",
+ "schedule": "0 30 4 * * *"
+ },
+ {
+ "name": "starter",
+ "type": "durableClient",
+ "direction": "in"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/Domain_OrchestrationStarterTimer/run.ps1 b/Domain_OrchestrationStarterTimer/run.ps1
new file mode 100644
index 000000000000..4662cff15a12
--- /dev/null
+++ b/Domain_OrchestrationStarterTimer/run.ps1
@@ -0,0 +1,15 @@
+param($Timer)
+
+$CurrentlyRunning = Get-Item "Cache_DomainAnalyser\CurrentlyRunning.txt" -ErrorAction SilentlyContinue | Where-Object -Property LastWriteTime -GT (Get-Date).AddHours(-24)
+if ($CurrentlyRunning) {
+ $Results = [pscustomobject]@{"Results" = "Already running. Please wait for the current instance to finish" }
+ Log-request -API "DomainAnalyser" -message "Attempted to start analysis but an instance was already running." -sev Info
+}
+else {
+ $InstanceId = Start-NewOrchestration -FunctionName 'DomainAnalyser_Orchestration'
+ Write-Host "Started orchestration with ID = '$InstanceId'"
+ $Orchestrator = New-OrchestrationCheckStatusResponse -Request $Request -InstanceId $InstanceId
+ Log-request -API "DomainAnalyser" -message "Starting Domain Analyser" -sev Info
+ $Results = [pscustomobject]@{"Results" = "Starting Domain Analyser" }
+}
+Write-Host ($Orchestrator | ConvertTo-Json)
diff --git a/EditGroup/run.ps1 b/EditGroup/run.ps1
index d3bbfecc12b8..f0a9ddeea00d 100644
--- a/EditGroup/run.ps1
+++ b/EditGroup/run.ps1
@@ -26,7 +26,7 @@ try {
}
catch {
Log-Request -user $request.headers.'x-ms-client-principal' -message "Add member API failed. $($_.Exception.Message)" -Sev "Error"
- $body = $results.add("Succesfully added the users $AddMembers to $($userobj.Groupid) $($_.Exception.Message)")
+ $body = $results.add("Failed to add $AddMembers to $($userobj.Groupid) $($_.Exception.Message)")
}
$RemoveMembers = ($userobj.Removemember).Split([Environment]::NewLine)
@@ -37,17 +37,51 @@ try {
New-GraphPostRequest -uri "https://graph.microsoft.com/beta/groups/$($userobj.groupid)/members/$($MemberInfo.id)/`$ref" -tenantid $Userobj.tenantid -type DELETE
Log-Request -API $APINAME -tenant $Userobj.tenantid -user $request.headers.'x-ms-client-principal' -message "Removed $($MemberInfo.UserPrincipalname) from $($userobj.displayname) group" -Sev "Info"
$body = $results.add("Success. Member $_ has been removed from $($userobj.Groupid)")
+ }
+ }
+}
+catch {
+ Log-Request -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $Userobj.tenantid -message "Add member API failed. $($_.Exception.Message)" -Sev "Error"
+ $body = $results.add("Could not remove $RemoveMembers from $($userobj.Groupid). $($_.Exception.Message)")
+}
+
+$AddOwners = ($userobj.Addowner).Split([Environment]::NewLine)
+try {
+ if ($AddOwners) {
+ $AddOwners | ForEach-Object {
+ $ID = "https://graph.microsoft.com/beta/users/" + (New-GraphGetRequest -uri "https://graph.microsoft.com/beta/users/$($_)" -tenantid $Userobj.tenantid).id
+ Write-Host $ID
+ $AddOwner = New-GraphPostRequest -uri "https://graph.microsoft.com/beta/groups/$($userobj.groupid)/owners/`$ref" -tenantid $Userobj.tenantid -type POST -body ('{"@odata.id": "' + $ID + '"}')
+ Log-Request -API $APINAME -tenant $Userobj.tenantid -user $request.headers.'x-ms-client-principal' -message "Added owner $_ to $($userobj.displayname) group" -Sev "Info"
+ $body = $results.add("Success. $_ has been added")
+
}
-
-
+
}
}
+catch {
+ Log-Request -user $request.headers.'x-ms-client-principal' -message "Add member API failed. $($_.Exception.Message)" -Sev "Error"
+ $body = $results.add("Failed to add $AddMembers to $($userobj.Groupid) $($_.Exception.Message)")
+}
+
+$RemoveOwners = ($userobj.RemoveOwner).Split([Environment]::NewLine)
+try {
+ if ($RemoveOwners) {
+ $RemoveOwners | ForEach-Object {
+ $MemberInfo = (New-GraphGetRequest -uri "https://graph.microsoft.com/beta/users/$($_)" -tenantid $Userobj.tenantid)
+ New-GraphPostRequest -uri "https://graph.microsoft.com/beta/groups/$($userobj.groupid)/owners/$($MemberInfo.id)/`$ref" -tenantid $Userobj.tenantid -type DELETE
+ Log-Request -API $APINAME -tenant $Userobj.tenantid -user $request.headers.'x-ms-client-principal' -message "Removed $($MemberInfo.UserPrincipalname) from $($userobj.displayname) group" -Sev "Info"
+ $body = $results.add("Success. Member $_ has been removed from $($userobj.Groupid)")
+ }
+ }
+}
catch {
Log-Request -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $Userobj.tenantid -message "Add member API failed. $($_.Exception.Message)" -Sev "Error"
$body = $results.add("Could not remove $RemoveMembers from $($userobj.Groupid). $($_.Exception.Message)")
}
+
$body = @{"Results" = ($results -join "
") }
# Associate values to output bindings by calling 'Push-OutputBinding'.
Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
diff --git a/EditUser/run.ps1 b/EditUser/run.ps1
index f30c2a148094..eb53a993740f 100644
--- a/EditUser/run.ps1
+++ b/EditUser/run.ps1
@@ -74,21 +74,21 @@ catch {
$results.add( "Succesfully edit user. The password is $password. We've failed to assign the license. $($_.Exception.Message)")
}
-#Add aliasses, removal currently not supported.
+#Add Aliases, removal currently not supported.
try {
- if ($aliasses) {
- foreach ($Alias in $aliasses) {
+ if ($Aliases) {
+ foreach ($Alias in $Aliases) {
New-GraphPostRequest -uri "https://graph.microsoft.com/beta/users/$($userobj.Userid)" -tenantid $Userobj.tenantid -type "patch" -body "{`"mail`": `"$Alias`"}" -verbose
}
New-GraphPostRequest -uri "https://graph.microsoft.com/beta/users/$($userobj.Userid)" -tenantid $Userobj.tenantid -type "patch" -body "{`"mail`": `"$UserprincipalName`"}" -verbose
- Log-Request -API $APINAME -tenant ($UserObj.tenantid) -user $request.headers.'x-ms-client-principal' -message "Added aliasses to $($userobj.displayname) license $($licences)" -Sev "Info"
+ Log-Request -API $APINAME -tenant ($UserObj.tenantid) -user $request.headers.'x-ms-client-principal' -message "Added Aliases to $($userobj.displayname) license $($licences)" -Sev "Info"
$results.add( "Success. User has been edited")
}
}
catch {
Log-Request -API $APINAME -tenant ($UserObj.tenantid) -user $request.headers.'x-ms-client-principal' -message "Alias API failed. $($_.Exception.Message)" -Sev "Error"
- $results.add( "Succesfully edited user. The password is $password. We've failed to create the aliasses: $($_.Exception.Message)")
+ $results.add( "Succesfully edited user. The password is $password. We've failed to create the Aliases: $($_.Exception.Message)")
}
if ($Request.body.CopyFrom -ne "") {
diff --git a/AssignApp/function.json b/ExecAccessChecks/function.json
similarity index 100%
rename from AssignApp/function.json
rename to ExecAccessChecks/function.json
diff --git a/AccessChecks/run.ps1 b/ExecAccessChecks/run.ps1
similarity index 74%
rename from AccessChecks/run.ps1
rename to ExecAccessChecks/run.ps1
index 168aa36e569f..04a01ed1ae21 100644
--- a/AccessChecks/run.ps1
+++ b/ExecAccessChecks/run.ps1
@@ -13,7 +13,7 @@ if ($Request.query.Permissions -eq "true") {
Log-Request -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Started permissions check" -Sev "Debug"
$Results = try {
$ExpectedPermissions = @(
- "Application.Read.All", "Application.ReadWrite.All", "AuditLog.Read.All", "Channel.Create", "Channel.Delete.All", "Channel.ReadBasic.All", "ChannelMember.Read.All", "ChannelMember.ReadWrite.All", "ChannelMessage.Delete", "ChannelMessage.Edit", "ChannelMessage.Read.All", "ChannelMessage.Send", "ChannelSettings.Read.All", "ChannelSettings.ReadWrite.All", "ConsentRequest.Read.All", "Device.Command", "Device.Read", "Device.Read.All", "DeviceManagementApps.ReadWrite.All", "DeviceManagementConfiguration.ReadWrite.All", "DeviceManagementManagedDevices.ReadWrite.All", "DeviceManagementRBAC.ReadWrite.All", "DeviceManagementServiceConfig.ReadWrite.All", "Directory.AccessAsUser.All", "Group.ReadWrite.All", "GroupMember.ReadWrite.All", "Mail.Send", "Mail.Send.Shared", "Member.Read.Hidden", "Organization.ReadWrite.All", "Policy.Read.All", "Policy.ReadWrite.AuthenticationFlows", "Policy.ReadWrite.AuthenticationMethod", "Policy.ReadWrite.Authorization", "Policy.ReadWrite.ConsentRequest", "Policy.ReadWrite.DeviceConfiguration", "PrivilegedAccess.Read.AzureResources", "PrivilegedAccess.ReadWrite.AzureResources", "Reports.Read.All", "RoleManagement.ReadWrite.Directory", "SecurityActions.ReadWrite.All", "SecurityEvents.ReadWrite.All", "ServiceHealth.Read.All", "ServiceMessage.Read.All", "Sites.ReadWrite.All", "Team.Create", "Team.ReadBasic.All", "TeamMember.ReadWrite.All", "TeamMember.ReadWriteNonOwnerRole.All", "TeamsActivity.Read", "TeamsActivity.Send", "TeamsApp.Read", "TeamsApp.Read.All", "TeamsApp.ReadWrite", "TeamsApp.ReadWrite.All", "TeamsAppInstallation.ReadForChat", "TeamsAppInstallation.ReadForTeam", "TeamsAppInstallation.ReadForUser", "TeamsAppInstallation.ReadWriteForChat", "TeamsAppInstallation.ReadWriteForTeam", "TeamsAppInstallation.ReadWriteForUser", "TeamsAppInstallation.ReadWriteSelfForChat", "TeamsAppInstallation.ReadWriteSelfForTeam", "TeamsAppInstallation.ReadWriteSelfForUser", "TeamSettings.Read.All", "TeamSettings.ReadWrite.All", "TeamsTab.Create", "TeamsTab.Read.All", "TeamsTab.ReadWrite.All", "TeamsTab.ReadWriteForChat", "TeamsTab.ReadWriteForTeam", "TeamsTab.ReadWriteForUser", "ThreatAssessment.ReadWrite.All", "UnifiedGroupMember.Read.AsGuest", "User.ManageIdentities.All", "User.Read", "User.ReadWrite.All", "UserAuthenticationMethod.Read.All", "UserAuthenticationMethod.ReadWrite", "UserAuthenticationMethod.ReadWrite.All"
+ "Application.Read.All", "Application.ReadWrite.All", "AuditLog.Read.All", "Channel.Create", "Channel.Delete.All", "Channel.ReadBasic.All", "ChannelMember.Read.All", "ChannelMember.ReadWrite.All", "ChannelMessage.Delete", "ChannelMessage.Edit", "ChannelMessage.Read.All", "ChannelMessage.Send", "ChannelSettings.Read.All", "ChannelSettings.ReadWrite.All", "ConsentRequest.Read.All", "Device.Command", "Device.Read", "Device.Read.All", "DeviceManagementApps.ReadWrite.All", "DeviceManagementConfiguration.ReadWrite.All", "DeviceManagementManagedDevices.ReadWrite.All", "DeviceManagementRBAC.ReadWrite.All", "DeviceManagementServiceConfig.ReadWrite.All", "Directory.AccessAsUser.All", "Domain.Read.All", "Group.ReadWrite.All", "GroupMember.ReadWrite.All", "Mail.Send", "Mail.Send.Shared", "Member.Read.Hidden", "Organization.ReadWrite.All", "Policy.Read.All", "Policy.ReadWrite.AuthenticationFlows", "Policy.ReadWrite.AuthenticationMethod", "Policy.ReadWrite.Authorization", "Policy.ReadWrite.ConsentRequest", "Policy.ReadWrite.DeviceConfiguration", "PrivilegedAccess.Read.AzureResources", "PrivilegedAccess.ReadWrite.AzureResources", "Reports.Read.All", "RoleManagement.ReadWrite.Directory", "SecurityActions.ReadWrite.All", "SecurityEvents.ReadWrite.All", "ServiceHealth.Read.All", "ServiceMessage.Read.All", "Sites.ReadWrite.All", "Team.Create", "Team.ReadBasic.All", "TeamMember.ReadWrite.All", "TeamMember.ReadWriteNonOwnerRole.All", "TeamsActivity.Read", "TeamsActivity.Send", "TeamsApp.Read", "TeamsApp.Read.All", "TeamsApp.ReadWrite", "TeamsApp.ReadWrite.All", "TeamsAppInstallation.ReadForChat", "TeamsAppInstallation.ReadForTeam", "TeamsAppInstallation.ReadForUser", "TeamsAppInstallation.ReadWriteForChat", "TeamsAppInstallation.ReadWriteForTeam", "TeamsAppInstallation.ReadWriteForUser", "TeamsAppInstallation.ReadWriteSelfForChat", "TeamsAppInstallation.ReadWriteSelfForTeam", "TeamsAppInstallation.ReadWriteSelfForUser", "TeamSettings.Read.All", "TeamSettings.ReadWrite.All", "TeamsTab.Create", "TeamsTab.Read.All", "TeamsTab.ReadWrite.All", "TeamsTab.ReadWriteForChat", "TeamsTab.ReadWriteForTeam", "TeamsTab.ReadWriteForUser", "ThreatAssessment.ReadWrite.All", "UnifiedGroupMember.Read.AsGuest", "User.ManageIdentities.All", "User.Read", "User.ReadWrite.All", "UserAuthenticationMethod.Read.All", "UserAuthenticationMethod.ReadWrite", "UserAuthenticationMethod.ReadWrite.All"
)
$GraphPermissions = ((Get-GraphToken -returnRefresh $true).scope).split(' ') -replace "https://graph.microsoft.com/", "" | Where-Object { $_ -notin @("email", "openid", "profile", ".default") }
$MissingPermissions = $ExpectedPermissions | Where-Object { $_ -notin $GraphPermissions }
diff --git a/ConverttoSharedMailbox/function.json b/ExecAssignApp/function.json
similarity index 100%
rename from ConverttoSharedMailbox/function.json
rename to ExecAssignApp/function.json
diff --git a/AssignApp/run.ps1 b/ExecAssignApp/run.ps1
similarity index 100%
rename from AssignApp/run.ps1
rename to ExecAssignApp/run.ps1
diff --git a/DisableUser/function.json b/ExecBackendURLs/function.json
similarity index 100%
rename from DisableUser/function.json
rename to ExecBackendURLs/function.json
diff --git a/ExecBackendURLs/run.ps1 b/ExecBackendURLs/run.ps1
new file mode 100644
index 000000000000..29544b2df611
--- /dev/null
+++ b/ExecBackendURLs/run.ps1
@@ -0,0 +1,37 @@
+using namespace System.Net
+
+# Input bindings are passed in via param block.
+param($Request, $TriggerMetadata)
+
+$APIName = $TriggerMetadata.FunctionName
+Log-Request -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug"
+
+$Subscription = ($ENV:WEBSITE_OWNER_NAME).split('+') | Select-Object -First 1
+$SWAName = $ENV:Website_SITE_NAME -replace "cipp", "CIPP-SWA-"
+# Write to the Azure Functions log stream.
+Write-Host "PowerShell HTTP trigger function processed a request."
+
+$results = @"
+
Resource Group
+
+Resource group
+Keyvault (Password storage)
+
Function app
+Function Application (Overview)
+Function Application (Configuration)
+Function Application (Deployment Center)
+
Static Web App
+Static Web App (Custom Domains)
+Static Web App (Role Management)
+
+
+"@
+
+
+$body = @{Results = $Results }
+
+# Associate values to output bindings by calling 'Push-OutputBinding'.
+Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
+ StatusCode = [httpstatusCode]::OK
+ Body = $body
+ })
diff --git a/ExcludeTenant/function.json b/ExecConverttoSharedMailbox/function.json
similarity index 100%
rename from ExcludeTenant/function.json
rename to ExecConverttoSharedMailbox/function.json
diff --git a/ConverttoSharedMailbox/run.ps1 b/ExecConverttoSharedMailbox/run.ps1
similarity index 100%
rename from ConverttoSharedMailbox/run.ps1
rename to ExecConverttoSharedMailbox/run.ps1
diff --git a/Logs/function.json b/ExecDisableUser/function.json
similarity index 100%
rename from Logs/function.json
rename to ExecDisableUser/function.json
diff --git a/DisableUser/run.ps1 b/ExecDisableUser/run.ps1
similarity index 100%
rename from DisableUser/run.ps1
rename to ExecDisableUser/run.ps1
diff --git a/NotifyEmail/function.json b/ExecExcludeTenant/function.json
similarity index 100%
rename from NotifyEmail/function.json
rename to ExecExcludeTenant/function.json
diff --git a/ExcludeTenant/run.ps1 b/ExecExcludeTenant/run.ps1
similarity index 96%
rename from ExcludeTenant/run.ps1
rename to ExecExcludeTenant/run.ps1
index 1cc65a15adf0..184b01445d87 100644
--- a/ExcludeTenant/run.ps1
+++ b/ExecExcludeTenant/run.ps1
@@ -23,7 +23,8 @@ try {
$name = $Request.Query.TenantFilter
if ($Request.Query.AddExclusion) {
Add-Content -Value "$($name)|$($username)|$($date)" -Path "ExcludedTenants"
- Log-Request -API $APINAME -tenant $($name) -user $request.headers.'x-ms-client-principal' -message "Added exclusion for customer $($name)" -Sev "Info"
+ Remove-CIPPCache
+ Log-Request -API $APINAME -tenant $($name) -user $request.headers.'x-ms-client-principal' -message "Added exclusion for customer $($name)" -Sev "Info"
$body = [pscustomobject]@{"Results" = "Success. We've added $name to the excluded tenants." }
}
@@ -31,15 +32,20 @@ try {
$Content = [System.IO.File]::ReadAllLines("ExcludedTenants")
$Content = $Content -replace $name, ''
$Content | Set-Content -Path "ExcludedTenants"
+ Remove-CIPPCache
Log-Request -API $APINAME -tenant $($name) -user $request.headers.'x-ms-client-principal' -message "Removed exclusion for customer $($name)" -Sev "Info"
$body = [pscustomobject]@{"Results" = "Success. We've removed $name from the excluded tenants." }
}
+
+
}
catch {
Log-Request -API $APINAME -tenant $($name) -user $request.headers.'x-ms-client-principal' -message "Exclusion API failed. $($_.Exception.Message)" -Sev "Error"
$body = [pscustomobject]@{"Results" = "Failed. $($_.Exception.Message)" }
}
+
+
# Associate values to output bindings by calling 'Push-OutputBinding'.
Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
StatusCode = [HttpStatusCode]::OK
diff --git a/NotifyTeams/function.json b/ExecOffboardUser/function.json
similarity index 100%
rename from NotifyTeams/function.json
rename to ExecOffboardUser/function.json
diff --git a/OffboardUser/run.ps1 b/ExecOffboardUser/run.ps1
similarity index 100%
rename from OffboardUser/run.ps1
rename to ExecOffboardUser/run.ps1
diff --git a/OffboardUser/function.json b/ExecResetPass/function.json
similarity index 100%
rename from OffboardUser/function.json
rename to ExecResetPass/function.json
diff --git a/ResetPass/run.ps1 b/ExecResetPass/run.ps1
similarity index 100%
rename from ResetPass/run.ps1
rename to ExecResetPass/run.ps1
diff --git a/ResetPass/function.json b/ExecSendPush/function.json
similarity index 100%
rename from ResetPass/function.json
rename to ExecSendPush/function.json
diff --git a/ExecSendPush/run.ps1 b/ExecSendPush/run.ps1
new file mode 100644
index 000000000000..70e031db2a7b
--- /dev/null
+++ b/ExecSendPush/run.ps1
@@ -0,0 +1,114 @@
+using namespace System.Net
+
+# Input bindings are passed in via param block.
+param($Request, $TriggerMetadata)
+
+$APIName = $TriggerMetadata.FunctionName
+Log-Request -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug"
+
+$TenantFilter = $Request.Query.TenantFilter
+$UserEmail = $Request.Query.UserEmail
+$MFAAppID = '981f26a1-7f43-403b-a875-f8b09b8cd720'
+
+# Function to keep trying to get the access token while we wait for MS to actually set the temp password
+function get-clientaccess {
+ param(
+ $uri,
+ $body,
+ $count = 1
+ )
+ try {
+ $ClientToken = Invoke-RestMethod -Method post -Uri $uri -Body $body -ea stop
+ } catch {
+ if ($count -lt 20) {
+
+ $count++
+ Start-Sleep 1
+ $ClientToken = get-clientaccess -uri $uri -body $body -count $count
+ } else {
+ Throw "Could not get Client Token: $_"
+ }
+ }
+ return $ClientToken
+}
+
+
+# Get all service principals
+$SPResult = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/servicePrincipals?`$top=999&`$select=id,appId" -tenantid $TenantFilter
+
+# Check if we have one for the MFA App
+$SPID = ($SPResult | where-object { $_.appId -eq $MFAAppID }).id
+
+# Create a serivce principal if needed
+if (!$SPID) {
+
+ $SPBody = [pscustomobject]@{
+ appId = $MFAAppID
+ }
+ $SPID = (New-GraphPostRequest -uri "https://graph.microsoft.com/v1.0/servicePrincipals" -tenantid $TenantFilter -type POST -body $SPBody -verbose).id
+}
+
+
+$PassReqBody = @{
+ "passwordCredential" = @{
+ "displayName" = "MFA Temporary Password"
+ "endDateTime" = $(((Get-Date).addminutes(5)))
+ "startDateTime" = $((Get-Date).addminutes(-5))
+ }
+} | ConvertTo-Json -depth 5
+
+$TempPass = (New-GraphPostRequest -uri "https://graph.microsoft.com/v1.0/servicePrincipals/$SPID/addPassword" -tenantid $TenantFilter -type POST -body $PassReqBody -verbose).secretText
+
+# Give it a chance to apply
+#Start-Sleep 5
+
+# Generate the XML for the push request
+$XML = @"
+
+1.0
+$UserEmail
+en-usOverrideVoiceOtpfalse69ff05bf-eb61-47f7-a70e-e7d77b6d47d0
+truetrueradiusUNKNOWN:
+"@
+
+# Request to get client token
+$body = @{
+ 'resource' = 'https://adnotifications.windowsazure.com/StrongAuthenticationService.svc/Connector'
+ 'client_id' = $MFAAppID
+ 'client_secret' = $TempPass
+ 'grant_type' = "client_credentials"
+ 'scope' = "openid"
+}
+
+# Attempt to get a token using the temp password
+$ClientUri = "https://login.microsoftonline.com/$TenantFilter/oauth2/token"
+try {
+ $ClientToken = get-clientaccess -Uri $ClientUri -Body $body
+} catch {
+ $Body = "Failed to create temporary password"
+}
+
+# If we got a token send a push
+if ($ClientToken) {
+
+ $ClientHeaders = @{ "Authorization" = "Bearer $($ClientToken.access_token)" }
+
+ $obj = Invoke-RestMethod -uri 'https://adnotifications.windowsazure.com/StrongAuthenticationService.svc/Connector//BeginTwoWayAuthentication' -Method POST -Headers $ClientHeaders -Body $XML -ContentType 'application/xml'
+
+ if ($obj.BeginTwoWayAuthenticationResponse.result) {
+ $Body = " Received an MFA confirmation: $($obj.BeginTwoWayAuthenticationResponse.result.value | Out-String)
"
+ }
+ if ($obj.BeginTwoWayAuthenticationResponse.AuthenticationResult -ne $true) {
+ $Body = " Authentication Failed! Does the user have Push/Phone call MFA configured? Errorcode: $($obj.BeginTwoWayAuthenticationResponse.result.value | out-string)
"
+ }
+
+}
+
+$Results = [pscustomobject]@{"Results" = $Body }
+Log-Request -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Sent push request to $UserEmail - Result: $($obj.BeginTwoWayAuthenticationResponse.result.value | out-string)" -Sev "Info"
+
+Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
+ StatusCode = [HttpStatusCode]::OK
+ Body = $Results
+ })
+
diff --git a/GetVersion/run.ps1 b/GetVersion/run.ps1
index 3336e63cfbc3..d6db3204d09b 100644
--- a/GetVersion/run.ps1
+++ b/GetVersion/run.ps1
@@ -6,10 +6,21 @@ param($Request, $TriggerMetadata)
$APIName = $TriggerMetadata.FunctionName
Log-Request -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug"
-$Version = Get-Content "version_latest.txt"
+$APIVersion = Get-Content "version_latest.txt" | Out-String
+$CIPPVersion = $request.query.localversion
+$RemoteAPIVersion = Invoke-RestMethod -Uri "https://raw.githubusercontent.com/KelvinTegelaar/CIPP-API/master/version_latest.txt"
+$RemoteCIPPVersion = Invoke-RestMethod -Uri "https://raw.githubusercontent.com/KelvinTegelaar/CIPP/master/version_latest.txt"
+
+$version = [PSCustomObject]@{
+ LocalCIPPVersion = $CIPPVersion
+ RemoteCIPPVersion = $RemoteCIPPVersion
+ LocalCIPPAPIVersion = $APIVersion
+ RemoteCIPPAPIVersion = $RemoteAPIVersion
+ OutOfDateCIPP = ([version]$RemoteCIPPVersion -ge [version]$CIPPVersion)
+ OutOfDateCIPPAPI = ([version]$RemoteAPIVersion -ge [version]$APIVersion)
+}
# Write to the Azure Functions log stream.
-Write-Host "Version is $($Version)"
# Associate values to output bindings by calling 'Push-OutputBinding'.
Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
diff --git a/GraphHelper.psm1 b/GraphHelper.psm1
index 25dd8b369602..3db015831e1b 100644
--- a/GraphHelper.psm1
+++ b/GraphHelper.psm1
@@ -47,13 +47,8 @@ function Log-Request ($message, $tenant, $API, $user, $sev) {
$LogMutex.ReleaseMutex()
}
-function New-GraphGetRequest ($uri, $tenantid, $scope, $AsApp) {
- $TenantList = Get-Content 'Tenants.cache.json' -ErrorAction SilentlyContinue | ConvertFrom-Json
- $Skiplist = Get-Content "ExcludedTenants" | ConvertFrom-Csv -Delimiter "|" -Header "Name", "User", "Date"
+function New-GraphGetRequest ($uri, $tenantid, $scope, $AsApp, $noPagination) {
- if ($tenantid -ne $null -and $tenantid -in $($Skiplist.name)) {
- return "Not allowed. Tenant is in exclusion list."
- }
if ($scope -eq "ExchangeOnline") {
$Headers = Get-GraphToken -AppID 'a0c73c16-a7e3-4564-9a95-2bdf47383716' -RefreshToken $ENV:ExchangeRefreshToken -Scope 'https://outlook.office365.com/.default' -Tenantid $tenantid
}
@@ -62,13 +57,13 @@ function New-GraphGetRequest ($uri, $tenantid, $scope, $AsApp) {
}
Write-Verbose "Using $($uri) as url"
$nextURL = $uri
- #not a fan of this, have to reconsider and change. Seperate function?
- if ($tenantid -in $tenantlist.defaultdomainname -or $uri -like "https://graph.microsoft.com/beta/contracts?`$top=999" -or $uri -like "*/customers/*") {
+
+ if ((Get-AuthorisedRequest -Uri $uri -TenantID $tenantid)) {
$ReturnedData = do {
try {
$Data = (Invoke-RestMethod -Uri $nextURL -Method GET -Headers $headers -ContentType "application/json; charset=utf-8")
if ($data.value) { $data.value } else { ($Data) }
- $nextURL = $data.'@odata.nextLink'
+ if ($noPagination) { $nextURL = $null } else { $nextURL = $data.'@odata.nextLink' }
}
catch {
$Message = ($_.ErrorDetails.Message | ConvertFrom-Json).error.message
@@ -84,19 +79,14 @@ function New-GraphGetRequest ($uri, $tenantid, $scope, $AsApp) {
}
function New-GraphPOSTRequest ($uri, $tenantid, $body, $type, $scope, $AsApp) {
- $Skiplist = Get-Content "ExcludedTenants" | ConvertFrom-Csv -Delimiter "|" -Header "Name", "User", "Date"
- $TenantList = Get-Content 'Tenants.cache.json' -ErrorAction SilentlyContinue | ConvertFrom-Json
- if ($tenantid -ne $null -and $tenantid -in $($Skiplist.name)) {
- return "Not allowed. Tenant is in exclusion list."
- }
$headers = Get-GraphToken -tenantid $tenantid -scope $scope -AsApp $asapp
Write-Verbose "Using $($uri) as url"
if (!$type) {
$type = 'POST'
}
- #not a fan of this, have to reconsider and change. Seperate function?
- if ($tenantid -in $tenantlist.defaultdomainname -or $uri -like "*/contracts*" -or $uri -like "*/customers/*") {
+
+ if ((Get-AuthorisedRequest -Uri $uri -TenantID $tenantid)) {
try {
$ReturnedData = (Invoke-RestMethod -Uri $($uri) -Method $TYPE -Body $body -Headers $headers -ContentType "application/json; charset=utf-8")
}
@@ -118,3 +108,151 @@ function convert-skuname($skuname, $skuID) {
if ($skuID) { $ReturnedName = ($ConvertTable | Where-Object { $_.guid -eq $skuid } | Select-Object -Last 1).'Product_Display_Name' }
if ($ReturnedName) { return $ReturnedName } else { return $skuname, $skuID }
}
+
+function Get-ClassicAPIToken($tenantID, $Resource) {
+ $uri = "https://login.microsoftonline.com/$($TenantID)/oauth2/token"
+ $body = "resource=$Resource&grant_type=refresh_token&refresh_token=$($ENV:ExchangeRefreshToken)"
+ try {
+ $token = Invoke-RestMethod $uri -Body $body -ContentType "application/x-www-form-urlencoded" -ErrorAction SilentlyContinue -Method post
+ return $token
+ }
+ catch {
+ Write-Error "Failed to obtain Classic API Token for $Tenant - $_"
+ }
+}
+
+function New-ClassicAPIGetRequest($TenantID, $Uri, $Method = 'GET', $Resource = 'https://admin.microsoft.com') {
+ $token = Get-ClassicAPIToken -Tenant $tenantID -Resource $Resource
+
+ $NextURL = $Uri
+
+ if ((Get-AuthorisedRequest -Uri $uri -TenantID $tenantid)) {
+ $ReturnedData = do {
+ try {
+ $Data = Invoke-RestMethod -ContentType "application/json;charset=UTF-8" -Uri $NextURL -Method $Method -Headers @{
+ Authorization = "Bearer $($token.access_token)";
+ "x-ms-client-request-id" = [guid]::NewGuid().ToString();
+ "x-ms-client-session-id" = [guid]::NewGuid().ToString()
+ 'x-ms-correlation-id' = [guid]::NewGuid()
+ 'X-Requested-With' = 'XMLHttpRequest'
+ }
+ $Data
+ if ($noPagination) { $nextURL = $null } else { $nextURL = $data.NextLink }
+ }
+ catch {
+ throw "Failed to make Classic Get Request $_"
+ }
+ } until ($null -eq $NextURL)
+ return $ReturnedData
+ }
+ else {
+ Write-Error "Not allowed. You cannot manage your own tenant or tenants not under your scope"
+ }
+}
+
+function New-ClassicAPIPostRequest($TenantID, $Uri, $Method = 'POST', $Resource = 'https://admin.microsoft.com', $Body) {
+
+ $token = Get-ClassicAPIToken -Tenant $tenantID -Resource $Resource
+
+ if ((Get-AuthorisedRequest -Uri $uri -TenantID $tenantid)) {
+ try {
+ $ReturnedData = Invoke-RestMethod -ContentType "application/json;charset=UTF-8" -Uri $Uri -Method $Method -Body $Body -Headers @{
+ Authorization = "Bearer $($token.access_token)";
+ "x-ms-client-request-id" = [guid]::NewGuid().ToString();
+ "x-ms-client-session-id" = [guid]::NewGuid().ToString()
+ 'x-ms-correlation-id' = [guid]::NewGuid()
+ 'X-Requested-With' = 'XMLHttpRequest'
+ }
+
+ }
+ catch {
+ throw "Failed to make Classic Get Request $_"
+ }
+ return $ReturnedData
+ }
+ else {
+ Write-Error "Not allowed. You cannot manage your own tenant or tenants not under your scope"
+ }
+}
+
+function Get-AuthorisedRequest($TenantID, $Uri) {
+ if ($uri -like "https://graph.microsoft.com/beta/contracts?`$top=999" -or $uri -like "*/customers/*") {
+ return $true
+ }
+ if ($TenantID -in (Get-Tenants).defaultdomainname) {
+ return $true
+ }
+ else {
+ return $false
+ }
+
+}
+
+function Get-Tenants {
+ param (
+ [Parameter( ParameterSetName = 'Skip', Mandatory = $True )]
+ [switch]$SkipList,
+ [Parameter( ParameterSetName = 'Standard')]
+ [switch]$IncludeAll
+
+ )
+
+ $cachefile = 'tenants.cache.json'
+
+ if ((!$Script:SkipListCache -and !$Script:SkipListCacheEmpty) -or !$Script:IncludedTenantsCache) {
+ # We create the excluded tenants file. This is not set to force so will not overwrite
+ New-Item -ErrorAction SilentlyContinue -ItemType File -Path "ExcludedTenants"
+ $Script:SkipListCache = Get-Content "ExcludedTenants" | ConvertFrom-Csv -Delimiter "|" -Header "Name", "User", "Date"
+ if ($null -eq $Script:SkipListCache) {
+ $Script:SkipListCacheEmpty = $true
+ }
+
+ # Load or refresh the cache if older than 24 hours
+ $Testfile = Get-Item $cachefile -ErrorAction SilentlyContinue | Where-Object -Property LastWriteTime -GT (Get-Date).Addhours(-24)
+ if ($Testfile) {
+ $Script:IncludedTenantsCache = Get-Content $cachefile -ErrorAction SilentlyContinue | ConvertFrom-Json
+ }
+ else {
+ $Script:IncludedTenantsCache = (New-GraphGetRequest -uri "https://graph.microsoft.com/beta/contracts?`$top=999" -tenantid $ENV:Tenantid) | Select-Object CustomerID, DefaultdomainName, DisplayName, domains | Where-Object -Property DefaultdomainName -NotIn $Script:SkipListCache.name
+ if ($Script:IncludedTenantsCache) {
+ $Script:IncludedTenantsCache | ConvertTo-Json | Out-File $cachefile
+ }
+ }
+ }
+ if ($SkipList) {
+ return $Script:SkipListCache
+ }
+ if ($IncludeAll) {
+ return (New-GraphGetRequest -uri "https://graph.microsoft.com/beta/contracts?`$top=999" -tenantid $ENV:Tenantid) | Select-Object CustomerID, DefaultdomainName, DisplayName, domains
+ }
+ else {
+ return $Script:IncludedTenantsCache
+ }
+}
+
+function Remove-CIPPCache {
+ Remove-Item 'tenants.cache.json' -Force
+ Get-ChildItem -Path "Cache_BestPracticeAnalyser" -Filter *.json | Remove-Item -Force -ErrorAction SilentlyContinue
+ Get-ChildItem -Path "Cache_DomainAnalyser" -Filter *.json | Remove-Item -Force -ErrorAction SilentlyContinue
+ $Script:SkipListCache = $Null
+ $Script:SkipListCacheEmpty = $Null
+ $Script:IncludedTenantsCache = $Null
+}
+
+function New-ExoRequest ($tenantid, $cmdlet, $cmdParams) {
+ $Headers = Get-GraphToken -AppID 'a0c73c16-a7e3-4564-9a95-2bdf47383716' -RefreshToken $ENV:ExchangeRefreshToken -Scope 'https://outlook.office365.com/.default' -Tenantid $tenantid
+ if ((Get-AuthorisedRequest -TenantID $tenantid)) {
+ $tenant = (get-tenants | Where-Object -Property defaultDomainName -EQ $tenantid).customerid
+ $ExoBody = @{
+ CmdletInput = @{
+ CmdletName = $cmdlet
+ Parameters = @{}
+ }
+ } | ConvertTo-Json
+ $ReturnedData = Invoke-RestMethod "https://outlook.office365.com/adminapi/beta/$($tenant)/InvokeCommand" -Method POST -Body $ExoBody -Headers $Headers -ContentType "application/json; charset=utf-8"
+ return $ReturnedData.value
+ }
+ else {
+ Write-Error "Not allowed. You cannot manage your own tenant or tenants not under your scope"
+ }
+}
\ No newline at end of file
diff --git a/ListContacts/function.json b/ListContacts/function.json
new file mode 100644
index 000000000000..306b0c51e560
--- /dev/null
+++ b/ListContacts/function.json
@@ -0,0 +1,19 @@
+{
+ "bindings": [
+ {
+ "authLevel": "anonymous",
+ "type": "httpTrigger",
+ "direction": "in",
+ "name": "Request",
+ "methods": [
+ "get",
+ "post"
+ ]
+ },
+ {
+ "type": "http",
+ "direction": "out",
+ "name": "Response"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/NotifyEmail/run.ps1 b/ListContacts/run.ps1
similarity index 53%
rename from NotifyEmail/run.ps1
rename to ListContacts/run.ps1
index e1a20af4be1a..dd3db876f654 100644
--- a/NotifyEmail/run.ps1
+++ b/ListContacts/run.ps1
@@ -6,22 +6,26 @@ param($Request, $TriggerMetadata)
$APIName = $TriggerMetadata.FunctionName
Log-Request -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug"
+$selectlist = "id", "companyName","displayName","mail","onPremisesSyncEnabled","editURL"
# Write to the Azure Functions log stream.
Write-Host "PowerShell HTTP trigger function processed a request."
+
# Interact with query parameters or the body of the request.
$TenantFilter = $Request.Query.TenantFilter
-if ($TenantFilter) {
- $GraphRequest = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/users?`$top=999" -tenantid $TenantFilter
-}
-else {
- $GraphRequest = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/users?`$top=999"
-}
+$ContactID = $Request.Query.ContactID
+Write-Host "Tenant Filter: $TenantFilter"
+
+$GraphRequest = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/contacts/$($ContactID)?`$top=999&`$select=$($selectlist -join ',')" -tenantid $TenantFilter | Select-Object $selectlist | foreach-object {
+ $_.editURL = "https://outlook.office365.com/ecp/@$TenantFilter/UsersGroups/EditContact.aspx?exsvurl=1&realm=$($Env:TenantID)&mkt=en-US&id=$($_.id)"
+ $_
+}
# Associate values to output bindings by calling 'Push-OutputBinding'.
Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
StatusCode = [HttpStatusCode]::OK
- Body = $GraphRequest
- })
\ No newline at end of file
+ Body = @($GraphRequest)
+ })
+
diff --git a/ListGroups/run.ps1 b/ListGroups/run.ps1
index 9bcf877913a1..b83ce69622fc 100644
--- a/ListGroups/run.ps1
+++ b/ListGroups/run.ps1
@@ -13,19 +13,23 @@ Write-Host "PowerShell HTTP trigger function processed a request."
# Interact with query parameters or the body of the request.
$TenantFilter = $Request.Query.TenantFilter
- $selectstring = "id,createdDateTime,displayName,description,mail,mailEnabled,mailNickname,resourceProvisioningOptions,securityEnabled,visibility,organizationId,onPremisesSamAccountName,membershipRule,grouptypes"
+$selectstring = "id,createdDateTime,displayName,description,mail,mailEnabled,mailNickname,resourceProvisioningOptions,securityEnabled,visibility,organizationId,onPremisesSamAccountName,membershipRule,grouptypes"
-if($Request.Query.GroupID){
-$groupid = $Request.query.groupid
-$selectstring = "id,createdDateTime,displayName,description,mail,mailEnabled,mailNickname,resourceProvisioningOptions,securityEnabled,visibility,organizationId,onPremisesSamAccountName,membershipRule"
+if ($Request.Query.GroupID) {
+ $groupid = $Request.query.groupid
+ $selectstring = "id,createdDateTime,displayName,description,mail,mailEnabled,mailNickname,resourceProvisioningOptions,securityEnabled,visibility,organizationId,onPremisesSamAccountName,membershipRule"
}
-if($Request.Query.members){
+if ($Request.Query.members) {
$members = "members"
$selectstring = "id,createdDateTime,displayName,description,hideFromOutlookClients,hideFromAddressLists,mail,mailEnabled,mailNickname,resourceProvisioningOptions,securityEnabled,visibility,organizationId,onPremisesSamAccountName,membershipRule"
}
+if ($Request.Query.owners) {
+ $members = "owners"
+ $selectstring = "id,createdDateTime,displayName,description,hideFromOutlookClients,hideFromAddressLists,mail,mailEnabled,mailNickname,resourceProvisioningOptions,securityEnabled,visibility,organizationId,onPremisesSamAccountName,membershipRule"
+}
- $GraphRequest = New-GraphGetRequest -asApp $true -uri "https://graph.microsoft.com/beta/groups/$($GroupID)/$($members)?`$top=999&select=$selectstring" -tenantid $TenantFilter | select-object *,@{ Name = 'primDomain'; Expression = { $_.mail -split "@" | Select-Object -last 1 } }
+$GraphRequest = New-GraphGetRequest -asApp $true -uri "https://graph.microsoft.com/beta/groups/$($GroupID)/$($members)?`$top=999&select=$selectstring" -tenantid $TenantFilter | Select-Object *, @{ Name = 'primDomain'; Expression = { $_.mail -split "@" | Select-Object -Last 1 } }
# Associate values to output bindings by calling 'Push-OutputBinding'.
diff --git a/ListLogs/function.json b/ListLogs/function.json
new file mode 100644
index 000000000000..306b0c51e560
--- /dev/null
+++ b/ListLogs/function.json
@@ -0,0 +1,19 @@
+{
+ "bindings": [
+ {
+ "authLevel": "anonymous",
+ "type": "httpTrigger",
+ "direction": "in",
+ "name": "Request",
+ "methods": [
+ "get",
+ "post"
+ ]
+ },
+ {
+ "type": "http",
+ "direction": "out",
+ "name": "Response"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/Logs/run.ps1 b/ListLogs/run.ps1
similarity index 69%
rename from Logs/run.ps1
rename to ListLogs/run.ps1
index 5322a5cb4a92..b265ad2df70b 100644
--- a/Logs/run.ps1
+++ b/ListLogs/run.ps1
@@ -7,12 +7,10 @@ $APIName = $TriggerMetadata.FunctionName
Log-Request -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug"
-$LogLevel = if ($Request.Query.LogLevel) { ($Request.query.Loglevel).split(',') } else { "Info", "Warn", "Error", "Critical" }
-# Write to the Azure Functions log stream.
-Write-Host "PowerShell HTTP trigger function processed a request."
-# Interact with query parameters or the body of the request.
+$LogLevel = if ($Request.Query.Severity) { ($Request.query.Severity).split(',') } else { "Info", "Warn", "Error", "Critical" }
$date = if ($Request.Query.DateFilter) { $Request.query.DateFilter } else { (Get-Date).ToString('MMyyyy') }
-$ReturnedLog = Get-Content "$($date).log" | ConvertFrom-Csv -Header "DateTime", "Tenant", "API", "Message", "User", "Severity" -Delimiter "|" | Where-Object -Property Severity -In $LogLevel
+$username = if ($Request.Query.User) { $Request.Query.User } else { '*' }
+$ReturnedLog = Get-Content "$($date).log" | ConvertFrom-Csv -Header "DateTime", "Tenant", "API", "Message", "User", "Severity" -Delimiter "|" | Where-Object { $_.Severity -In $LogLevel -and $_.user -like $username }
if ($request.query.last) {
$ReturnedLog = $ReturnedLog | Select-Object -Last $request.query.last
diff --git a/ListTenants/run.ps1 b/ListTenants/run.ps1
index 55366c926784..aa30f8a7ee2d 100644
--- a/ListTenants/run.ps1
+++ b/ListTenants/run.ps1
@@ -6,16 +6,9 @@ param($Request, $TriggerMetadata)
$APIName = $TriggerMetadata.FunctionName
Log-Request -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug"
-# We create the excluded tenants file. This is not set to force so will not overwrite
-New-Item -ErrorAction SilentlyContinue -ItemType File -Path "ExcludedTenants"
-
-# Set cache locations
-$cachefile = 'tenants.cache.json'
-
# Clear Cache
if ($request.Query.ClearCache -eq "true") {
- Remove-Item $cachefile -Force
- Get-ChildItem -Path "Cache_BestPracticeAnalyser" -Filter *.json | Remove-Item -Force -ErrorAction SilentlyContinue
+ Remove-CIPPCache
$GraphRequest = [pscustomobject]@{"Results" = "Successfully completed request." }
Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
StatusCode = [HttpStatusCode]::OK
@@ -24,30 +17,11 @@ if ($request.Query.ClearCache -eq "true") {
exit
}
-# Get the list of tenants to skip, create the file first if it does not exist yet.
-$Skiplist = (Get-Content ExcludedTenants -ErrorAction SilentlyContinue | ConvertFrom-Csv -Delimiter "|" -Header "name", "date", "user").name
+$Body = Get-Tenants
-# Get the tenant cache file where it is under 24 hours old. If it's over 24 hours old, re-create it
-$Testfile = Get-Item $cachefile -ErrorAction SilentlyContinue | Where-Object -Property LastWriteTime -GT (Get-Date).Addhours(-24)
-if ($Testfile) {
- $GraphRequest = Get-Content $cachefile | ConvertFrom-Json
- Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
- StatusCode = [HttpStatusCode]::OK
- Body = $GraphRequest | Where-Object -Property DefaultdomainName -NotIn $Skiplist
- })
- exit
-}
-else {
- Write-Host "Grabbing all tenants via Graph API" -ForegroundColor Green
- $GraphRequest = (New-GraphGetRequest -uri "https://graph.microsoft.com/beta/contracts?`$top=999" -tenantid $ENV:Tenantid) | Select-Object CustomerID, DefaultdomainName, DisplayName, domains | Where-Object -Property DefaultdomainName -NotIn $Skiplist
- # Associate values to output bindings by calling 'Push-OutputBinding'.
- Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
- StatusCode = [HttpStatusCode]::OK
- Body = $GraphRequest
- })
+
+Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
+ StatusCode = [HttpStatusCode]::OK
+ Body = $Body
+ })
- # Re have returned tenants now, but we still need to generate the cache file and save it
- if ($GraphRequest) {
- $GraphRequest | ConvertTo-Json | Out-File $cachefile
- }
-}
diff --git a/ListUserConditionalAccessPolicies/function.json b/ListUserConditionalAccessPolicies/function.json
new file mode 100644
index 000000000000..306b0c51e560
--- /dev/null
+++ b/ListUserConditionalAccessPolicies/function.json
@@ -0,0 +1,19 @@
+{
+ "bindings": [
+ {
+ "authLevel": "anonymous",
+ "type": "httpTrigger",
+ "direction": "in",
+ "name": "Request",
+ "methods": [
+ "get",
+ "post"
+ ]
+ },
+ {
+ "type": "http",
+ "direction": "out",
+ "name": "Response"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/ListUserConditionalAccessPolicies/run.ps1 b/ListUserConditionalAccessPolicies/run.ps1
new file mode 100644
index 000000000000..99112b307916
--- /dev/null
+++ b/ListUserConditionalAccessPolicies/run.ps1
@@ -0,0 +1,451 @@
+using namespace System.Net
+
+# Input bindings are passed in via param block.
+param($Request, $TriggerMetadata)
+
+$APIName = $TriggerMetadata.FunctionName
+Log-Request -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug"
+
+
+function Get-LocationNameFromId {
+ [CmdletBinding()]
+ param (
+ [Parameter()]
+ $ID,
+
+ [Parameter(Mandatory = $true)]
+ $Locations
+ )
+ if ($id -eq 'All') {
+ return 'All'
+ }
+ $DisplayName = $Locations | ? { $_.id -eq $ID } | Select -ExpandProperty DisplayName
+ if ([string]::IsNullOrEmpty($displayName)) {
+ return ""
+ } else {
+ return $DisplayName
+ }
+}
+
+function Get-RoleNameFromId {
+ [CmdletBinding()]
+ param (
+ [Parameter()]
+ $ID,
+
+ [Parameter(Mandatory = $true)]
+ $RoleDefinitions
+ )
+ if ($id -eq 'All') {
+ return 'All'
+ }
+ $DisplayName = $RoleDefinitions | ? { $_.id -eq $ID } | Select -ExpandProperty DisplayName
+ if ([string]::IsNullOrEmpty($displayName)) {
+ return ""
+ } else {
+ return $DisplayName
+ }
+}
+
+function Get-UserNameFromId {
+ [CmdletBinding()]
+ param (
+ [Parameter()]
+ $ID,
+
+ [Parameter(Mandatory = $true)]
+ $Users
+ )
+ if ($id -eq 'All') {
+ return 'All'
+ }
+ $DisplayName = $Users | ? { $_.id -eq $ID } | Select -ExpandProperty DisplayName
+ if ([string]::IsNullOrEmpty($displayName)) {
+ return ""
+ } else {
+ return $DisplayName
+ }
+}
+
+function Get-GroupNameFromId {
+ param (
+ [Parameter()]
+ $ID,
+
+ [Parameter(Mandatory = $true)]
+ $Groups
+ )
+ if ($id -eq 'All') {
+ return 'All'
+ }
+ $DisplayName = $Groups | ? { $_.id -eq $ID } | Select -ExpandProperty DisplayName
+ if ([string]::IsNullOrEmpty($displayName)) {
+ return "No Data"
+ } else {
+ return $DisplayName
+ }
+}
+
+function Get-ApplicationNameFromId {
+ [CmdletBinding()]
+ param (
+ [Parameter()]
+ $ID,
+
+ [Parameter(Mandatory = $true)]
+ $Applications
+ )
+ if ($id -eq 'All') {
+ return 'All'
+ }
+ switch ($id) {
+ 00000004-0000-0ff1-ce00-000000000000 { $return = 'Microsoft.Lync' }
+ 00000006-0000-0ff1-ce00-000000000000 { $return = 'Microsoft.Office365Portal' }
+ 00000003-0000-0ff1-ce00-000000000000 { $return = 'Microsoft.SharePoint ' }
+ 00000005-0000-0000-c000-000000000000 { $return = 'Microsoft.Azure.Workflow' }
+ 00000009-0000-0000-c000-000000000000 { $return = 'Microsoft.Azure.AnalysisServices' }
+ 00000002-0000-0ff1-ce00-000000000000 { $return = 'Microsoft.Exchange' }
+ 00000007-0000-0ff1-ce00-000000000000 { $return = 'Microsoft.ExchangeOnlineProtection' }
+ 00000002-0000-0000-c000-000000000000 { $return = 'Microsoft.Azure.ActiveDirectory' }
+ 8fca0a66-c008-4564-a876-ab3ae0fd5cff { $return = 'Microsoft.SMIT' }
+ 0000000b-0000-0000-c000-000000000000 { $return = 'Microsoft.SellerDashboard' }
+ 0000000f-0000-0000-c000-000000000000 { $return = 'Microsoft.Azure.GraphExplorer' }
+ 0000000c-0000-0000-c000-000000000000 { $return = 'Microsoft App Access Panel' }
+ 00000013-0000-0000-c000-000000000000 { $return = 'Microsoft.Azure.Portal' }
+ 00000010-0000-0000-c000-000000000000 { $return = 'Microsoft.Azure.GraphStore' }
+ 93ee9413-cf4c-4d4e-814b-a91ff20a01bd { $return = 'Workflow' }
+ aa9ecb1e-fd53-4aaa-a8fe-7a54de2c1334 { $return = 'Microsoft.Office365.Configure' }
+ 797f4846-ba00-4fd7-ba43-dac1f8f63013 { $return = 'Windows Azure Service Management API' }
+ 00000005-0000-0ff1-ce00-000000000000 { $return = 'Microsoft.YammerEnterprise' }
+ 601d4e27-7bb3-4dee-8199-90d47d527e1c { $return = 'Microsoft.Office365.ChangeManagement' }
+ 6f82282e-0070-4e78-bc23-e6320c5fa7de { $return = 'Microsoft.DiscoveryService' }
+ 0f698dd4-f011-4d23-a33e-b36416dcb1e6 { $return = 'Microsoft.OfficeClientService' }
+ 67e3df25-268a-4324-a550-0de1c7f97287 { $return = 'Microsoft.OfficeWebAppsService' }
+ ab27a73e-a3ba-4e43-8360-8bcc717114d8 { $return = 'Microsoft.OfficeModernCalendar' }
+ aedca418-a84d-430d-ab84-0b1ef06f318f { $return = 'Workflow' }
+ 595d87a1-277b-4c0a-aa7f-44f8a068eafc { $return = 'Microsoft.SupportTicketSubmission' }
+ e3583ad2-c781-4224-9b91-ad15a8179ba0 { $return = 'Microsoft.ExtensibleRealUserMonitoring' }
+ b645896d-566e-447e-8f7f-e2e663b5d182 { $return = 'OpsDashSharePointApp' }
+ 48229a4a-9f1d-413a-8b96-4c02462c0360 { $return = 'OpsDashSharePointApp' }
+ 48717084-a59c-4306-9dc4-3f618dbecdf9 { $return = '"Napa" Office 365 Development Tools' }
+ c859ff33-eb41-4ba6-8093-a2c5153bbd7c { $return = 'Workflow' }
+ 67cad61c-3411-48d7-ab73-561c64f11ed6 { $return = 'Workflow' }
+ 914ed757-9257-4200-b68e-a2bed2f12c5a { $return = 'RbacBackfill' }
+ 499b84ac-1321-427f-aa17-267ca6975798 { $return = 'Microsoft.VisualStudio.Online' }
+ b2590339-0887-4e94-93aa-13357eb510d7 { $return = 'Workflow' }
+ 0000001b-0000-0000-c000-000000000000 { $return = 'Microsoft Power BI Information Service' }
+ 89f80565-bfac-4c01-9535-9f0eba332ffe { $return = 'Power BI' }
+ 433895fb-4ec7-45c3-a53c-c44d10f80d5b { $return = 'Compromised Account Service' }
+ d7c17728-4f1e-4a1e-86cf-7e0adf3fe903 { $return = 'Workflow' }
+ 17ef6d31-381f-4783-b186-7b440a3c85c1 { $return = 'Workflow' }
+ 00000012-0000-0000-c000-000000000000 { $return = 'Microsoft.Azure.RMS' }
+ 81ce94d4-9422-4c0d-a4b9-3250659366ce { $return = 'Workflow' }
+ 8d3a7d3c-c034-4f19-a2ef-8412952a9671 { $return = 'MicrosoftOffice' }
+ 0469d4cd-df37-4d93-8a61-f8c75b809164 { $return = 'Microsoft Policy Administration Service' }
+ 31d3f3f5-7267-45a8-9549-affb00110054 { $return = 'Windows Azure RemoteApp Service' }
+ 4e004241-32db-46c2-a86f-aaaba29bea9c { $return = 'Workflow' }
+ 748d098e-7a3b-436d-8b0a-006a58b29647 { $return = 'Workflow' }
+ dbf08535-1d3b-4f89-bf54-1d48dd613a61 { $return = 'Workflow' }
+ ed9fe1ef-25a4-482f-9981-2b60f91e2448 { $return = 'Workflow' }
+ 8ad28d50-ee26-42fc-8a29-e41ea38461f2 { $return = 'Office365RESTAPIExplorer.Office365App' }
+ 38285dce-a13d-4107-9b04-3016b941bb3a { $return = 'BasicDataOperationsREST' }
+ 92bb96c8-321c-47f9-bcc5-8849490c2b07 { $return = 'BasicSelfHostedAppREST' }
+ 488a57a0-00e2-4817-8c8d-cf8a15a994d2 { $return = 'WindowsFormsApplication2.Office365App' }
+ 11c174dc-1945-4a9a-a36b-c79a0f246b9b { $return = 'AzureApplicationInsights' }
+ e6acb561-0d94-4287-bd3a-3169f421b112 { $return = 'Tutum' }
+ 7b77b3a2-8490-49e1-8842-207cd0899af9 { $return = 'Nearpod' }
+ 0000000a-0000-0000-c000-000000000000 { $return = 'Microsoft.Intune' }
+ 93625bc8-bfe2-437a-97e0-3d0060024faa { $return = 'SelfServicePasswordReset' }
+ dee7ba80-6a55-4f3b-a86c-746a9231ae49 { $return = 'MicrosoftAppPlatEMA' }
+ 803ee9ca-3f7f-4824-bd6e-0b99d720c35c { $return = 'Azure Media Service' }
+ 2d4d3d8e-2be3-4bef-9f87-7875a61c29de { $return = 'OneNote' }
+ 8d40666e-5abf-45f6-a5e7-b7192d6d56ed { $return = 'Workflow' }
+ 262044b1-e2ce-469f-a196-69ab7ada62d3 { $return = 'Backup Management Service' }
+ 087a2c70-c89e-463f-8dd3-e3959eabb1a9 { $return = 'Microsoft Profile Service Platform Service' }
+ 7cd684f4-8a78-49b0-91ec-6a35d38739ba { $return = 'Azure Logic Apps' }
+ c5393580-f805-4401-95e8-94b7a6ef2fc2 { $return = 'Office 365 Management APIs' }
+ 96231a05-34ce-4eb4-aa6a-70759cbb5e83 { $return = 'MicrosoftAzureRedisCache' }
+ b8340c3b-9267-498f-b21a-15d5547fd85e { $return = 'Hyper-V Recovery Manager' }
+ abfa0a7c-a6b6-4736-8310-5855508787cd { $return = 'Microsoft.Azure.WebSites' }
+ c44b4083-3bb0-49c1-b47d-974e53cbdf3c { $return = 'IbizaPortal' }
+ 905fcf26-4eb7-48a0-9ff0-8dcc7194b5ba { $return = 'Sway' }
+ b10686fd-6ba8-49f2-a3cd-67e4d2f52ac8 { $return = 'NovoEd' }
+ c606301c-f764-4e6b-aa45-7caaaea93c9a { $return = 'OfficeStore' }
+ 569e8598-685b-4ba2-8bff-5bced483ac46 { $return = 'Evercontact' }
+ 20a23a2f-8c32-4de7-8063-8c8f909602c0 { $return = 'Workflow' }
+ aaf214cc-8013-4b95-975f-13203ae36039 { $return = 'Power BI Tiles' }
+ d88a361a-d488-4271-a13f-a83df7dd99c2 { $return = 'IDML Graph Resolver Service and CAD' }
+ dff9b531-6290-4620-afce-26826a62a4e7 { $return = 'DocuSign' }
+ 01cb2876-7ebd-4aa4-9cc9-d28bd4d359a9 { $return = 'Device Registration Service' }
+ 3290e3f7-d3ac-4165-bcef-cf4874fc4270 { $return = 'Smartsheet' }
+ a4ee6867-8640-4495-b1fd-8b26037a5bd3 { $return = 'Workflow' }
+ aa0e3dd4-df02-478d-869e-fc61dd71b6e8 { $return = 'Workflow' }
+ 0f6edad5-48f2-4585-a609-d252b1c52770 { $return = 'AIGraphClient' }
+ 0c8139b5-d545-4448-8d2b-2121bb242680 { $return = 'BillingExtension' }
+ 475226c6-020e-4fb2-8a90-7a972cbfc1d4 { $return = 'KratosAppsService' }
+ 39624784-6cbe-4a60-afbe-9f46d10fdb27 { $return = 'SkypeForBusinessRemotePowershell' }
+ 8bdebf23-c0fe-4187-a378-717ad86f6a53 { $return = 'ResourceHealthRP' }
+ c161e42e-d4df-4a3d-9b42-e7a3c31f59d4 { $return = 'MicrosoftIntuneAPI' }
+ 9cb77803-d937-493e-9a3b-4b49de3f5a74 { $return = 'MicrosoftIntuneServiceDiscovery' }
+ ddbf3205-c6bd-46ae-8127-60eb93363864 { $return = 'Microsoft Azure Batch' }
+ 80ccca67-54bd-44ab-8625-4b79c4dc7775 { $return = 'ComplianceCenter' }
+ 0a5f63c0-b750-4f38-a71c-4fc0d58b89e2 { $return = 'Microsoft Mobile Application Management' }
+ e1335bb1-2aec-4f92-8140-0e6e61ae77e5 { $return = 'CIWebService' }
+ 75018fbe-21fe-4a57-b63c-83252b5eaf16 { $return = 'TeamImprover - Team Organization Chart' }
+ a393296b-5695-4463-97cb-9fa8638a494a { $return = 'My SharePoint Sites' }
+ fe217466-5583-431c-9531-14ff7268b7b3 { $return = 'Microsoft Education' }
+ 5bfe8a29-054e-4348-9e7a-3981b26b125f { $return = 'Bing Places for Business' }
+ eaf8a961-f56e-47eb-9ffd-936e22a554ef { $return = 'DevilFish' }
+ 4b4b1d56-1f03-47d9-a0a3-87d4afc913c9 { $return = 'Wunderlist' }
+ 00000003-0000-0000-c000-000000000000 { $return = 'Microsoft Graph' }
+ 60e6cd67-9c8c-4951-9b3c-23c25a2169af { $return = 'Compute Resource Provider' }
+ 507bc9da-c4e2-40cb-96a7-ac90df92685c { $return = 'Office365Reports' }
+ 09abbdfd-ed23-44ee-a2d9-a627aa1c90f3 { $return = 'ProjectWorkManagement' }
+ 28ec9756-deaf-48b2-84d5-a623b99af263 { $return = 'Office Personal Assistant at Work Service' }
+ 9e4a5442-a5c9-4f6f-b03f-5b9fcaaf24b1 { $return = 'OfficeServicesManager' }
+ 3138fe80-4087-4b04-80a6-8866c738028a { $return = 'SharePoint Notification Service' }
+ d2a0a418-0aac-4541-82b2-b3142c89da77 { $return = 'MicrosoftAzureOperationalInsights' }
+ 2cf9eb86-36b5-49dc-86ae-9a63135dfa8c { $return = 'AzureTrafficManagerandDNS' }
+ 32613fc5-e7ac-4894-ac94-fbc39c9f3e4a { $return = 'OAuth Sandbox' }
+ 925eb0d0-da50-4604-a19f-bd8de9147958 { $return = 'Groupies Web Service' }
+ e4ab13ed-33cb-41b4-9140-6e264582cf85 { $return = 'Azure SQL Database Backup To Azure Backup Vault' }
+ ad230543-afbe-4bb4-ac4f-d94d101704f8 { $return = 'Apiary for Power BI' }
+ 11cd3e2e-fccb-42ad-ad00-878b93575e07 { $return = 'Automated Call Distribution' }
+ de17788e-c765-4d31-aba4-fb837cfff174 { $return = 'Skype for Business Management Reporting and Analytics' }
+ 65d91a3d-ab74-42e6-8a2f-0add61688c74 { $return = 'Microsoft Approval Management' }
+ 5225545c-3ebd-400f-b668-c8d78550d776 { $return = 'Office Agent Service' }
+ 1cda9b54-9852-4a5a-96d4-c2ab174f9edf { $return = 'O365Account' }
+ 4747d38e-36c5-4bc3-979b-b0ef74df54d1 { $return = 'PushChannel' }
+ b97b6bd4-a49f-4a0c-af18-af507d1da76c { $return = 'Office Shredding Service' }
+ d4ebce55-015a-49b5-a083-c84d1797ae8c { $return = 'Microsoft Intune Enrollment' }
+ 5b20c633-9a48-4a5f-95f6-dae91879051f { $return = 'Azure Information Protection' }
+ 441509e5-a165-4363-8ee7-bcf0b7d26739 { $return = 'EnterpriseAgentPlatform' }
+ e691bce4-6612-4025-b94c-81372a99f77e { $return = 'Boomerang' }
+ 8edd93e1-2103-40b4-bd70-6e34e586362d { $return = 'Windows Azure Security Resource Provider' }
+ 94c63fef-13a3-47bc-8074-75af8c65887a { $return = 'Office Delve' }
+ e95d8bee-4725-4f59-910d-94d415da51b9 { $return = 'Skype for Business Name Dictionary Service' }
+ e3c5dbcd-bb5f-4bda-b943-adc7a5bbc65e { $return = 'Workflow' }
+ 8602e328-9b72-4f2d-a4ae-1387d013a2b3 { $return = 'Azure API Management' }
+ 8b3391f4-af01-4ee8-b4ea-9871b2499735 { $return = 'O365 Secure Score' }
+ c26550d6-bc82-4484-82ca-ac1c75308ca3 { $return = 'Office 365 YammerOnOls' }
+ 33be1cef-03fb-444b-8fd3-08ca1b4d803f { $return = 'OneDrive Web' }
+ dcad865d-9257-4521-ad4d-bae3e137b345 { $return = 'Microsoft SharePoint Online - SharePoint Home' }
+ b2cc270f-563e-4d8a-af47-f00963a71dcd { $return = 'OneProfile Service' }
+ 4660504c-45b3-4674-a709-71951a6b0763 { $return = 'Microsoft Invitation Acceptance Portal' }
+ ba23cd2a-306c-48f2-9d62-d3ecd372dfe4 { $return = 'OfficeGraph' }
+ d52485ee-4609-4f6b-b3a3-68b6f841fa23 { $return = 'On-Premises Data Gateway Connector' }
+ 996def3d-b36c-4153-8607-a6fd3c01b89f { $return = 'Dynamics 365 for Financials' }
+ b6b84568-6c01-4981-a80f-09da9a20bbed { $return = 'Microsoft Invoicing' }
+ 9d3e55ba-79e0-4b7c-af50-dc460b81dca1 { $return = 'Microsoft Azure Data Catalog' }
+ 4345a7b9-9a63-4910-a426-35363201d503 { $return = 'O365 Suite UX' }
+ ac815d4a-573b-4174-b38e-46490d19f894 { $return = 'Workflow' }
+ bb8f18b0-9c38-48c9-a847-e1ef3af0602d { $return = 'Microsoft.Azure.ActiveDirectoryIUX' }
+ cc15fd57-2c6c-4117-a88c-83b1d56b4bbe { $return = 'Microsoft Teams Services' }
+ 5e3ce6c0-2b1f-4285-8d4b-75ee78787346 { $return = 'Skype Teams' }
+ 1fec8e78-bce4-4aaf-ab1b-5451cc387264 { $return = 'Microsoft Teams' }
+ 6d32b7f8-782e-43e0-ac47-aaad9f4eb839 { $return = 'Permission Service O365' }
+ cdccd920-384b-4a25-897d-75161a4b74c1 { $return = 'Skype Teams Firehose' }
+ 1c0ae35a-e2ec-4592-8e08-c40884656fa5 { $return = 'Skype Team Substrate connector' }
+ cf6c77f8-914f-4078-baef-e39a5181158b { $return = 'Skype Teams Settings Store' }
+ 64f79cb9-9c82-4199-b85b-77e35b7dcbcb { $return = 'Microsoft Teams Bots' }
+ b7912db9-aa33-4820-9d4f-709830fdd78f { $return = 'ConnectionsService' }
+ 82f77645-8a66-4745-bcdf-9706824f9ad0 { $return = 'PowerApps Runtime Service' }
+ 6204c1d1-4712-4c46-a7d9-3ed63d992682 { $return = 'Microsoft Flow Portal' }
+ 7df0a125-d3be-4c96-aa54-591f83ff541c { $return = 'Microsoft Flow Service' }
+ 331cc017-5973-4173-b270-f0042fddfd75 { $return = 'PowerAppsService' }
+ 0a0e9e37-25e3-47d4-964c-5b8237cad19a { $return = 'CloudSponge' }
+ df09ff61-2178-45d8-888c-4210c1c7b0b2 { $return = 'O365 UAP Processor' }
+ 8338dec2-e1b3-48f7-8438-20c30a534458 { $return = 'ViewPoint' }
+ 00000001-0000-0000-c000-000000000000 { $return = 'Azure ESTS Service' }
+ 394866fc-eedb-4f01-8536-3ff84b16be2a { $return = 'Microsoft People Cards Service' }
+ 0a0a29f9-0a25-49c7-94bf-c53c3f8fa69d { $return = 'Cortana Experience with O365' }
+ bb2a2e3a-c5e7-4f0a-88e0-8e01fd3fc1f4 { $return = 'CPIM Service' }
+ 0004c632-673b-4105-9bb6-f3bbd2a927fe { $return = 'PowerApps and Flow' }
+ d3ce4cf8-6810-442d-b42e-375e14710095 { $return = 'Graph Explorer' }
+ 3aa5c166-136f-40eb-9066-33ac63099211 { $return = 'O365 Customer Monitoring' }
+ d6fdaa33-e821-4211-83d0-cf74736489e1 { $return = 'Microsoft Service Trust' }
+ ef4a2a24-4b4e-4abf-93ba-cc11c5bd442c { $return = 'Edmodo' }
+ b692184e-b47f-4706-b352-84b288d2d9ee { $return = 'Microsoft.MileIQ.RESTService' }
+ a25dbca8-4e60-48e5-80a2-0664fdb5c9b6 { $return = 'Microsoft.MileIQ' }
+ f7069a8d-9edc-4300-b365-ae53c9627fc4 { $return = 'Microsoft.MileIQ.Dashboard' }
+ 02e3ae74-c151-4bda-b8f0-55fbf341de08 { $return = 'Application Registration Portal' }
+ 1f5530b3-261a-47a9-b357-ded261e17918 { $return = 'Azure Multi-Factor Auth Connector' }
+ 981f26a1-7f43-403b-a875-f8b09b8cd720 { $return = 'Azure Multi-Factor Auth Client' }
+ 6ea8091b-151d-447a-9013-6845b83ba57b { $return = 'AD Hybrid Health' }
+ fc68d9e5-1f76-45ef-99aa-214805418498 { $return = 'Azure AD Identity Protection' }
+ 01fc33a7-78ba-4d2f-a4b7-768e336e890e { $return = 'MS-PIM' }
+ a6aa9161-5291-40bb-8c5c-923b567bee3b { $return = 'Storage Resource Provider' }
+ 4e9b8b9a-1001-4017-8dd1-6e8f25e19d13 { $return = 'Adobe Acrobat' }
+ 159b90bb-bb28-4568-ad7c-adad6b814a2f { $return = 'LastPass' }
+ b4bddae8-ab25-483e-8670-df09b9f1d0ea { $return = 'Signup' }
+ aa580612-c342-4ace-9055-8edee43ccb89 { $return = 'Microsoft StaffHub' }
+ 51133ff5-8e0d-4078-bcca-84fb7f905b64 { $return = 'Microsoft Teams Mailhook' }
+ ab3be6b7-f5df-413d-ac2d-abf1e3fd9c0b { $return = 'Microsoft Teams Graph Service' }
+ b1379a75-ce5e-4fa3-80c6-89bb39bf646c { $return = 'Microsoft Teams Chat Aggregator' }
+ 48af08dc-f6d2-435f-b2a7-069abd99c086 { $return = 'Connectors' }
+ d676e816-a17b-416b-ac1a-05ad96f43686 { $return = 'Workflow' }
+ cfa8b339-82a2-471a-a3c9-0fc0be7a4093 { $return = 'Azure Key Vault' }
+ c2f89f53-3971-4e09-8656-18eed74aee10 { $return = 'calendly' }
+ 6da466b6-1d13-4a2c-97bd-51a99e8d4d74 { $return = 'Exchange Office Graph Client for AAD - Interactive' }
+ 0eda3b13-ddc9-4c25-b7dd-2f6ea073d6b7 { $return = 'Microsoft Flow CDS Integration Service' }
+ eacba838-453c-4d3e-8c6a-eb815d3469a3 { $return = 'Microsoft Flow CDS Integration Service TIP1' }
+ 4ac7d521-0382-477b-b0f8-7e1d95f85ca2 { $return = 'SQL Server Analysis Services Azure' }
+ b4114287-89e4-4209-bd99-b7d4919bcf64 { $return = 'OfficeDelve' }
+ 4580fd1d-e5a3-4f56-9ad1-aab0e3bf8f76 { $return = 'Call Recorder' }
+ a855a166-fd92-4c76-b60d-a791e0762432 { $return = 'Microsoft Teams VSTS' }
+ c37c294f-eec8-47d2-b3e2-fc3daa8f77d3 { $return = 'Workflow' }
+ fc75330b-179d-49af-87dd-3b1acf6827fa { $return = 'AzureAutomationAADPatchS2S' }
+ 766d89a4-d6a6-444d-8a5e-e1a18622288a { $return = 'OneDrive' }
+ f16c4a38-5aff-4549-8199-ee7d3c5bd8dc { $return = 'Workflow' }
+ 4c4f550b-42b2-4a16-93f9-fdb9e01bb6ed { $return = 'Targeted Messaging Service' }
+ 765fe668-04e7-42ba-aec0-2c96f1d8b652 { $return = 'Exchange Office Graph Client for AAD - Noninteractive' }
+ 0130cc9f-7ac5-4026-bd5f-80a08a54e6d9 { $return = 'Azure Data Warehouse Polybase' }
+ a1cf9e0a-fe14-487c-beb9-dd3360921173 { $return = 'Meetup' }
+ 76cd24bf-a9fc-4344-b1dc-908275de6d6d { $return = 'Azure SQL Virtual Network to Network Resource Provider' }
+ 9f505dbd-a32c-4685-b1c6-72e4ef704cb0 { $return = 'Amazon Alexa' }
+ 1e2ca66a-c176-45ea-a877-e87f7231e0ee { $return = 'Microsoft B2B Admin Worker' }
+ 2634dd23-5e5a-431c-81ca-11710d9079f4 { $return = 'Microsoft Stream Service' }
+ cf53fce8-def6-4aeb-8d30-b158e7b1cf83 { $return = 'Microsoft Stream Portal' }
+ c9a559d2-7aab-4f13-a6ed-e7e9c52aec87 { $return = 'Microsoft Forms' }
+ 978877ea-b2d6-458b-80c7-05df932f3723 { $return = 'Microsoft Teams AuditService' }
+ dbc36ae1-c097-4df9-8d94-343c3d091a76 { $return = 'Service Encryption' }
+ fa7ff576-8e31-4a58-a5e5-780c1cd57caa { $return = 'OneNote' }
+ cb4dc29f-0bf4-402a-8b30-7511498ed654 { $return = 'Power BI Premium' }
+ f5aeb603-2a64-4f37-b9a8-b544f3542865 { $return = 'Microsoft Teams RetentionHook Service' }
+ da109bdd-abda-4c06-8808-4655199420f8 { $return = 'Glip Contacts' }
+ 76c7f279-7959-468f-8943-3954880e0d8c { $return = 'Azure SQL Managed Instance to Microsoft.Network' }
+ 3a9ddf38-83f3-4ea1-a33a-ecf934644e2d { $return = 'Protected Message Viewer' }
+ 5635d99c-c364-4411-90eb-764a511b5fdf { $return = 'Responsive Banner Slider' }
+ a43e5392-f48b-46a4-a0f1-098b5eeb4757 { $return = 'Cloudsponge' }
+ d73f4b35-55c9-48c7-8b10-651f6f2acb2e { $return = 'MCAPI Authorization Prod' }
+ 166f1b03-5b19-416f-a94b-1d7aa2d247dc { $return = 'Office Hive' }
+ b815ce1c-748f-4b1e-9270-a42c1fa4485a { $return = 'Workflow' }
+ bd7b778b-4aa8-4cde-8d90-8aeb821c0bd2 { $return = 'Workflow' }
+ 9d06afd9-66c9-49a6-b385-ea7509332b0b { $return = 'O365SBRM Service' }
+ 9ea1ad79-fdb6-4f9a-8bc3-2b70f96e34c7 { $return = 'Bing' }
+ 57fb890c-0dab-4253-a5e0-7188c88b2bb4 { $return = 'SharePoint Online Client' }
+ 45c10911-200f-4e27-a666-9e9fca147395 { $return = 'drawio' }
+ b73f62d0-210b-4396-a4c5-ea50c4fab79b { $return = 'Skype Business Voice Fraud Detection and Prevention' }
+ bc59ab01-8403-45c6-8796-ac3ef710b3e3 { $return = 'Outlook Online Add-in App' }
+ 035f9e1d-4f00-4419-bf50-bf2d87eb4878 { $return = 'Azure Monitor Restricted' }
+ 7c33bfcb-8d33-48d6-8e60-dc6404003489 { $return = 'Network Watcher' }
+ a0be0c72-870e-46f0-9c49-c98333a996f7 { $return = 'AzureDnsFrontendApp' }
+ 1e3e4475-288f-4018-a376-df66fd7fac5f { $return = 'NetworkTrafficAnalyticsService' }
+ 7557eb47-c689-4224-abcf-aef9bd7573df { $return = 'Skype for Business' }
+ c39c9bac-9d1f-4dfb-aa29-27f6365e5cb7 { $return = 'Azure Advisor' }
+ 2087bd82-7206-4c0a-b305-1321a39e5926 { $return = 'Microsoft To-Do' }
+ f8d98a96-0999-43f5-8af3-69971c7bb423 { $return = 'iOS Accounts' }
+ c27373d3-335f-4b45-8af9-fe81c240d377 { $return = 'P2P Server' }
+ 5c2ffddc-f1d7-4dc3-926e-3c1bd98e32bd { $return = 'RITS Dev' }
+ 982bda36-4632-4165-a46a-9863b1bbcf7d { $return = 'O365 Demeter' }
+ 98c8388a-4e86-424f-a176-d1288462816f { $return = 'OfficeFeedProcessors' }
+ bf9fc203-c1ff-4fd4-878b-323642e462ec { $return = 'Jarvis Transaction Service' }
+ 257601fd-462f-4a21-b623-7f719f0f90f4 { $return = 'Centralized Deployment' }
+ 2a486b53-dbd2-49c0-a2bc-278bdfc30833 { $return = 'Cortana at Work Service' }
+ 22d7579f-06c2-4baa-89d2-e844486adb9d { $return = 'Cortana at Work Bing Services' }
+ 4c8f074c-e32b-4ba7-b072-0f39d71daf51 { $return = 'IPSubstrate' }
+ a164aee5-7d0a-46bb-9404-37421d58bdf7 { $return = 'Microsoft Teams AuthSvc' }
+ 354b5b6d-abd6-4736-9f51-1be80049b91f { $return = 'Microsoft Mobile Application Management Backend' }
+ 82b293b2-d54d-4d59-9a95-39c1c97954a7 { $return = 'Tasks in a Box' }
+ fdc83783-b652-4258-a622-66bc85f1a871 { $return = 'FedExPackageTracking' }
+ d0597157-f0ae-4e23-b06c-9e65de434c4f { $return = 'Microsoft Teams Task Service' }
+ f5c26e74-f226-4ae8-85f0-b4af0080ac9e { $return = 'Application Insights API' }
+ 57c0fc58-a83a-41d0-8ae9-08952659bdfd { $return = 'Azure Cosmos DB Virtual Network To Network Resource Provider' }
+ 744e50be-c4ff-4e90-8061-cd7f1fabac0b { $return = 'LinkedIn Microsoft Graph Connector' }
+ 823dfde0-1b9a-415a-a35a-1ad34e16dd44 { $return = 'Microsoft Teams Wiki Images Migration' }
+ 3ab9b3bc-762f-4d62-82f7-7e1d653ce29f { $return = 'Microsoft Volume Licensing' }
+ 44eb7794-0e11-42b6-800b-dc31874f9f60 { $return = 'Alignable' }
+ c58637bb-e2e1-4312-8a00-04b5ffcd3403 { $return = 'SharePoint Online Client Extensibility' }
+ 62b732f7-fc71-40bc-b27d-35efcb0509de { $return = 'Microsoft Teams AadSync' }
+ 07978fee-621a-42df-82bb-3eabc6511c26 { $return = 'SurveyMonkey' }
+ 47ee738b-3f1a-4fc7-ab11-37e4822b007e { $return = 'Azure AD Application Proxy' }
+ 00000007-0000-0000-c000-000000000000 { $return = 'Dynamics CRM Online' }
+ 913c6de4-2a4a-4a61-a9ce-945d2b2ce2e0 { $return = 'Dynamics Lifecycle services' }
+ f217ad13-46b8-4c5b-b661-876ccdf37302 { $return = 'Attach OneDrive files to Asana' }
+ 00000008-0000-0000-c000-000000000000 { $return = 'Microsoft.Azure.DataMarket' }
+ 9b06ebd4-9068-486b-bdd2-dac26b8a5a7a { $return = 'Microsoft.DynamicsMarketing' }
+ e8ab36af-d4be-4833-a38b-4d6cf1cfd525 { $return = 'Microsoft Social Engagement' }
+ 8909aac3-be91-470c-8a0b-ff09d669af91 { $return = 'Microsoft Parature Dynamics CRM' }
+ 71234da4-b92f-429d-b8ec-6e62652e50d7 { $return = 'Microsoft Customer Engagement Portal' }
+ b861dbcc-a7ef-4219-a005-0e4de4ea7dcf { $return = 'Data Export Service for Microsoft Dynamics 365' }
+ 2db8cb1d-fb6c-450b-ab09-49b6ae35186b { $return = 'Microsoft Dynamics CRM Learning Path' }
+ 2e49aa60-1bd3-43b6-8ab6-03ada3d9f08b { $return = 'Dynamics Data Integration' }
+ }
+
+ if ([string]::IsNullOrEmpty($return)) {
+ $return = $Applications | ? { $_.Appid -eq $ID } | Select -ExpandProperty DisplayName
+ }
+
+ if ([string]::IsNullOrEmpty($return)) {
+ $return = $Applications | ? { $_.ID -eq $ID } | Select -ExpandProperty DisplayName
+ }
+
+ if ([string]::IsNullOrEmpty($return)) {
+ $return = ''
+ }
+
+ return $return
+}
+
+# Write to the Azure Functions log stream.
+Write-Host "PowerShell HTTP trigger function processed a request."
+
+# Interact with query parameters or the body of the request.
+$TenantFilter = $Request.Query.TenantFilter
+$UserID = $Request.Query.UserID
+
+
+$json = '{"conditions":{"users":{"allUsers":2,"included":{"userIds":["' + $UserID + '"],"groupIds":[]},"excluded":{"userIds":[],"groupIds":[]}},"servicePrincipals":{"allServicePrincipals":1,"includeAllMicrosoftApps":false,"excludeAllMicrosoftApps":false,"userActions":[],"stepUpTags":[]},"conditions":{"minUserRisk":{"noRisk":false,"lowRisk":false,"mediumRisk":false,"highRisk":false,"applyCondition":false},"minSigninRisk":{"noRisk":false,"lowRisk":false,"mediumRisk":false,"highRisk":false,"applyCondition":false},"servicePrincipalRiskLevels":{"noRisk":false,"lowRisk":false,"mediumRisk":false,"highRisk":false,"applyCondition":false},"devicePlatforms":{"all":2,"included":{"android":false,"ios":false,"windowsPhone":false,"windows":false,"macOs":false,"linux":false},"excluded":null,"applyCondition":false},"locations":{"applyCondition":true,"includeLocationType":2,"excludeAllTrusted":false},"clientApps":{"applyCondition":false,"specificClientApps":false,"webBrowsers":false,"exchangeActiveSync":false,"onlyAllowSupportedPlatforms":false,"mobileDesktop":false},"clientAppsV2":{"applyCondition":false,"webBrowsers":false,"mobileDesktop":false,"modernAuth":false,"exchangeActiveSync":false,"onlyAllowSupportedPlatforms":false,"otherClients":false},"deviceState":{"includeDeviceStateType":1,"excludeDomainJoionedDevice":false,"excludeCompliantDevice":false,"applyCondition":true}}},"country":"","device":{}}'
+$UserPolicies = (New-ClassicAPIPostRequest -uri "https://main.iam.ad.ext.azure.com/api/Policies/Evaluate?" -tenantid $tenantfilter -Method POST -body $json -resource '74658136-14ec-4630-ad9b-26e160ff0fc6' -verbose | where-object { $_.applied -eq $true })
+
+
+
+
+$ConditionalAccessPolicyOutput = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/identity/conditionalAccess/policies" -tenantid $tenantfilter
+$AllNamedLocations = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/identity/conditionalAccess/namedLocations" -tenantid $tenantfilter
+$AllApplications = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/applications" -tenantid $tenantfilter
+$AllRoleDefinitions = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/roleManagement/directory/roleDefinitions" -tenantid $tenantfilter
+$GroupListOutput = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/groups" -tenantid $tenantfilter
+$UserListOutput = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/users" -tenantid $tenantfilter | Select-Object * -ExcludeProperty *extensionAttribute*
+
+$GraphRequest = foreach ($cap in $ConditionalAccessPolicyOutput) {
+ if ($cap.id -in $UserPolicies.policyId) {
+ $temp = [PSCustomObject]@{
+ id = $cap.id
+ displayName = $cap.displayName
+ customer = $cap.Customer
+ tenantID = $cap.TenantID
+ createdDateTime = $(if (![string]::IsNullOrEmpty($cap.createdDateTime)) { [datetime]$cap.createdDateTime | Get-Date -Format "yyyy-MM-dd HH:mm" }else { "" })
+ modifiedDateTime = $(if (![string]::IsNullOrEmpty($cap.modifiedDateTime)) { [datetime]$cap.modifiedDateTime | Get-Date -Format "yyyy-MM-dd HH:mm" }else { "" })
+ state = $cap.state
+ clientAppTypes = ($cap.conditions.clientAppTypes) -join ","
+ includePlatforms = ($cap.conditions.platforms.includePlatforms) -join ","
+ excludePlatforms = ($cap.conditions.platforms.excludePlatforms) -join ","
+ includeLocations = (Get-LocationNameFromId -Locations $AllNamedLocations -id $cap.conditions.locations.includeLocations) -join ","
+ excludeLocations = (Get-LocationNameFromId -Locations $AllNamedLocations -id $cap.conditions.locations.excludeLocations) -join ","
+ includeApplications = ($cap.conditions.applications.includeApplications | % { Get-ApplicationNameFromId -Applications $AllApplications -id $_ }) -join ","
+ excludeApplications = ($cap.conditions.applications.excludeApplications | % { Get-ApplicationNameFromId -Applications $AllApplications -id $_ }) -join ","
+ includeUserActions = ($cap.conditions.applications.includeUserActions | out-string)
+ includeAuthenticationContextClassReferences = ($cap.conditions.applications.includeAuthenticationContextClassReferences | out-string)
+ includeUsers = ($cap.conditions.users.includeUsers | % { Get-UserNameFromId -Users $UserListOutput -id $_ }) | Out-String
+ excludeUsers = ($cap.conditions.users.excludeUsers | % { Get-UserNameFromId -Users $UserListOutput -id $_ }) | Out-String
+ includeGroups = ($cap.conditions.users.includeGroups | % { Get-GroupNameFromId -Groups $GroupListOutput -id $_ }) | Out-String
+ excludeGroups = ($cap.conditions.users.excludeGroups | % { Get-GroupNameFromId -Groups $GroupListOutput -id $_ }) | Out-String
+ includeRoles = ($cap.conditions.users.includeRoles | % { Get-RoleNameFromId -RoleDefinitions $AllRoleDefinitions -id $_ }) | Out-String
+ excludeRoles = ($cap.conditions.users.excludeRoles | % { Get-RoleNameFromId -RoleDefinitions $AllRoleDefinitions -id $_ }) | Out-String
+ grantControlsOperator = ($cap.grantControls.operator) -join ","
+ builtInControls = ($cap.grantControls.builtInControls) -join ","
+ customAuthenticationFactors = ($cap.grantControls.customAuthenticationFactors) -join ","
+ termsOfUse = ($cap.grantControls.termsOfUse) -join ","
+ }
+ $temp
+ }
+}
+
+Write-Host $GraphRequest
+
+# Associate values to output bindings by calling 'Push-OutputBinding'.
+Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
+ StatusCode = [HttpStatusCode]::OK
+ Body = @($GraphRequest)
+ })
diff --git a/ListUserDevices/function.json b/ListUserDevices/function.json
new file mode 100644
index 000000000000..306b0c51e560
--- /dev/null
+++ b/ListUserDevices/function.json
@@ -0,0 +1,19 @@
+{
+ "bindings": [
+ {
+ "authLevel": "anonymous",
+ "type": "httpTrigger",
+ "direction": "in",
+ "name": "Request",
+ "methods": [
+ "get",
+ "post"
+ ]
+ },
+ {
+ "type": "http",
+ "direction": "out",
+ "name": "Response"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/ListUserDevices/run.ps1 b/ListUserDevices/run.ps1
new file mode 100644
index 000000000000..463457df9ae0
--- /dev/null
+++ b/ListUserDevices/run.ps1
@@ -0,0 +1,36 @@
+using namespace System.Net
+
+# Input bindings are passed in via param block.
+param($Request, $TriggerMetadata)
+
+$APIName = $TriggerMetadata.FunctionName
+Log-Request -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug"
+
+
+# Write to the Azure Functions log stream.
+Write-Host "PowerShell HTTP trigger function processed a request."
+
+# Interact with query parameters or the body of the request.
+$TenantFilter = $Request.Query.TenantFilter
+$UserID = $Request.Query.UserID
+$GraphRequest = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/users/$UserID/ownedDevices?`$top=999" -Tenantid $tenantfilter | Select-Object @{ Name = 'ID'; Expression = { $_.'id' } },
+@{ Name = 'accountEnabled'; Expression = { $_.'accountEnabled' } },
+@{ Name = 'approximateLastSignInDateTime'; Expression = { $_.'approximateLastSignInDateTime' | Out-String } },
+@{ Name = 'createdDateTime'; Expression = { $_.'createdDateTime' | Out-String } },
+@{ Name = 'deviceOwnership'; Expression = { $_.'deviceOwnership' } },
+@{ Name = 'displayName'; Expression = { $_.'displayName' } },
+@{ Name = 'enrollmentType'; Expression = { $_.'enrollmentType' } },
+@{ Name = 'isCompliant'; Expression = { $_.'isCompliant' } },
+@{ Name = 'managementType'; Expression = { $_.'managementType' } },
+@{ Name = 'manufacturer'; Expression = { $_.'manufacturer' } },
+@{ Name = 'model'; Expression = { $_.'model' } },
+@{ Name = 'operatingSystem'; Expression = { $_.'operatingSystem' } },
+@{ Name = 'onPremisesSyncEnabled'; Expression = { $(if([string]::IsNullOrEmpty($_.'onPremisesSyncEnabled')){$false}else{$true}) } },
+@{ Name = 'operatingSystemVersion'; Expression = { $_.'operatingSystemVersion' } },
+@{ Name = 'trustType'; Expression = { $_.'trustType' } }
+
+# Associate values to output bindings by calling 'Push-OutputBinding'.
+Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
+ StatusCode = [HttpStatusCode]::OK
+ Body = @($GraphRequest)
+ })
diff --git a/ListUserSigninLogs/function.json b/ListUserSigninLogs/function.json
new file mode 100644
index 000000000000..306b0c51e560
--- /dev/null
+++ b/ListUserSigninLogs/function.json
@@ -0,0 +1,19 @@
+{
+ "bindings": [
+ {
+ "authLevel": "anonymous",
+ "type": "httpTrigger",
+ "direction": "in",
+ "name": "Request",
+ "methods": [
+ "get",
+ "post"
+ ]
+ },
+ {
+ "type": "http",
+ "direction": "out",
+ "name": "Response"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/ListUserSigninLogs/run.ps1 b/ListUserSigninLogs/run.ps1
new file mode 100644
index 000000000000..44844865e34c
--- /dev/null
+++ b/ListUserSigninLogs/run.ps1
@@ -0,0 +1,38 @@
+using namespace System.Net
+
+# Input bindings are passed in via param block.
+param($Request, $TriggerMetadata)
+
+$APIName = $TriggerMetadata.FunctionName
+Log-Request -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug"
+
+
+# Write to the Azure Functions log stream.
+Write-Host "PowerShell HTTP trigger function processed a request."
+
+# Interact with query parameters or the body of the request.
+$TenantFilter = $Request.Query.TenantFilter
+$UserID = $Request.Query.UserID
+
+$StartTime = Get-Date (Get-Date).ToUniversalTime() -UFormat '+%Y-%m-%dT%H:%M:%S.000Z'
+$EndDate = (Get-Date).addDays(-1)
+$EndTime = Get-Date (Get-Date($EndDate)).ToUniversalTime() -UFormat '+%Y-%m-%dT%H:%M:%S.000Z'
+
+
+$URI = "https://graph.microsoft.com/beta/auditLogs/signIns?`$filter=(userId eq '$UserID')&`$top=50&`$orderby=createdDateTime desc"
+Write-Host $URI
+$GraphRequest = New-GraphGetRequest -uri $URI -tenantid $TenantFilter -verbose
+
+#$GraphRequest = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/reports/get$($type)Detail(period='D7')" -tenantid $TenantFilter | convertfrom-csv | select-object @{ Name = 'UPN'; Expression = { $_.'Owner Principal Name' } },
+#@{ Name = 'displayName'; Expression = { $_.'Owner Display Name' } },
+#@{ Name = 'LastActive'; Expression = { $_.'Last Activity Date' } },
+#@{ Name = 'FileCount'; Expression = { $_.'File Count' } },
+#@{ Name = 'UsedGB'; Expression = { [math]::round($_.'Storage Used (Byte)' /1GB,0) } },
+#@{ Name = 'URL'; Expression = { $_.'Site URL' } },
+#@{ Name = 'Allocated'; Expression = { $_.'Storage Allocated (Byte)' /1GB } }
+
+# Associate values to output bindings by calling 'Push-OutputBinding'.
+Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
+ StatusCode = [HttpStatusCode]::OK
+ Body = @($GraphRequest)
+ })
\ No newline at end of file
diff --git a/ListUsers/run.ps1 b/ListUsers/run.ps1
index b9047bb574c2..ea61e458ace4 100644
--- a/ListUsers/run.ps1
+++ b/ListUsers/run.ps1
@@ -6,7 +6,7 @@ param($Request, $TriggerMetadata)
$APIName = $TriggerMetadata.FunctionName
Log-Request -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug"
-$selectlist = "id", "accountEnabled", "businessPhones", "city", "createdDateTime", "companyName", "country", "department", "displayName", "faxNumber", "givenName", "isResourceAccount", "jobTitle", "mail", "mailNickname", "mobilePhone", "onPremisesDistinguishedName", "officeLocation", "onPremisesLastSyncDateTime", "otherMails", "postalCode", "preferredDataLocation", "preferredLanguage", "proxyAddresses", "showInAddressList", "state", "streetAddress", "surname", "usageLocation", "userPrincipalName", "userType", "assignedLicenses", "onPremisesSyncEnabled", "LicJoined", "Aliasses"
+$selectlist = "id", "accountEnabled", "businessPhones", "city", "createdDateTime", "companyName", "country", "department", "displayName", "faxNumber", "givenName", "isResourceAccount", "jobTitle", "mail", "mailNickname", "mobilePhone", "onPremisesDistinguishedName", "officeLocation", "onPremisesLastSyncDateTime", "otherMails", "postalCode", "preferredDataLocation", "preferredLanguage", "proxyAddresses", "showInAddressList", "state", "streetAddress", "surname", "usageLocation", "userPrincipalName", "userType", "assignedLicenses", "onPremisesSyncEnabled", "LicJoined", "Aliases", "primDomain"
# Write to the Azure Functions log stream.
Write-Host "PowerShell HTTP trigger function processed a request."
@@ -16,17 +16,34 @@ $TenantFilter = $Request.Query.TenantFilter
$userid = $Request.Query.UserID
$GraphRequest = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/users/$($userid)?`$top=999&`$select=$($selectlist -join ',')" -tenantid $TenantFilter | Select-Object $selectlist | ForEach-Object {
$_.onPremisesSyncEnabled = [bool]($_.onPremisesSyncEnabled)
- $_.Aliasses = $_.Proxyaddresses -join ", "
+ $_.Aliases = $_.Proxyaddresses -join ", "
$SkuID = $_.AssignedLicenses.skuid
$_.LicJoined = ($ConvertTable | Where-Object { $_.guid -in $skuid }).'Product_Display_Name' -join ", "
+ $_.primDomain = ($_.userPrincipalName -split '@' | Select-Object -Last 1)
$_
}
+if ($userid) {
+ $uri = "https://login.microsoftonline.com/$($TenantFilter)/oauth2/token"
+ $body = "resource=https://admin.microsoft.com&grant_type=refresh_token&refresh_token=$($ENV:ExchangeRefreshToken)"
+ $token = Invoke-RestMethod $uri -Body $body -ContentType "application/x-www-form-urlencoded" -ErrorAction SilentlyContinue -Method post
+ $LastSignIn = Invoke-RestMethod -ContentType "application/json;charset=UTF-8" -Uri "https://admin.microsoft.com/admin/api/users/$($userid)/lastSignInInfo" -Method GET -Headers @{
+ Authorization = "Bearer $($token.access_token)";
+ "x-ms-client-request-id" = [guid]::NewGuid().ToString();
+ "x-ms-client-session-id" = [guid]::NewGuid().ToString()
+ 'x-ms-correlation-id' = [guid]::NewGuid()
+ 'X-Requested-With' = 'XMLHttpRequest'
+ }
+ $GraphRequest = $GraphRequest | Select-Object *,
+ @{ Name = 'LastSigninApplication'; Expression = { $LastSignIn.AppDisplayName } },
+ @{ Name = 'LastSigninDate'; Expression = { $($LastSignIn.CreatedDateTime | Out-String) } },
+ @{ Name = 'LastSigninStatus'; Expression = { $LastSignIn.Status.AdditionalDetails } },
+ @{ Name = 'LastSigninResult'; Expression = { if ($LastSignIn.Status.ErrorCode -eq 0) { "Success" } else { "Failure" } } },
+ @{ Name = 'LastSigninFailureReason'; Expression = { if ($LastSignIn.Status.ErrorCode -eq 0) { "Sucessfully signed in" } else { $LastSignIn.status.FailureReason } } }
+}
# Associate values to output bindings by calling 'Push-OutputBinding'.
Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
StatusCode = [HttpStatusCode]::OK
Body = @($GraphRequest)
- })
-
-#@{ Name = 'LicJoined'; Expression = { ($_.assignedLicenses | ForEach-Object { convert-skuname -skuID $_.skuid }) -join ", " } }, @{ Name = 'Aliasses'; Expression = { $_.Proxyaddresses -join ", " } }, @{ Name = 'primDomain'; Expression = { $_.userPrincipalName -split "@" | Select-Object -Last 1 } }
\ No newline at end of file
+ })
\ No newline at end of file
diff --git a/RemoveUser/run.ps1 b/RemoveUser/run.ps1
index c6c514f5e9f0..2a0c3f32071a 100644
--- a/RemoveUser/run.ps1
+++ b/RemoveUser/run.ps1
@@ -28,4 +28,4 @@ Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
Body = $body
})
-#@{ Name = 'LicJoined'; Expression = { ($_.assignedLicenses | ForEach-Object { convert-skuname -skuID $_.skuid }) -join ", " } }, @{ Name = 'Aliasses'; Expression = { $_.Proxyaddresses -join ", " } }, @{ Name = 'primDomain'; Expression = { $_.userPrincipalName -split "@" | Select-Object -Last 1 } }
\ No newline at end of file
+#@{ Name = 'LicJoined'; Expression = { ($_.assignedLicenses | ForEach-Object { convert-skuname -skuID $_.skuid }) -join ", " } }, @{ Name = 'Aliases'; Expression = { $_.Proxyaddresses -join ", " } }, @{ Name = 'primDomain'; Expression = { $_.userPrincipalName -split "@" | Select-Object -Last 1 } }
\ No newline at end of file
diff --git a/SecurityBaselines_All/function.json b/SecurityBaselines_All/function.json
new file mode 100644
index 000000000000..2d4ea9094b24
--- /dev/null
+++ b/SecurityBaselines_All/function.json
@@ -0,0 +1,9 @@
+{
+ "bindings": [
+ {
+ "name": "tenant",
+ "direction": "in",
+ "type": "activityTrigger"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/SecurityBaselines_All/results.json b/SecurityBaselines_All/results.json
new file mode 100644
index 000000000000..2767793f552b
--- /dev/null
+++ b/SecurityBaselines_All/results.json
@@ -0,0 +1 @@
+"WindmillGroup.onmicrosoft.com"
diff --git a/SecurityBaselines_All/run.ps1 b/SecurityBaselines_All/run.ps1
new file mode 100644
index 000000000000..a4862f7e8b17
--- /dev/null
+++ b/SecurityBaselines_All/run.ps1
@@ -0,0 +1,4 @@
+param($tenant)
+#Log-request -API "SecurityBaselines" -tenant $tenant -message "SecurityBaselines_All called at $((Get-Date).tofiletime())" -sev Info
+Write-Output $tenant.defaultDomainName
+
diff --git a/SecurityBaselines_Orchestration/function.json b/SecurityBaselines_Orchestration/function.json
new file mode 100644
index 000000000000..7326b39c184d
--- /dev/null
+++ b/SecurityBaselines_Orchestration/function.json
@@ -0,0 +1,9 @@
+{
+ "bindings": [
+ {
+ "name": "Context",
+ "type": "orchestrationTrigger",
+ "direction": "in"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/SecurityBaselines_Orchestration/run.ps1 b/SecurityBaselines_Orchestration/run.ps1
new file mode 100644
index 000000000000..5f15b2ea36e0
--- /dev/null
+++ b/SecurityBaselines_Orchestration/run.ps1
@@ -0,0 +1,24 @@
+param($Context)
+
+Log-request -API "SecurityBaselines" -tenant $tenant -message "SecurityBaselines_Orchestration called at $((Get-Date).tofiletime())" -sev Info
+#Remove-Item "SecurityBaselines_All\results.json" -Force
+
+New-Item "SecurityBaselines_All\CurrentlyRunning.txt" -ItemType File -Force
+$Batch = Get-Tenants
+$ParallelTasks = foreach ($Item in $Batch) {
+ Invoke-DurableActivity -FunctionName "SecurityBaselines_All" -Input $item -NoWait
+}
+
+Log-request -API "SecurityBaselines" -tenant $tenant -message "STARTING PROCESS OF OUTPUTS!" -sev Info
+$Outputs = Wait-ActivityFunction -Task $ParallelTasks
+Log-request -API "SecurityBaselines" -tenant $tenant -message "Outputs found count = $($Outputs.count)" -sev Info
+
+foreach ($item in $Outputs) {
+ Write-Host $Item | Out-String
+ $Object = $Item | ConvertTo-Json
+
+ Set-Content "SecurityBaselines_All\results.json" -Value $Object -Force
+}
+
+#Log-request -API "DomainAnalyser" -tenant $tenant -message "Domain Analyser has Finished" -sev Info
+Remove-Item "SecurityBaselines_All\CurrentlyRunning.txt" -Force
\ No newline at end of file
diff --git a/SecurityBaselines_OrchestrationStarter/function.json b/SecurityBaselines_OrchestrationStarter/function.json
new file mode 100644
index 000000000000..14c44f4f0217
--- /dev/null
+++ b/SecurityBaselines_OrchestrationStarter/function.json
@@ -0,0 +1,24 @@
+{
+ "bindings": [
+ {
+ "authLevel": "anonymous",
+ "name": "Request",
+ "type": "httpTrigger",
+ "direction": "in",
+ "methods": [
+ "post",
+ "get"
+ ]
+ },
+ {
+ "type": "http",
+ "direction": "out",
+ "name": "Response"
+ },
+ {
+ "name": "starter",
+ "type": "durableClient",
+ "direction": "in"
+ }
+ ]
+}
diff --git a/SecurityBaselines_OrchestrationStarter/run.ps1 b/SecurityBaselines_OrchestrationStarter/run.ps1
new file mode 100644
index 000000000000..b94c58e14fd2
--- /dev/null
+++ b/SecurityBaselines_OrchestrationStarter/run.ps1
@@ -0,0 +1,33 @@
+using namespace System.Net
+
+param($Request, $TriggerMetadata)
+Log-request -API "SecurityBaselines" -tenant $tenant -message "SecurityBaselines_OrchestrationStarter called at $(Get-Date)" -sev Info
+$APIName = $TriggerMetadata.FunctionName
+$OrchestratorName = "SecurityBaselines_Orchestration"
+
+$CurrentlyRunning = Get-Item "SecurityBaselines_All\CurrentlyRunning.txt" -ErrorAction SilentlyContinue | Where-Object -Property LastWriteTime -GT (Get-Date).AddHours(-24)
+if ($CurrentlyRunning) {
+ $Results = [pscustomobject]@{"Results" = "Already running. Please wait for the current instance to finish" }
+ Log-request -API $APIName -message "Attempted to start an instance but an instance was already running." -sev Info
+}
+else {
+ $InstanceId = Start-NewOrchestration -FunctionName $OrchestratorName
+ Write-Host "Started orchestration with ID = '$InstanceId'"
+ $Orchestrator = New-OrchestrationCheckStatusResponse -Request $Request -InstanceId $InstanceId
+ do {
+ $StillRunning = Get-Item "SecurityBaselines_All\CurrentlyRunning.txt" -ErrorAction SilentlyContinue | Where-Object -Property LastWriteTime -GT (Get-Date).AddHours(-24)
+ if (!$StillRunning) {
+ $Results = Get-Content "SecurityBaselines_All\Results.json" -ErrorAction SilentlyContinue
+ }
+ else {
+ Start-Sleep -Milliseconds 500
+ }
+ } while ($StillRunning)
+}
+
+
+
+Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
+ StatusCode = [HttpStatusCode]::OK
+ Body = $results
+ })
\ No newline at end of file
diff --git a/Standards_AuditLog/run.ps1 b/Standards_AuditLog/run.ps1
index 965eb974da76..a9254a26dbca 100644
--- a/Standards_AuditLog/run.ps1
+++ b/Standards_AuditLog/run.ps1
@@ -6,6 +6,10 @@ try {
$credential = New-Object System.Management.Automation.PSCredential($upn, $tokenValue)
$session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri "https://ps.outlook.com/powershell-liveid?DelegatedOrg=$($tenant)&BasicAuthToOAuthConversion=true" -Credential $credential -Authentication Basic -AllowRedirection -ErrorAction Continue
Import-PSSession $session -ea Silentlycontinue -AllowClobber -CommandName "Set-AdminAuditLogConfig", "Get-OrganizationConfig", "Enable-OrganizationCustomization"
+ $DehydratedTenant = (Get-OrganizationConfig).IsDehydrated
+ if ($DehydratedTenant) {
+ Enable-OrganizationCustomization
+ }
Set-AdminAuditLogConfig -UnifiedAuditLogIngestionEnabled $true
Get-PSSession | Remove-PSSession
Log-request -API "Standards" -tenant $tenant -message "Unified Audit Log Enabled." -sev Info
diff --git a/Standards_AutoExpandArchive/function.json b/Standards_AutoExpandArchive/function.json
new file mode 100644
index 000000000000..2d4ea9094b24
--- /dev/null
+++ b/Standards_AutoExpandArchive/function.json
@@ -0,0 +1,9 @@
+{
+ "bindings": [
+ {
+ "name": "tenant",
+ "direction": "in",
+ "type": "activityTrigger"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/Standards_AutoExpandArchive/run.ps1 b/Standards_AutoExpandArchive/run.ps1
new file mode 100644
index 000000000000..a42ac6ab92ec
--- /dev/null
+++ b/Standards_AutoExpandArchive/run.ps1
@@ -0,0 +1,16 @@
+param($tenant)
+
+try {
+ $upn = "notRequired@required.com"
+ $tokenvalue = ConvertTo-SecureString (Get-GraphToken -AppID 'a0c73c16-a7e3-4564-9a95-2bdf47383716' -RefreshToken $ENV:ExchangeRefreshToken -Scope 'https://outlook.office365.com/.default' -Tenantid $tenant).Authorization -AsPlainText -Force
+ $credential = New-Object System.Management.Automation.PSCredential($upn, $tokenValue)
+ $session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri "https://ps.outlook.com/powershell-liveid?DelegatedOrg=$($tenant)&BasicAuthToOAuthConversion=true" -Credential $credential -Authentication Basic -AllowRedirection -ErrorAction Continue
+ Import-PSSession $session -ea Silentlycontinue -AllowClobber -CommandName "Set-OrganizationConfig",
+ Set-OrganizationConfig -AutoExpandingArchive
+ Get-PSSession | Remove-PSSession
+ Log-request -API "Standards" -tenant $tenant -message "Added Auto Expanding Archive." -sev Info
+
+}
+catch {
+ Log-request -API "Standards" -tenant $tenant -message "Failed to apply Auto Expanding Archives Error: $($_.exception.message)" -sev Error
+}
\ No newline at end of file
diff --git a/Standards_DelegateSentItems/run.ps1 b/Standards_DelegateSentItems/run.ps1
index a6ae31a2962e..f13b0a3a7537 100644
--- a/Standards_DelegateSentItems/run.ps1
+++ b/Standards_DelegateSentItems/run.ps1
@@ -6,7 +6,7 @@ try {
$credential = New-Object System.Management.Automation.PSCredential($upn, $tokenValue)
$session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri "https://ps.outlook.com/powershell-liveid?DelegatedOrg=$($Tenant)&BasicAuthToOAuthConversion=true" -Credential $credential -Authentication Basic -AllowRedirection -ErrorAction Continue
Import-PSSession $session -ea Silentlycontinue -AllowClobber -CommandName "Get-Mailbox", "Set-mailbox"
- Get-Mailbox -ResultSize Unlimited -RecipientTypeDetails UserMailbox, SharedMailbox | set-mailbox -erroraction SilentlyContinue -MessageCopyForSentAsEnabled $true -MessageCopyForSendOnBehalfEnabled $true
+ Get-Mailbox -ResultSize Unlimited -RecipientTypeDetails UserMailbox, SharedMailbox | Where-Object { $_.MessageCopyForSendOnBehalfEnabled -eq $false -or $_.MessageCopyForSentAsEnabled -eq $false } | set-mailbox -erroraction SilentlyContinue -MessageCopyForSentAsEnabled $true -MessageCopyForSendOnBehalfEnabled $true
Get-PSSession | Remove-PSSession
Log-request -API "Standards" -tenant $tenant -message "Delegate Sent Items Style enabled." -sev Info
}
diff --git a/Standards_LegacyMFA/run.ps1 b/Standards_LegacyMFA/run.ps1
index c77e642671a2..4b2fac6b5d80 100644
--- a/Standards_LegacyMFA/run.ps1
+++ b/Standards_LegacyMFA/run.ps1
@@ -1,8 +1,24 @@
param($tenant)
try {
- #nothing yet.
+ $AADGraphtoken = (Get-GraphToken -scope "https://graph.windows.net/.default")
+ $tenantid = ((Get-Content "tenants.cache.json" | ConvertFrom-Json) | Where-Object -Property DefaultDomainName -EQ $tenant).CustomerID
+ $TrackingGuid = New-Guid
+ $LogonPost = @"
+http://provisioning.microsoftonline.com/IProvisioningWebService/MsolConnecturn:uuid:$TrackingGuidhttp://www.w3.org/2005/08/addressing/anonymous$($AADGraphtoken['Authorization'])50afce61-c917-435b-8c6d-60aa5a8b8aa71.2.183.57Version47$($TrackingGuid)https://provisioningapi.microsoftonline.com/provisioningwebservice.svcVersion4
+"@
+ $DataBlob = (Invoke-RestMethod -Method POST -Uri "https://provisioningapi.microsoftonline.com/provisioningwebservice.svc" -ContentType "application/soap+xml; charset=utf-8" -Body $LogonPost).envelope.header.BecContext.DataBlob.'#text'
+ $UserListXML = @"
+http://provisioning.microsoftonline.com/IProvisioningWebService/ListUsersurn:uuid:$TrackingGuidhttp://www.w3.org/2005/08/addressing/anonymous$($AADGraphtoken['Authorization'])$DataBlob250afce61-c917-435b-8c6d-60aa5a8b8aa71.2.183.57Version474e6cb653-c968-4a3a-8a11-2c8919218aebhttps://provisioningapi.microsoftonline.com/provisioningwebservice.svcVersion16$($tenantid)500AscendingNone
+"@
+ $Users = (Invoke-RestMethod -Uri "https://provisioningapi.microsoftonline.com/provisioningwebservice.svc" -Method post -Body $UserListXML -ContentType 'application/soap+xml; charset=utf-8').envelope.body.ListUsersResponse.listusersresult.returnvalue.results.user | Where-Object { ($_.StrongAuthenticationRequirements.StrongAuthenticationRequirement.state -eq $null) }
+ foreach ($user in $users) {
+ $MSOLXML = @"
+http://provisioning.microsoftonline.com/IProvisioningWebService/SetUserurn:uuid:$TrackingGuidhttp://www.w3.org/2005/08/addressing/anonymous$($AADGraphtoken['Authorization'])$($DataBlob)9450afce61-c917-435b-8c6d-60aa5a8b8aa71.2.183.57Version47$TrackingGuidhttps://provisioningapi.microsoftonline.com/provisioningwebservice.svcVersion16$($tenantid)$($User.ObjectID)*0001-01-01T00:00:00Enabled
+"@
+ $SetMFA = (Invoke-RestMethod -Uri "https://provisioningapi.microsoftonline.com/provisioningwebservice.svc" -Method post -Body $MSOLXML -ContentType 'application/soap+xml; charset=utf-8')
+ }
}
catch {
- Log-request "Standards API: $($tenant) failed to apply mailbox retention. Error: $($exception.message)" -sev Error
+ Log-request "Standards API: $($tenant) failed to apply per user MFA. Error: $($exception.message)" -sev Error
}
\ No newline at end of file
diff --git a/version_latest.txt b/version_latest.txt
index e2cac26c1a82..589268e6fedb 100644
--- a/version_latest.txt
+++ b/version_latest.txt
@@ -1 +1 @@
-1.2.3
\ No newline at end of file
+1.3.0
\ No newline at end of file