diff --git a/AddScheduledItems/function.json b/AddScheduledItems/function.json
new file mode 100644
index 000000000000..925eab5aeae1
--- /dev/null
+++ b/AddScheduledItems/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/AddScheduledItems/run.ps1 b/AddScheduledItems/run.ps1
new file mode 100644
index 000000000000..b8dbc7843324
--- /dev/null
+++ b/AddScheduledItems/run.ps1
@@ -0,0 +1,20 @@
+using namespace System.Net
+param($Request, $TriggerMetadata)
+$APIName = $TriggerMetadata.FunctionName
+Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug'
+$task = $Request.Body | ConvertFrom-Json
+$Table = Get-CIPPTable -TableName 'ScheduledTasks'
+Add-AzDataTableEntity @Table -Entity @{
+ PartitionKey = 'ScheduledTask'
+ TaskState = 'Scheduled'
+ RowKey = $task.TaskID
+ Command = $task.Command
+ Parameters = $task.Parameters
+ ScheduledTime = $task.ScheduledTime
+ Results = 'Not Executed'
+ # add more properties here based on what properties your tasks have
+}
+Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
+ StatusCode = [HttpStatusCode]::OK
+ Body = 'Task added successfully.'
+})
\ No newline at end of file
diff --git a/Applications_Upload/run.ps1 b/Applications_Upload/run.ps1
index 12abc83e0b40..31566b015b8d 100644
--- a/Applications_Upload/run.ps1
+++ b/Applications_Upload/run.ps1
@@ -112,7 +112,7 @@ foreach ($tenant in $tenants) {
$assign = New-GraphPOSTRequest -uri "https://graph.microsoft.com/beta/deviceAppManagement/mobileApps/$($NewApp.id)/assign" -tenantid $tenant -type POST -body $AssignBody
Write-LogMessage -api "AppUpload" -tenant $($Tenant) -message "Assigned application $($chocoApp.ApplicationName) to $AssignTo" -Sev "Info"
}
- Write-LogMessage -api "AppUpload" -tenant $($Tenant) -message "Successfully added Application"
+ Write-LogMessage -api "AppUpload" -tenant $($Tenant) -message "Successfully added Application" -Sev "Info"
}
catch {
"Failed to add Application for $($Tenant): $($_.Exception.Message)"
diff --git a/BestPracticeAnalyser_All/run.ps1 b/BestPracticeAnalyser_All/run.ps1
index 08003efa32af..741da28b8eaa 100644
--- a/BestPracticeAnalyser_All/run.ps1
+++ b/BestPracticeAnalyser_All/run.ps1
@@ -48,7 +48,7 @@ try {
$Result.TAPEnabled = $TAPEnabled.State
}
catch {
- Write-LogMessage -API 'BestPracticeAnalyser' -tenant $tenant -message "Security Defaults State on $($tenant) Error: $($_.exception.message)" -sev 'Error'
+ Write-LogMessage -API 'BestPracticeAnalyser' -tenant $tenant -message "Retrieving TAP state failed: $($tenant) Error: $($_.exception.message)" -sev 'Error'
}
# Get the nudge State
try {
diff --git a/Cache_SAMSetup/PermissionsTranslator.json b/Cache_SAMSetup/PermissionsTranslator.json
index 4d31a2c37dae..f7ce2cf3235a 100644
--- a/Cache_SAMSetup/PermissionsTranslator.json
+++ b/Cache_SAMSetup/PermissionsTranslator.json
@@ -1,4 +1,11 @@
[
+ {
+ "description": "Allows Exchange Management as app",
+ "displayName": "Manage Exchange As Application ",
+ "id": "dc50a0fb-09a3-484d-be87-e023b12c6440",
+ "origin": "Application (Office 365 Exchange Online)",
+ "value": "Exchange.ManageAsApp"
+ },
{
"description": "Allows the app to read a basic set of profile properties of other users in your organization without a signed-in user. Includes display name, first and last name, email address, open extensions, and photo.",
"displayName": "Read all users' basic profiles",
diff --git a/Cache_SAMSetup/SAMManifest.json b/Cache_SAMSetup/SAMManifest.json
index 478ae7f6b135..3f458048fda0 100644
--- a/Cache_SAMSetup/SAMManifest.json
+++ b/Cache_SAMSetup/SAMManifest.json
@@ -146,7 +146,11 @@
{ "id": "34bf0e97-1971-4929-b999-9e2442d941d7", "type": "Role" },
{ "id": "45cc0394-e837-488b-a098-1918f48d186c", "type": "Role" },
{ "id": "be74164b-cff1-491c-8741-e671cb536e13", "type": "Role" },
+ { "id": "2a60023f-3219-47ad-baa4-40e17cd02a1d", "type": "Role" },
+ { "id": "338163d7-f101-4c92-94ba-ca46fe52447c", "type": "Role" },
+ { "id": "cac88765-0581-4025-9725-5ebc13f729ee", "type": "Role" },
{ "id": "b27a61ec-b99c-4d6a-b126-c4375d08ae30", "type": "Scope" },
+ { "id": "84bccea3-f856-4a8a-967b-dbe0a3d53a64", "type": "Scope" },
{ "id": "280b3b69-0437-44b1-bc20-3b2fca1ee3e9", "type": "Scope" },
{
"id": "885f682f-a990-4bad-a642-36736a74b0c7",
@@ -172,7 +176,8 @@
{
"resourceAppId": "00000002-0000-0ff1-ce00-000000000000",
"resourceAccess": [
- { "id": "ab4f2b77-0b06-4fc1-a9de-02113fc2ab7c", "type": "Scope" }
+ { "id": "ab4f2b77-0b06-4fc1-a9de-02113fc2ab7c", "type": "Scope" },
+ { "id": "dc50a0fb-09a3-484d-be87-e023b12c6440", "type": "Role" }
]
},
{
diff --git a/Config/7b41924e-3051-4a23-b0d0-8cdeadc2c05a.IntuneTemplate.json b/Config/7b41924e-3051-4a23-b0d0-8cdeadc2c05a.IntuneTemplate.json
index 9e29aee25935..b7e696cc9b81 100644
--- a/Config/7b41924e-3051-4a23-b0d0-8cdeadc2c05a.IntuneTemplate.json
+++ b/Config/7b41924e-3051-4a23-b0d0-8cdeadc2c05a.IntuneTemplate.json
@@ -1,7 +1,7 @@
{
"Displayname": "CIPP Default: Enable Onedrive Silent Logon and Known Folder Move",
"Description": "This policy enables Onedrive Silent Logon and Known Folder move",
- "RAWJson": "{\"added\":[{\"enabled\":true,\"presentationValues\":[],\"definition@odata.bind\":\"https://graph.microsoft.com/beta/deviceManagement/groupPolicyDefinitions('9a4db949-29e4-4e31-a129-bf2b88d8fa1b')\"},{\"enabled\":true,\"presentationValues\":[{\"@odata.type\":\"#microsoft.graph.groupPolicyPresentationValueText\",\"value\":\"$($tenant.customerId)\",\"presentation@odata.bind\":\"https://graph.microsoft.com/beta/deviceManagement/groupPolicyDefinitions('39147fa2-6c5e-437b-8264-19b50b891709')/presentations('fbefbbdf-5382-477c-8b6c-71f4a06e2805')\"},{\"@odata.type\":\"#microsoft.graph.groupPolicyPresentationValueText\",\"value\":\"0\",\"presentation@odata.bind\":\"https://graph.microsoft.com/beta/deviceManagement/groupPolicyDefinitions('39147fa2-6c5e-437b-8264-19b50b891709')/presentations('35c82072-a93b-4022-be14-8684c2f6fcc2')\"}],\"definition@odata.bind\":\"https://graph.microsoft.com/beta/deviceManagement/groupPolicyDefinitions('39147fa2-6c5e-437b-8264-19b50b891709')\"},{\"enabled\":true,\"presentationValues\":[],\"definition@odata.bind\":\"https://graph.microsoft.com/beta/deviceManagement/groupPolicyDefinitions('81c07ba0-7512-402d-b1f6-00856975cfab')\"},{\"enabled\":true,\"presentationValues\":[],\"definition@odata.bind\":\"https://graph.microsoft.com/beta/deviceManagement/groupPolicyDefinitions('61b07a01-7e60-4127-b086-f6b32458a5c5')\"},{\"enabled\":true,\"presentationValues\":[],\"definition@odata.bind\":\"https://graph.microsoft.com/beta/deviceManagement/groupPolicyDefinitions('8866d7bd-42fb-4695-b6f2-80e0a90b1ac3')\"},{\"enabled\":true,\"presentationValues\":[],\"definition@odata.bind\":\"https://graph.microsoft.com/beta/deviceManagement/groupPolicyDefinitions('f974758d-1fab-42fe-ad36-3a6cd25c49c1')\"}],\"updated\":[],\"deletedIds\":[]}\r\n",
+ "RAWJson": "{\n\"added\":[\n{\n\"enabled\":true,\n\"presentationValues\":[],\n\"definition@odata.bind\":\"https://graph.microsoft.com/beta/deviceManagement/groupPolicyDefinitions('9a4db949-29e4-4e31-a129-bf2b88d8fa1b')\"\n},\n{\n\"enabled\":true,\n\"presentationValues\":[\n{\n\"@odata.type\":\"#microsoft.graph.groupPolicyPresentationValueText\",\n\"value\":\"%tenantid%\",\n\"presentation@odata.bind\":\"https://graph.microsoft.com/beta/deviceManagement/groupPolicyDefinitions('39147fa2-6c5e-437b-8264-19b50b891709')/presentations('fbefbbdf-5382-477c-8b6c-71f4a06e2805')\"\n},\n{\n\"@odata.type\":\"#microsoft.graph.groupPolicyPresentationValueText\",\n\"value\":\"0\",\n\"presentation@odata.bind\":\"https://graph.microsoft.com/beta/deviceManagement/groupPolicyDefinitions('39147fa2-6c5e-437b-8264-19b50b891709')/presentations('35c82072-a93b-4022-be14-8684c2f6fcc2')\"\n}\n],\n\"definition@odata.bind\":\"https://graph.microsoft.com/beta/deviceManagement/groupPolicyDefinitions('39147fa2-6c5e-437b-8264-19b50b891709')\"\n},\n{\n\"enabled\":true,\n\"presentationValues\":[],\n\"definition@odata.bind\":\"https://graph.microsoft.com/beta/deviceManagement/groupPolicyDefinitions('81c07ba0-7512-402d-b1f6-00856975cfab')\"\n},\n{\n\"enabled\":true,\n\"presentationValues\":[],\n\"definition@odata.bind\":\"https://graph.microsoft.com/beta/deviceManagement/groupPolicyDefinitions('61b07a01-7e60-4127-b086-f6b32458a5c5')\"\n},\n],\n\"updated\":[],\n\"deletedIds\":[]\n}",
"Type": "Admin",
"GUID": "7b41924e-3051-4a23-b0d0-8cdeadc2c05a.IntuneTemplate.json"
}
diff --git a/DomainAnalyser_GetTenantDomains/run.ps1 b/DomainAnalyser_GetTenantDomains/run.ps1
index a2717df70d17..def86a78461b 100644
--- a/DomainAnalyser_GetTenantDomains/run.ps1
+++ b/DomainAnalyser_GetTenantDomains/run.ps1
@@ -93,5 +93,5 @@ if ($TenantCount -gt 0) {
}
catch { Write-LogMessage -API 'DomainAnalyser' -message "Domain Analyser GetTenantDomains Error $($_.Exception.Message)" -sev info }
}
- catch { Write-LogMessage -API 'DomainAnalyser' -message "GetTenantDomains loop exception: $($_.Exception.Message) line $($_.InvocationInfo.ScriptLineNumber)" }
+ catch { Write-LogMessage -API 'DomainAnalyser' -message "GetTenantDomains loop exception: $($_.Exception.Message) line $($_.InvocationInfo.ScriptLineNumber)" -sev "Error"}
}
diff --git a/ExecAccessChecks/run.ps1 b/ExecAccessChecks/run.ps1
index 42b0d2e6fbe8..a07a4f7cfb5b 100644
--- a/ExecAccessChecks/run.ps1
+++ b/ExecAccessChecks/run.ps1
@@ -137,7 +137,7 @@ if ($Request.query.Tenants -eq 'true') {
catch {
@{
TenantName = "$($tenant)"
- Status = "Failed to connect to $(Get-NormalizedError -message $_.Exception.Message)"
+ Status = "Failed to connect to: $(Get-NormalizedError -message $_.Exception.Message)"
}
Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $tenant -message "Tenant access check failed: $(Get-NormalizedError -message $_) " -Sev 'Error'
diff --git a/ExecCPVPermissions/run.ps1 b/ExecCPVPermissions/run.ps1
index b91fa7ba63ab..7494a1b54f99 100644
--- a/ExecCPVPermissions/run.ps1
+++ b/ExecCPVPermissions/run.ps1
@@ -43,9 +43,7 @@ $GraphRequest = $ExpectedPermissions.requiredResourceAccess | ForEach-Object {
$AppBody = @"
{
"ApplicationGrants":[ $(ConvertTo-Json -InputObject $RequiredCPVPerms -Compress -Depth 10)],
- "ApplicationId": "$($env:ApplicationID)",
- "DisplayName": "CIPP-SAM"
-}
+ "ApplicationId": "$($env:ApplicationID)"}
"@
$CPVConsent = New-GraphpostRequest -body $AppBody -Type POST -noauthcheck $true -uri "https://api.partnercenter.microsoft.com/v1/customers/$($TenantFilter)/applicationconsents" -scope "https://api.partnercenter.microsoft.com/.default" -tenantid $env:TenantID
"Succesfully set CPV permissions for $Permissionsname"
@@ -58,6 +56,34 @@ $GraphRequest = $ExpectedPermissions.requiredResourceAccess | ForEach-Object {
}
}
+
+$ourSVCPrincipal = New-GraphGETRequest -uri "https://graph.microsoft.com/beta/servicePrincipals(appId='$($ENV:applicationid)')" -tenantid $Tenantfilter
+
+# if the app svc principal exists, consent app permissions
+$apps = $ExpectedPermissions
+$Grants = foreach ($App in $apps.requiredResourceAccess) {
+ try {
+ $svcPrincipalId = New-GraphGETRequest -uri "https://graph.microsoft.com/v1.0/servicePrincipals(appId='$($app.resourceAppId)')" -tenantid $tenantfilter
+ }
+ catch {
+ continue
+ }
+ foreach ($SingleResource in $app.ResourceAccess | Where-Object -Property Type -EQ "Role") {
+ [pscustomobject]@{
+ principalId = $($ourSVCPrincipal.id)
+ resourceId = $($svcPrincipalId.id)
+ appRoleId = "$($SingleResource.Id)"
+ }
+ }
+}
+foreach ($Grant in $grants) {
+ try {
+ $SettingsRequest = New-GraphPOSTRequest -body ($grant | ConvertTo-Json) -uri "https://graph.microsoft.com/beta/servicePrincipals/$($ourSVCPrincipal.id)/appRoleAssignedTo" -tenantid $tenantfilter -type POST
+ }
+ catch {
+ "Failed to grant $($grant.appRoleId) to $($grant.resourceId): $($_.Exception.Message). "
+ }
+}
$StatusCode = [HttpStatusCode]::OK
# Associate values to output bindings by calling 'Push-OutputBinding'.
diff --git a/ExecOffboardUser/run.ps1 b/ExecOffboardUser/run.ps1
index 9b8665b4b796..8a78bd45e49c 100644
--- a/ExecOffboardUser/run.ps1
+++ b/ExecOffboardUser/run.ps1
@@ -14,286 +14,55 @@ try {
Write-Host ($request.body | ConvertTo-Json)
$results = switch ($request.body) {
{ $_."ConvertToShared" -eq 'true' } {
- try {
- $SharedMailbox = New-ExoRequest -tenantid $TenantFilter -cmdlet "Set-mailbox" -cmdParams @{Identity = $userid; type = "Shared" } -Anchor $username
- $ConvertedMailbox = New-ExoRequest -tenantid $TenantFilter -cmdlet "Get-mailbox" -cmdParams @{Identity = $userid }
- $counter = 0
- while ($ConvertedMailbox.RecipientTypeDetails -eq 'UserMailbox' -and $counter -le 3) {
- $counter ++
- $ConvertedMailbox = New-ExoRequest -tenantid $TenantFilter -cmdlet "Get-mailbox" -cmdParams @{Identity = $userid }
- Write-Host "Waiting on convert"
-
- if ($ConvertedMailbox.RecipientTypeDetails -eq "UserMailbox" -and $counter -eq 3) {
- $LicenseUnAssignAllowed = $false
- "Conversion to Shared Mailbox is in progress. We might not be able to make changes to the licenses."
- }
- elseif ($ConvertedMailbox.RecipientTypeDetails -ne "UserMailbox" -and $counter -eq 3) {
- $LicenseUnAssignAllowed = $true
- "Converted $($username) to Shared Mailbox"
- }
- Start-Sleep -Milliseconds 500
- }
-
- Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Converted $($username) to a shared mailbox" -Sev "Info" -tenant $TenantFilter
-
- }
- catch {
- Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Could not convert $username to shared mailbox" -Sev "Error" -tenant $TenantFilter
- "Could not convert $($username) to a shared mailbox. Error: $($_.Exception.Message)"
- }
+ Set-CIPPMailboxType -ExecutingUser $request.headers.'x-ms-client-principal' -tenantFilter $tenantFilter -userid $username -username $username -MailboxType "Shared" -APIName "ExecOffboardUser"
}
{ $_.RevokeSessions -eq 'true' } {
- try {
- $GraphRequest = New-GraphPostRequest -uri "https://graph.microsoft.com/beta/users/$($userid)/invalidateAllRefreshTokens" -tenantid $TenantFilter -type POST -body '{}' -verbose
- "Success. All sessions by $username have been revoked"
- Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Revoked sessions for $($username)" -Sev "Info" -tenant $TenantFilter
-
- }
- catch {
- "Revoke Session Failed: $($_.Exception.Message)"
- }
+ Revoke-CIPPSessions -tenantFilter $tenantFilter -username $username -userid $userid -ExecutingUser $request.headers.'x-ms-client-principal' -APIName "ExecOffboardUser"
}
{ $_.ResetPass -eq 'true' } {
- try {
- $password = New-passwordString
- $passwordProfile = @"
- {"passwordProfile": { "forceChangePasswordNextSignIn": true, "password": "$password" }}'
-"@
- $GraphRequest = New-GraphPostRequest -uri "https://graph.microsoft.com/v1.0/users/$($userid)" -tenantid $TenantFilter -type PATCH -body $passwordProfile -verbose
- "The new password is $password"
- Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Reset the password for $($username)" -Sev "Info" -tenant $TenantFilter
-
- }
- catch {
- Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Could not reset password for $($username)" -Sev "Error" -tenant $TenantFilter
-
- "Could not reset password for $($username). Error: $($_.Exception.Message)"
- }
+ Set-CIPPResetPassword -tenantFilter $tenantFilter -userid $username -ExecutingUser $request.headers.'x-ms-client-principal' -APIName "ExecOffboardUser"
}
{ $_.RemoveGroups -eq 'true' } {
- $AllGroups = (New-GraphGetRequest -uri "https://graph.microsoft.com/beta/groups/?$select=DisplayName,mailEnabled" -tenantid $tenantFilter)
- (New-GraphPostRequest -uri "https://graph.microsoft.com/beta/users/$($userid)/GetMemberGroups" -tenantid $tenantFilter -type POST -body '{"securityEnabledOnly": false}').value | ForEach-Object -parallel {
- Import-Module '.\GraphHelper.psm1'
- $group = $_
- try {
- $Groupname = ($using:AllGroups | Where-Object -Property id -EQ $group).displayName
- $IsMailEnabled = ($using:AllGroups | Where-Object -Property id -EQ $group).mailEnabled
- $IsM365Group = ($using:AllGroups | Where-Object {$_.id -eq $group -and $_.groupTypes -contains "Unified"}) -ne $null
- if ($IsM365Group) {
- $RemoveRequest = New-GraphPostRequest -uri "https://graph.microsoft.com/beta/groups/$_/members/$($using:userid)/`$ref" -tenantid $using:tenantFilter -type DELETE -body '' -Verbose
- }
- elseif (-not $IsMailEnabled) {
- $RemoveRequest = New-GraphPostRequest -uri "https://graph.microsoft.com/beta/groups/$_/members/$($using:userid)/`$ref" -tenantid $using:tenantFilter -type DELETE -body '' -Verbose
- }
- elseif ($IsMailEnabled) {
- $Params = @{ Identity = $Groupname; Member = $using:userid ; BypassSecurityGroupManagerCheck = $true }
- New-ExoRequest -tenantid $using:tenantFilter -cmdlet "Remove-DistributionGroupMember" -cmdParams $params -UseSystemMailbox $true
- }
- "Successfully removed user from group $Groupname"
- Write-LogMessage -user $using:request.headers.'x-ms-client-principal' -API $using:APINAME -message "Removed $($using:username) from $groupname" -Sev "Info" -tenant $using:TenantFilter
- }
- catch {
- Write-LogMessage -user $using:request.headers.'x-ms-client-principal' -API $using:APINAME -message "Could not remove $($using:username) from group $groupname" -Sev "Error" -tenant $using:TenantFilter
-
- "Could not remove user from group $($Groupname): $($_.Exception.Message). This is likely because its a Dynamic Group or synched with active directory"
- }
-
- }
+ Remove-CIPPGroups -userid $userid -tenantFilter $Tenantfilter -ExecutingUser $request.headers.'x-ms-client-principal' -APIName "ExecOffboardUser"
}
{ $_."HideFromGAL" -eq 'true' } {
- try {
- $HideRequest = New-GraphPostRequest -uri "https://graph.microsoft.com/v1.0/users/$($userid)" -tenantid $tenantFilter -type PATCH -body '{"showInAddressList": false}' -verbose
- "Hidden from address list"
- Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Hid $($username) from address list" -Sev "Info" -tenant $TenantFilter
-
- }
- catch {
- Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Could not hide $($username) from address list" -Sev "Error" -tenant $TenantFilter
-
- "Could not hide $($username) from address list. Error: $($_.Exception.Message)"
- }
+ Set-CIPPHideFromGAL -tenantFilter $tenantFilter -userid $username -HideFromGAL $true -ExecutingUser $request.headers.'x-ms-client-principal' -APIName "ExecOffboardUser"
}
{ $_."DisableSignIn" -eq 'true' } {
- try {
- $DisableUser = New-GraphPostRequest -uri "https://graph.microsoft.com/v1.0/users/$($userid)" -tenantid $TenantFilter -type PATCH -body '{"accountEnabled":false}' -verbose
- "Disabled user account for $username"
- Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Disabled $($username)" -Sev "Info" -tenant $TenantFilter
-
- }
- catch {
- Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Could not disable sign in for $($username)" -Sev "Error" -tenant $TenantFilter
-
- "Could not disable $($username). Error: $($_.Exception.Message)"
- }
-
+ Set-CIPPSignInState -TenantFilter $tenantFilter -userid $username -AccountEnabled $false -ExecutingUser $request.headers.'x-ms-client-principal' -APIName "ExecOffboardUser"
}
{ $_."OnedriveAccess" -ne "" } {
- try {
- $URL = (New-GraphGetRequest -uri "https://graph.microsoft.com/beta/reports/getOneDriveUsageAccountDetail(period='D7')?`$format=application/json" -tenantid $tenantFilter | Where-Object -Property 'OwnerPrincipalName' -EQ $username).siteUrl
- $OnMicrosoft = (New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/domains?$top=999' -tenantid $tenantFilter | Where-Object -Property isInitial -EQ $true).id.split('.') | Select-Object -First 1
- $AdminUrl = "https://$($onmicrosoft)-admin.sharepoint.com"
- Write-Host ($OnMicrosoft)
- $XML = @"
-
-
-
-
-
-
-
- $URL
- $($request.body.OnedriveAccess)
- true
-
-
-
-
-
-"@
- $request = New-GraphPostRequest -scope "$AdminURL/.default" -tenantid $tenantFilter -Uri "$AdminURL/_vti_bin/client.svc/ProcessQuery" -Type POST -Body $XML -ContentType 'text/xml'
-
- "Users Onedrive url is $URL. Access has been given to $($request.body.OnedriveAccess)"
- Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Gave $($Request.body.onedriveaccess) access to $($username) onedrive" -Sev "Info" -tenant $TenantFilter
-
- }
- catch {
- Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Could not add new owner to Onedrive $($request.body.AccessAutomap) on $($username)" -Sev "Error" -tenant $TenantFilter
-
- "Could not add owner to Onedrive for $($username). Error: $($_.Exception.Message)"
- }
+ Set-CIPPOnedriveAccess -tenantFilter $tenantFilter -userid $username -OnedriveAccessUser $request.body.OnedriveAccess -ExecutingUser $request.headers.'x-ms-client-principal' -APIName "ExecOffboardUser"
}
{ $_."AccessNoAutomap" -ne "" } {
- try {
- $permissions = New-ExoRequest -tenantid $TenantFilter -cmdlet "Add-MailboxPermission" -cmdParams @{Identity = $userid; user = $Request.body.AccessNoAutomap; automapping = $false; accessRights = @("FullAccess"); InheritanceType = "all" } -Anchor $username
- "added $($Request.body.AccessNoAutomap) to $($username) Shared Mailbox without automapping"
- Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Gave full permissions to $($request.body.AccessNoAutomap) on $($username)" -Sev "Info" -tenant $TenantFilter
-
- }
- catch {
- Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Could not add mailbox permissions for $($request.body.AccessAutomap) on $($username)" -Sev "Error" -tenant $TenantFilter
-
- "Could not add shared mailbox permissions with no auto-mapping for $($username). Error: $($_.Exception.Message)"
- }
+ Set-CIPPMailboxAccess -tenantFilter $tenantFilter -userid $username -AccessUser $request.body.AccessNoAutomap -Automap $true -AccessRights @("FullAccess") -ExecutingUser $request.headers.'x-ms-client-principal' -APIName "ExecOffboardUser"
}
{ $_."AccessAutomap" -ne "" } {
- try {
- $permissions = New-ExoRequest -tenantid $TenantFilter -cmdlet "Add-MailboxPermission" -cmdParams @{Identity = $userid; user = $Request.body.AccessAutomap; automapping = $true; accessRights = @("FullAccess"); InheritanceType = "all" } -Anchor $username
- "added $($Request.body.AccessAutomap) to $($username) Shared Mailbox with automapping"
- Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Gave full permissions to $($request.body.AccessAutomap) on $($username)" -Sev "Info" -tenant $TenantFilter
-
- }
- catch {
- Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Could not add mailbox permissions for $($request.body.AccessAutomap) on $($username)" -Sev "Error" -tenant $TenantFilter
-
- "Could not add shared mailbox permissions with automapping for $($username). Error: $($_.Exception.Message)"
- }
+ Set-CIPPMailboxAccess -tenantFilter $tenantFilter -userid $username -AccessUser $request.body.AccessNoAutomap -Automap $false -AccessRights @("FullAccess") -ExecutingUser $request.headers.'x-ms-client-principal' -APIName "ExecOffboardUser"
}
{ $_."OOO" -ne "" } {
- try {
- $permissions = New-ExoRequest -tenantid $TenantFilter -cmdlet "Set-MailboxAutoReplyConfiguration" -cmdParams @{Identity = $userid; AutoReplyState = "Enabled"; InternalMessage = $_."OOO"; ExternalMessage = $_."OOO" } -Anchor $username
- "added Out-of-office to $username"
- Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Set Out-of-office for $($username)" -Sev "Info" -tenant $TenantFilter
-
- }
- catch {
- Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Could not add OOO for $($username)" -Sev "Error" -tenant $TenantFilter
-
- "Could not add out of office message for $($username). Error: $($_.Exception.Message)"
- }
+ Set-CIPPOutOfOffice -tenantFilter $tenantFilter -userid $username -OOO $request.body.OOO -ExecutingUser $request.headers.'x-ms-client-principal' -APIName "ExecOffboardUser"
}
{ $_."forward" -ne "" } {
- try {
- $permissions = New-ExoRequest -tenantid $TenantFilter -cmdlet "Set-mailbox" -cmdParams @{Identity = $userid; ForwardingAddress = $_.forward ; DeliverToMailboxAndForward = [bool]$request.body.keepCopy } -Anchor $username
- "Forwarding all email for $username to $($_.Forward)"
- Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Set Forwarding for $($username) to $($_.Forward)" -Sev "Info" -tenant $TenantFilter
-
- }
- catch {
- Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Could not add forwarding for $($username)" -Sev "Error" -tenant $TenantFilter
-
- "Could not add forwarding for $($username). Error: $($_.Exception.Message)"
- }
+ Set-CIPPForwarding -userid $userid -username $username -tenantFilter $Tenantfilter -Forward $request.body.forward -KeepCopy [bool]$request.body.keepCopy -ExecutingUser $request.headers.'x-ms-client-principal' -APIName "ExecOffboardUser"
}
{ $_."RemoveLicenses" -eq 'true' } {
- try {
- if ($LicenseUnAssignAllowed -eq $false) {
- "Could not remove license as the users mailbox is still being converted to a shared mailbox."
- Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Could not remove license as the users mailbox is still being converted to a shared mailbox" -Sev "Info" -tenant $TenantFilter
- }
- else {
- $CurrentLicenses = (New-GraphGetRequest -uri "https://graph.microsoft.com/beta/users/$($userid)" -tenantid $tenantFilter).assignedlicenses.skuid
- $LicensesToRemove = if ($CurrentLicenses) { ConvertTo-Json @( $CurrentLicenses) } else { "[]" }
- $LicenseBody = '{"addLicenses": [], "removeLicenses": ' + $LicensesToRemove + '}'
- $LicRequest = New-GraphPostRequest -uri "https://graph.microsoft.com/beta/users/$($userid)/assignlicense" -tenantid $tenantFilter -type POST -body $LicenseBody -verbose
- "Removed current licenses: $(($ConvertTable | Where-Object { $_.guid -in $CurrentLicenses }).'Product_Display_Name' -join ',')"
- Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Removed all licenses for $($username)" -Sev "Info" -tenant $TenantFilter
- }
-
- }
- catch {
- Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Could not remove licenses for $($username)" -Sev "Error" -tenant $TenantFilter
-
- "Could not remove licenses for $($username). Error: $($_.Exception.Message)"
- }
+ Remove-CIPPLicense -userid $userid -username $Username -tenantFilter $Tenantfilter -ExecutingUser $request.headers.'x-ms-client-principal' -APIName "ExecOffboardUser"
}
{ $_."Deleteuser" -eq 'true' } {
- try {
- $DeleteRequest = New-GraphPostRequest -uri "https://graph.microsoft.com/beta/users/$($userid)" -type DELETE -tenant $TenantFilter
- "Deleted the user account"
- Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Deleted account $($username)" -Sev "Info" -tenant $TenantFilter
-
- }
- catch {
- Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Could not delete $($username)" -Sev "Error" -tenant $TenantFilter
- "Could not delete $($username). Error: $($_.Exception.Message)"
- }
+ Remove-CIPPUser -userid $userid -username $Username -tenantFilter $Tenantfilter -ExecutingUser $request.headers.'x-ms-client-principal' -APIName "ExecOffboardUser"
}
{ $_."RemoveRules" -eq 'true' } {
- try {
- $rules = New-ExoRequest -tenantid $TenantFilter -cmdlet "Get-InboxRule" -cmdParams @{mailbox = $userid }
- if ($rules -eq $null) {
- Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "No Rules for $($username) to delete" -Sev "Info" -tenant $TenantFilter
- "No rules for $($username) to delete"
- }
- else {
- ForEach ($rule in $rules) {
- New-ExoRequest -tenantid $TenantFilter -cmdlet "Remove-InboxRule" -Anchor $username -cmdParams @{Identity = $rule.Identity }
- }
- Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Deleted Rules for $($username)" -Sev "Info" -tenant $TenantFilter
- "Deleted Rules for $($username)"
- }
- }
- catch {
- Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Could not delete rules for $($username): $($_.Exception.Message)" -Sev "Error" -tenant $TenantFilter
- "Could not delete rules for $($username). Error: $($_.Exception.Message)"
- }
+ Remove-CIPPRules -userid $userid -username $Username -tenantFilter $Tenantfilter -ExecutingUser $request.headers.'x-ms-client-principal' -APIName "ExecOffboardUser"
}
{ $_."RemoveMobile" -eq 'true' } {
- try {
- $devices = New-ExoRequest -tenantid $TenantFilter -cmdlet "Get-MobileDevice" -Anchor $username -cmdParams @{mailbox = $userid } | ForEach-Object {
- try {
- New-ExoRequest -tenantid $TenantFilter -cmdlet "Remove-MobileDevice" -Anchor $username -cmdParams @{Identity = $_.Identity }
- "Removed device: $($_.FriendlyName)"
- }
- catch {
- "Could not remove device: $($_.FriendlyName)"
- continue
- }
- }
-
- Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Deleted mobile devices for $($username)" -Sev "Info" -tenant $TenantFilter
-
- }
- catch {
- Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Could not delete mobile devices for $($username): $($_.Exception.Message)" -Sev "Error" -tenant $TenantFilter
- "Could not delete mobile devices for $($username). Error: $($_.Exception.Message)"
- }
+ Remove-CIPPMobileDevice -userid $userid -username $Username -tenantFilter $Tenantfilter -ExecutingUser $request.headers.'x-ms-client-principal' -APIName "ExecOffboardUser"
}
}
diff --git a/ExtListGraphRequest/function.json b/ExtListGraphRequest/function.json
new file mode 100644
index 000000000000..db6842a7b1a6
--- /dev/null
+++ b/ExtListGraphRequest/function.json
@@ -0,0 +1,24 @@
+{
+ "scriptFile": "../Modules/CippEntryPoints/CippEntryPoints.psm1",
+ "entryPoint": "Receive-CippHttpTrigger",
+ "bindings": [
+ {
+ "authLevel": "anonymous",
+ "type": "httpTrigger",
+ "direction": "in",
+ "name": "Request",
+ "methods": ["get", "post"]
+ },
+ {
+ "type": "http",
+ "direction": "out",
+ "name": "Response"
+ },
+ {
+ "type": "queue",
+ "direction": "out",
+ "name": "QueueTenant",
+ "queueName": "GraphRequestQueue"
+ }
+ ]
+}
diff --git a/GetCippAlerts/run.ps1 b/GetCippAlerts/run.ps1
index 1c1e0d11b657..c26917f79185 100644
--- a/GetCippAlerts/run.ps1
+++ b/GetCippAlerts/run.ps1
@@ -24,13 +24,7 @@ if ($env:WEBSITE_RUN_FROM_PACKAGE -ne '1') {
})
}
if ($Rows) { $Rows | ForEach-Object { $alerts.add($_) } }
-if (!$env:WEBSITE_NAME) {
- #Running locally, no alerts. :)
- $Alerts = $null
-}
-else {
- $Alerts = @($Alerts)
-}
+$Alerts = @($Alerts)
$APIName = $TriggerMetadata.FunctionName
Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug'
diff --git a/GraphHelper.psm1 b/GraphHelper.psm1
index c9a4db6e3e84..659d93ff305a 100644
--- a/GraphHelper.psm1
+++ b/GraphHelper.psm1
@@ -64,7 +64,7 @@ function Get-GraphToken($tenantid, $scope, $AsApp, $AppID, $refreshToken, $Retur
if (!$tenantid) { $tenantid = $env:TenantID }
- $TokenKey = '{0}-{1}' -f $tenantid, $scope
+ $TokenKey = '{0}-{1}-{2}' -f $tenantid, $scope, $asApp
try {
if ($script:AccessTokens.$TokenKey -and [int](Get-Date -UFormat %s -Millisecond 0) -lt $script:AccessTokens.$TokenKey.expires_on) {
@@ -162,7 +162,6 @@ function New-GraphGetRequest {
if ($ComplexFilter) {
$headers['ConsistencyLevel'] = 'eventual'
}
- Write-Verbose "Using $($uri) as url"
$nextURL = $uri
# Track consecutive Graph API failures
@@ -219,8 +218,14 @@ function New-GraphPOSTRequest ($uri, $tenantid, $body, $type, $scope, $AsApp, $N
try {
$ReturnedData = (Invoke-RestMethod -Uri $($uri) -Method $TYPE -Body $body -Headers $headers -ContentType 'application/json; charset=utf-8')
} catch {
- $Message = ($_.ErrorDetails.Message | ConvertFrom-Json -ErrorAction SilentlyContinue).error.message
- if ($Message -eq $null) { $Message = $($_.Exception.Message) }
+ #setting ErrorMess because the error from a failed json conversion overwrites the exception.
+ $ErrorMess = $($_.Exception.Message)
+ try {
+ $Message = ($_.ErrorDetails.Message | ConvertFrom-Json -ErrorAction SilentlyContinue).error.message
+ } catch {
+ $Message = $ErrorMess
+ }
+
throw $Message
}
return $ReturnedData
@@ -359,8 +364,16 @@ function New-ClassicAPIPostRequest($TenantID, $Uri, $Method = 'POST', $Resource
}
}
-function Get-AuthorisedRequest($TenantID, $Uri) {
- if ($uri -like 'https://graph.microsoft.com/beta/contracts*' -or $uri -like '*/customers/*' -or $uri -eq 'https://graph.microsoft.com/v1.0/me/sendMail' -or $uri -like '*/tenantRelationships/*') {
+function Get-AuthorisedRequest {
+ [CmdletBinding()]
+ Param(
+ [string]$TenantID,
+ [string]$Uri
+ )
+ if (!$TenantID) {
+ $TenantID = $env:TenantId
+ }
+ if ($Uri -like 'https://graph.microsoft.com/beta/contracts*' -or $Uri -like '*/customers/*' -or $Uri -eq 'https://graph.microsoft.com/v1.0/me/sendMail' -or $Uri -like '*/tenantRelationships/*') {
return $true
}
if (($TenantID -ne $env:TenantId -or $env:PartnerTenantAvailable) -and (Get-Tenants -SkipList).defaultDomainName -notcontains $TenantID) {
@@ -370,6 +383,7 @@ function Get-AuthorisedRequest($TenantID, $Uri) {
}
}
+
function Get-Tenants {
param (
[Parameter( ParameterSetName = 'Skip', Mandatory = $True )]
@@ -518,9 +532,10 @@ function New-ExoRequest ($tenantid, $cmdlet, $cmdParams, $useSystemMailbox, $Anc
try {
$ReturnedData = Invoke-RestMethod "https://outlook.office365.com/adminapi/beta/$($tenant)/InvokeCommand" -Method POST -Body $ExoBody -Headers $Headers -ContentType 'application/json; charset=utf-8'
} catch {
+ $ErrorMess = $($_.Exception.Message)
$ReportedError = ($_.ErrorDetails | ConvertFrom-Json -ErrorAction SilentlyContinue)
$Message = if ($ReportedError.error.details.message) { $ReportedError.error.details.message } else { $ReportedError.error.innererror.internalException.message }
- if ($Message -eq $null) { $Message = $($_.Exception.Message) }
+ if ($null -eq $Message) { $Message = $ErrorMess }
throw $Message
}
return $ReturnedData.value
@@ -661,3 +676,72 @@ function New-passwordString {
-join ('abcdefghkmnrstuvwxyzABCDEFGHKLMNPRSTUVWXYZ23456789$%&*#'.ToCharArray() | Get-Random -Count $count)
}
}
+
+function New-GraphBulkRequest {
+ Param(
+ $tenantid,
+ $NoAuthCheck,
+ $scope,
+ $asapp,
+ $Requests
+ )
+
+ $headers = Get-GraphToken -tenantid $tenantid -scope $scope -AsApp $asapp
+
+ $URL = 'https://graph.microsoft.com/beta/$batch'
+
+ # Track consecutive Graph API failures
+ $TenantsTable = Get-CippTable -tablename Tenants
+ $Filter = "PartitionKey eq 'Tenants' and (defaultDomainName eq '{0}' or customerId eq '{0}')" -f $tenantid
+ $Tenant = Get-AzDataTableEntity @TenantsTable -Filter $Filter
+ if (!$Tenant) {
+ $Tenant = @{
+ GraphErrorCount = 0
+ LastGraphError = $null
+ PartitionKey = 'TenantFailed'
+ RowKey = 'Failed'
+ }
+ }
+ if ((Get-AuthorisedRequest -Uri $uri -TenantID $tenantid) -or $NoAuthCheck) {
+ try {
+ $ReturnedData = for ($i = 0; $i -lt $Requests.count; $i += 20) {
+ $req = @{}
+ # Use select to create hashtables of id, method and url for each call
+ $req['requests'] = ($Requests[$i..($i + 19)])
+ Invoke-RestMethod -Uri $URL -Method POST -Headers $headers -ContentType 'application/json; charset=utf-8' -Body ($req | ConvertTo-Json -Depth 10)
+ }
+
+ foreach ($MoreData in $ReturnedData.Responses | Where-Object { $_.body.'@odata.nextLink' }) {
+ $AdditionalValues = New-GraphGetRequest -ComplexFilter -uri $MoreData.body.'@odata.nextLink' -tenantid $TenantFilter
+ $NewValues = [System.Collections.Generic.List[PSCustomObject]]$MoreData.body.value
+ $AdditionalValues | ForEach-Object { $NewValues.add($_) }
+ $MoreData.body.value = $NewValues
+ }
+
+ } catch {
+ $Message = ($_.ErrorDetails.Message | ConvertFrom-Json -ErrorAction SilentlyContinue).error.message
+ if ($Message -eq $null) { $Message = $($_.Exception.Message) }
+ if ($Message -ne 'Request not applicable to target tenant.') {
+ $Tenant.LastGraphError = $Message
+ $Tenant.GraphErrorCount++
+ Update-AzDataTableEntity @TenantsTable -Entity $Tenant
+ }
+ throw $Message
+ }
+
+ $Tenant.LastGraphError = ''
+ Update-AzDataTableEntity @TenantsTable -Entity $Tenant
+
+ return $ReturnedData.responses
+ } else {
+ Write-Error 'Not allowed. You cannot manage your own tenant or tenants not under your scope'
+ }
+}
+
+function Get-GraphBulkResultByID ($Results, $ID, [switch]$Value) {
+ if ($Value) {
+ ($Results | Where-Object { $_.id -eq $ID }).body.value
+ } else {
+ ($Results | Where-Object { $_.id -eq $ID }).body
+ }
+}
diff --git a/LICENSE.CustomLicenses b/LICENSE.CustomLicenses
new file mode 100644
index 000000000000..058ce0a6cd3b
--- /dev/null
+++ b/LICENSE.CustomLicenses
@@ -0,0 +1 @@
+Custom licenses are available upon agreement via Github Sponsorships. Custom licenses will not ahve to be published in this repository. All contributors automatically agree with this provision.
\ No newline at end of file
diff --git a/ListDeviceDetails/function.json b/ListDeviceDetails/function.json
new file mode 100644
index 000000000000..3d31416065c2
--- /dev/null
+++ b/ListDeviceDetails/function.json
@@ -0,0 +1,22 @@
+{
+ "bindings": [
+ {
+ "authLevel": "anonymous",
+ "type": "httpTrigger",
+ "direction": "in",
+ "name": "Request",
+ "methods": ["get", "post"]
+ },
+ {
+ "type": "http",
+ "direction": "out",
+ "name": "Response"
+ },
+ {
+ "type": "queue",
+ "direction": "out",
+ "name": "Msg",
+ "queueName": "generalAllTenantQueue"
+ }
+ ]
+}
diff --git a/ListDeviceDetails/run.ps1 b/ListDeviceDetails/run.ps1
new file mode 100644
index 000000000000..14e97fb6a155
--- /dev/null
+++ b/ListDeviceDetails/run.ps1
@@ -0,0 +1,93 @@
+using namespace System.Net
+
+# Input bindings are passed in via param block.
+param($Request, $TriggerMetadata)
+
+$APIName = $TriggerMetadata.FunctionName
+Write-LogMessage -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
+$DeviceID = $Request.Query.DeviceID
+$DeviceName = $Request.Query.DeviceName
+$DeviceSerial = $Request.Query.DeviceSerial
+
+try {
+ if ($DeviceID) {
+ $GraphRequest = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/deviceManagement/managedDevices/$DeviceID" -Tenantid $tenantfilter
+ } elseif ($DeviceSerial -or $DeviceName) {
+ $Found = $False
+ if ($SeriaNumber -and $DeviceName) {
+ $GraphRequest = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/deviceManagement/managedDevices?`$filter=serialnumber eq '$DeviceSerial' and deviceName eq '$DeviceName'" -Tenantid $tenantfilter
+
+ if (($GraphRequest | Measure-Object).count -eq 1 -and $GraphRequest.'@odata.count' -ne 0 ) {
+ $Found = $True
+ }
+ }
+ if ($DeviceSerial -and $Found -eq $False) {
+ $GraphRequest = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/deviceManagement/managedDevices?`$filter=serialnumber eq '$DeviceSerial'" -Tenantid $tenantfilter
+ if (($GraphRequest | Measure-Object).count -eq 1 -and $GraphRequest.'@odata.count' -ne 0 ) {
+ $Found = $True
+ }
+ }
+ if ($DeviceName -and $Found -eq $False) {
+ $GraphRequest = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/deviceManagement/managedDevices?`$filter=deviceName eq '$DeviceName'" -Tenantid $tenantfilter
+ if (($GraphRequest | Measure-Object).count -eq 1 -and $GraphRequest.'@odata.count' -ne 0 ) {
+ $Found = $True
+ }
+ }
+
+ }
+
+ if (!(($GraphRequest | Measure-Object).count -eq 1 -and $GraphRequest.'@odata.count' -ne 0 )) {
+ $GraphRequest = $Null
+ }
+
+ if ($GraphRequest){
+ [System.Collections.Generic.List[PSCustomObject]]$BulkRequests = @(
+ @{
+ id = 'DeviceGroups'
+ method = 'GET'
+ url = "/devices(deviceID='$($GraphRequest.azureADDeviceId)')/memberOf"
+ },
+ @{
+ id = 'CompliancePolicies'
+ method = 'GET'
+ url = "/deviceManagement/managedDevices('$($GraphRequest.id)')/deviceCompliancePolicyStates"
+ },
+ @{
+ id = 'DetectedApps'
+ method = 'GET'
+ url = "deviceManagement/managedDevices('$($GraphRequest.id)')?expand=detectedApps"
+ }
+ )
+
+ $BulkResults = New-GraphBulkRequest -Requests $BulkRequests -tenantid $TenantFilter
+
+ $DeviceGroups = Get-GraphBulkResultByID -Results $BulkResults -ID 'DeviceGroups' -Value
+ $CompliancePolicies = Get-GraphBulkResultByID -Results $BulkResults -ID 'CompliancePolicies' -Value
+ $DetectedApps = Get-GraphBulkResultByID -Results $BulkResults -ID 'DetectedApps'
+
+ $Null = $GraphRequest | Add-Member -NotePropertyName 'DetectedApps' -NotePropertyValue ($DetectedApps.DetectedApps | select-object id, displayName, version)
+ $Null = $GraphRequest | Add-Member -NotePropertyName 'CompliancePolicies' -NotePropertyValue ($CompliancePolicies | select-object id, displayname, UserPrincipalName, state)
+ $Null = $GraphRequest | Add-Member -NotePropertyName 'DeviceGroups' -NotePropertyValue ($DeviceGroups | select-object id, displayName, description)
+
+
+ }
+
+ $StatusCode = [HttpStatusCode]::OK
+} catch {
+ $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message
+ $StatusCode = [HttpStatusCode]::Forbidden
+ $GraphRequest = $ErrorMessage
+
+}
+# Associate values to output bindings by calling 'Push-OutputBinding'.
+Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
+ StatusCode = $StatusCode
+ Body = $GraphRequest
+ })
diff --git a/ListGenericTestFunction/run.ps1 b/ListGenericTestFunction/run.ps1
index 7eee05f40a2f..c764f4a7f8a2 100644
--- a/ListGenericTestFunction/run.ps1
+++ b/ListGenericTestFunction/run.ps1
@@ -5,28 +5,10 @@ param($Request, $TriggerMetadata)
$APIName = $TriggerMetadata.FunctionName
Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug'
-
-
-try {
- Write-LogMessage -API "Scheduler_Billing" -tenant "none" -message "Starting billing processing." -sev Info
-
- $Table = Get-CIPPTable -TableName Extensionsconfig
- $Configuration = (Get-AzDataTableEntity @Table).config | ConvertFrom-Json -Depth 10
- foreach ($ConfigItem in $Configuration.psobject.properties.name) {
- switch ($ConfigItem) {
- "Gradient" {
- If ($Configuration.Gradient.enabled -and $Configuration.Gradient.BillingEnabled) {
- New-GradientServiceSyncRun
- }
- }
- }
- }
-}
-catch {
- Write-LogMessage -API "Scheduler_Billing" -tenant "none" -message "Could not start billing processing $($_.Exception.Message)" -sev Error
-}
+$body = '{"userPreferredMethodForSecondaryAuthentication": "push"}'
+$graphRequest = New-GraphPOSTRequest -body $body -type PATCH -uri 'https://graph.microsoft.com/beta/users/b4156a0c-91c5-4195-bb1b-41b96d0806a7/authentication/signInPreferences' -tenantid $TenantFilter
Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
- StatusCode = [HttpStatusCode]::OK
- Body = @("Executed")
- }) -clobber
\ No newline at end of file
+ StatusCode = [HttpStatusCode]::OK
+ Body = @($graphRequest)
+ }) -clobber
\ No newline at end of file
diff --git a/ListGraphRequest/function.json b/ListGraphRequest/function.json
index 8d0ea74203dd..a58da1def32f 100644
--- a/ListGraphRequest/function.json
+++ b/ListGraphRequest/function.json
@@ -1,6 +1,6 @@
{
- "scriptFile": "../Modules/GraphRequests/GraphRequests.psm1",
- "entryPoint": "Get-GraphRequestListHttp",
+ "scriptFile": "../Modules/CippEntryPoints/CippEntryPoints.psm1",
+ "entryPoint": "Receive-CippHttpTrigger",
"bindings": [
{
"authLevel": "anonymous",
@@ -17,7 +17,7 @@
{
"type": "queue",
"direction": "out",
- "name": "QueueTenant",
+ "name": "QueueItem",
"queueName": "GraphRequestQueue"
}
]
diff --git a/ListGraphRequestQueue/function.json b/ListGraphRequestQueue/function.json
index 8837488872b1..372b2c5a4b4a 100644
--- a/ListGraphRequestQueue/function.json
+++ b/ListGraphRequestQueue/function.json
@@ -1,9 +1,9 @@
{
- "scriptFile": "../Modules/GraphRequests/GraphRequests.psm1",
- "entryPoint": "Push-GraphRequestListQueue",
+ "scriptFile": "../Modules/CippEntryPoints/CippEntryPoints.psm1",
+ "entryPoint": "Receive-CippQueueTrigger",
"bindings": [
{
- "name": "QueueTenant",
+ "name": "QueueItem",
"type": "queueTrigger",
"direction": "in",
"queueName": "GraphRequestQueue"
diff --git a/ListIntunePolicy/run.ps1 b/ListIntunePolicy/run.ps1
index 50438eac61db..048d6f60f50b 100644
--- a/ListIntunePolicy/run.ps1
+++ b/ListIntunePolicy/run.ps1
@@ -24,7 +24,7 @@ try {
"https://graph.microsoft.com/beta/deviceManagement/groupPolicyConfigurations?`$expand=assignments&top=1000"
"https://graph.microsoft.com/beta/deviceAppManagement/mobileAppConfigurations?`$expand=assignments&`$filter=microsoft.graph.androidManagedStoreAppConfiguration/appSupportsOemConfig%20eq%20true"
"https://graph.microsoft.com/beta/deviceManagement/configurationPolicies"
- "https://graph.microsoft.com/beta/deviceManagement/configurationPolicies?`$select=id,name,description,platforms,technologies,lastModifiedDateTime,settingCount,roleScopeTagIds,isAssigned&`$top=100&`$filter=(platforms%20eq%20%27windows10%27%20or%20platforms%20eq%20%27macOS%27)%20and%20(technologies%20eq%20%27mdm%27%20or%20technologies%20eq%20%27windows10XManagement%27)%20and%20(templateReference/templateFamily%20eq%20%27none%27)&`$expand=assignments")
+ )
$GraphRequest = $GraphURLS | ForEach-Object {
$URLName = (($_).split('?') | Select-Object -First 1) -replace 'https://graph.microsoft.com/beta/deviceManagement/', ''
diff --git a/ListOrg/run.ps1 b/ListOrg/run.ps1
index f31f18f9d9b5..92ee467c194f 100644
--- a/ListOrg/run.ps1
+++ b/ListOrg/run.ps1
@@ -12,12 +12,15 @@ Write-Host "PowerShell HTTP trigger function processed a request."
# Interact with query parameters or the body of the request.
$TenantFilter = $Request.Query.TenantFilter
-$GraphRequest = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/organization" -tenantid $TenantFilter
-$StatusCode = [HttpStatusCode]::OK
-
+if ($TenantFilter -eq "AllTenants") {
+
+}
+else {
+ $GraphRequest = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/organization" -tenantid $TenantFilter
+}
# Associate values to output bindings by calling 'Push-OutputBinding'.
Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
- StatusCode = $StatusCode
+ StatusCode = [HttpStatusCode]::OK
Body = $GraphRequest
})
diff --git a/ListScheduledItems/function.json b/ListScheduledItems/function.json
new file mode 100644
index 000000000000..b0ca1676cc0b
--- /dev/null
+++ b/ListScheduledItems/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/ListScheduledItems/run.ps1 b/ListScheduledItems/run.ps1
new file mode 100644
index 000000000000..3b76fb77b212
--- /dev/null
+++ b/ListScheduledItems/run.ps1
@@ -0,0 +1,15 @@
+using namespace System.Net
+# Input bindings are passed in via param block.
+param($Request, $TriggerMetadata)
+$APIName = $TriggerMetadata.FunctionName
+Write-LogMessage -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.'
+$Table = Get-CIPPTable -TableName 'ScheduledTasks'
+$ScheduledTasks = Get-AzDataTableEntity @Table -Filter "PartitionKey eq 'ScheduledTask'"
+
+# Associate values to output bindings by calling 'Push-OutputBinding'.
+Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
+ StatusCode = [HttpStatusCode]::OK
+ Body = @($ScheduledTasks)
+ })
\ No newline at end of file
diff --git a/ListSharepointQuota/function.json b/ListSharepointQuota/function.json
new file mode 100644
index 000000000000..925eab5aeae1
--- /dev/null
+++ b/ListSharepointQuota/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/ListSharepointQuota/run.ps1 b/ListSharepointQuota/run.ps1
new file mode 100644
index 000000000000..f4ec77408445
--- /dev/null
+++ b/ListSharepointQuota/run.ps1
@@ -0,0 +1,42 @@
+using namespace System.Net
+
+# Input bindings are passed in via param block.
+param($Request, $TriggerMetadata)
+
+$APIName = $TriggerMetadata.FunctionName
+Write-LogMessage -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
+
+if ($Request.Query.TenantFilter -eq 'AllTenants') {
+ $UsedStoragePercentage = 'Not Supported'
+} else {
+ $tenantName = (New-GraphGetRequest -uri "https://graph.microsoft.com/beta/domains" -tenantid $TenantFilter | Where-Object { $_.isInitial -eq $true }).id.Split(".")[0]
+
+ $sharepointToken = (Get-GraphToken -scope "https://$($tenantName)-admin.sharepoint.com/.default" -tenantid $TenantFilter)
+ $sharepointToken.Add('accept','application/json')
+ # Implement a try catch later to deal with sharepoint guest user settings
+ $sharepointQuota = (Invoke-RestMethod -Method "GET" -Headers $sharepointToken -Uri "https://$($tenantName)-admin.sharepoint.com/_api/StorageQuotas()?api-version=1.3.2" -ErrorAction Stop).value
+ if ($sharepointQuota) {
+ $UsedStoragePercentage = [int](($sharepointQuota.GeoUsedStorageMB / $sharepointQuota.TenantStorageMB) * 100)
+ }
+}
+
+$sharepointQuotaDetails = @{
+ GeoUsedStorageMB = $sharepointQuota.GeoUsedStorageMB
+ TenantStorageMB = $sharepointQuota.TenantStorageMB
+ Percentage = $UsedStoragePercentage
+ Dashboard = "$($UsedStoragePercentage) / 100"
+}
+
+$StatusCode = [HttpStatusCode]::OK
+
+# Associate values to output bindings by calling 'Push-OutputBinding'.
+Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
+ StatusCode = $StatusCode
+ Body = $sharepointQuotaDetails
+ })
\ No newline at end of file
diff --git a/Modules/CIPPCore/CIPPCore.psm1 b/Modules/CIPPCore/CIPPCore.psm1
new file mode 100644
index 000000000000..34e402aa7101
--- /dev/null
+++ b/Modules/CIPPCore/CIPPCore.psm1
@@ -0,0 +1,13 @@
+$Public = @(Get-ChildItem -Path $PSScriptRoot\Public\*.ps1 -ErrorAction SilentlyContinue)
+$Private = @(Get-ChildItem -Path $PSScriptRoot\private\*.ps1 -ErrorAction SilentlyContinue)
+$Functions = $Public + $Private
+foreach ($import in @($Functions)) {
+ try {
+ . $import.FullName
+ }
+ catch {
+ Write-Error -Message "Failed to import function $($import.FullName): $_"
+ }
+}
+
+Export-ModuleMember -Function $Public.BaseName -Alias *
diff --git a/Modules/CIPPCore/CippCore.psd1 b/Modules/CIPPCore/CippCore.psd1
new file mode 100644
index 000000000000..71c567254d51
Binary files /dev/null and b/Modules/CIPPCore/CippCore.psd1 differ
diff --git a/Modules/CIPPCore/Public/Remove-CIPPGroups.ps1 b/Modules/CIPPCore/Public/Remove-CIPPGroups.ps1
new file mode 100644
index 000000000000..26a3961b9d74
--- /dev/null
+++ b/Modules/CIPPCore/Public/Remove-CIPPGroups.ps1
@@ -0,0 +1,40 @@
+function Remove-CIPPGroups {
+ [CmdletBinding()]
+ param(
+ $userid,
+ $tenantFilter,
+ $APIName = "Remove From Groups",
+ $ExecutingUser
+ )
+
+ $AllGroups = (New-GraphGetRequest -uri "https://graph.microsoft.com/beta/groups/?$select=DisplayName,mailEnabled" -tenantid $tenantFilter)
+
+ (New-GraphPostRequest -uri "https://graph.microsoft.com/beta/users/$($userid)/GetMemberGroups" -tenantid $tenantFilter -type POST -body '{"securityEnabledOnly": false}').value | ForEach-Object -Parallel {
+ Import-Module '.\GraphHelper.psm1'
+ $group = $_
+
+ try {
+ $Groupname = ($using:AllGroups | Where-Object -Property id -EQ $group).displayName
+ $IsMailEnabled = ($using:AllGroups | Where-Object -Property id -EQ $group).mailEnabled
+ $IsM365Group = ($using:AllGroups | Where-Object { $_.id -eq $group -and $_.groupTypes -contains "Unified" }) -ne $null
+
+ if ($IsM365Group) {
+ $RemoveRequest = New-GraphPostRequest -uri "https://graph.microsoft.com/beta/groups/$_/members/$($using:userid)/`$ref" -tenantid $using:tenantFilter -type DELETE -body '' -Verbose
+ }
+ elseif (-not $IsMailEnabled) {
+ $RemoveRequest = New-GraphPostRequest -uri "https://graph.microsoft.com/beta/groups/$_/members/$($using:userid)/`$ref" -tenantid $using:tenantFilter -type DELETE -body '' -Verbose
+ }
+ elseif ($IsMailEnabled) {
+ $Params = @{ Identity = $Groupname; Member = $using:userid ; BypassSecurityGroupManagerCheck = $true }
+ New-ExoRequest -tenantid $using:tenantFilter -cmdlet "Remove-DistributionGroupMember" -cmdParams $params -UseSystemMailbox $true
+ }
+
+ Write-LogMessage -user $using:ExecutingUser -API $($using:APIName) -message "Removed $($using:userid) from $groupname" -Sev "Info" -tenant $using:TenantFilter
+ return "Successfully removed user from group $Groupname"
+ }
+ catch {
+ Write-LogMessage -user $using:ExecutingUser -API $($using:APIName) -message "Could not remove $($using:userid) from group $groupname" -Sev "Error" -tenant $using:TenantFilter
+ return "Could not remove user from group $($Groupname): $($_.Exception.Message). This is likely because its a Dynamic Group or synched with active directory"
+ }
+ }
+}
diff --git a/Modules/CIPPCore/Public/Remove-CIPPLicense.ps1 b/Modules/CIPPCore/Public/Remove-CIPPLicense.ps1
new file mode 100644
index 000000000000..a0c8ae94091c
--- /dev/null
+++ b/Modules/CIPPCore/Public/Remove-CIPPLicense.ps1
@@ -0,0 +1,24 @@
+function Remove-CIPPLicense {
+ [CmdletBinding()]
+ param (
+ $ExecutingUser,
+ $userid,
+ $username,
+ $APIName = "Remove License",
+ $TenantFilter
+ )
+
+ try {
+ $CurrentLicenses = (New-GraphGetRequest -uri "https://graph.microsoft.com/beta/users/$($userid)" -tenantid $tenantFilter).assignedlicenses.skuid
+ $LicensesToRemove = if ($CurrentLicenses) { ConvertTo-Json @( $CurrentLicenses) } else { "[]" }
+ $LicenseBody = '{"addLicenses": [], "removeLicenses": ' + $LicensesToRemove + '}'
+ $LicRequest = New-GraphPostRequest -uri "https://graph.microsoft.com/beta/users/$($userid)/assignlicense" -tenantid $tenantFilter -type POST -body $LicenseBody -verbose
+ Write-LogMessage -user $ExecutingUser -API $APIName -message "Removed license for $($username)" -Sev "Info" -tenant $TenantFilter
+ Return "Removed current licenses: $(($ConvertTable | Where-Object { $_.guid -in $CurrentLicenses }).'Product_Display_Name' -join ',')"
+
+ }
+ catch {
+ Write-LogMessage -user $ExecutingUser -API $APIName -message "Could not remove license for $username" -Sev "Error" -tenant $TenantFilter
+ return "Could not remove license for $($username). Error: $($_.Exception.Message)"
+ }
+}
diff --git a/Modules/CIPPCore/Public/Remove-CIPPMobileDevice.ps1 b/Modules/CIPPCore/Public/Remove-CIPPMobileDevice.ps1
new file mode 100644
index 000000000000..78625e367fbd
--- /dev/null
+++ b/Modules/CIPPCore/Public/Remove-CIPPMobileDevice.ps1
@@ -0,0 +1,29 @@
+function Remove-CIPPMobileDevice {
+ [CmdletBinding()]
+ param(
+ $userid,
+ $tenantFilter,
+ $username,
+ $APIName = "Remove Mobile",
+ $ExecutingUser
+ )
+
+ try {
+ $devices = New-ExoRequest -tenantid $tenantFilter -cmdlet "Get-MobileDevice" -Anchor $username -cmdParams @{mailbox = $userid } | ForEach-Object {
+ try {
+ New-ExoRequest -tenantid $tenantFilter -cmdlet "Remove-MobileDevice" -Anchor $username -cmdParams @{Identity = $_.Identity }
+ "Removed device: $($_.FriendlyName)"
+ }
+ catch {
+ "Could not remove device: $($_.FriendlyName)"
+ continue
+ }
+ }
+
+ Write-LogMessage -user $ExecutingUser -API $APIName -message "Deleted mobile devices for $($username)" -Sev "Info" -tenant $tenantFilter
+ }
+ catch {
+ Write-LogMessage -user $ExecutingUser -API $APIName -message "Could not delete mobile devices for $($username): $($_.Exception.Message)" -Sev "Error" -tenant $tenantFilter
+ return "Could not delete mobile devices for $($username). Error: $($_.Exception.Message)"
+ }
+}
diff --git a/Modules/CIPPCore/Public/Remove-CIPPRules.ps1 b/Modules/CIPPCore/Public/Remove-CIPPRules.ps1
new file mode 100644
index 000000000000..207e122fac47
--- /dev/null
+++ b/Modules/CIPPCore/Public/Remove-CIPPRules.ps1
@@ -0,0 +1,29 @@
+function Remove-CIPPRules {
+ [CmdletBinding()]
+ param (
+ $userid,
+ $username,
+ $TenantFilter,
+ $APIName = "Rules Removal",
+ $ExecutingUser
+ )
+
+ try {
+ $rules = New-ExoRequest -tenantid $TenantFilter -cmdlet "Get-InboxRule" -cmdParams @{mailbox = $userid }
+ if ($rules -eq $null) {
+ Write-LogMessage -user $ExecutingUser -API $APIName -message "No Rules for $($userid) to delete" -Sev "Info" -tenant $TenantFilter
+ return "No rules for $($userid) to delete"
+ }
+ else {
+ ForEach ($rule in $rules) {
+ New-ExoRequest -tenantid $TenantFilter -cmdlet "Remove-InboxRule" -Anchor $userid -cmdParams @{Identity = $rule.Identity }
+ }
+ Write-LogMessage -user $ExecutingUser -API $APIName -message "Deleted Rules for $($userid)" -Sev "Info" -tenant $TenantFilter
+ return "Deleted Rules for $($userid)"
+ }
+ }
+ catch {
+ Write-LogMessage -user $ExecutingUser -API $APIName -message "Could not delete rules for $($userid): $($_.Exception.Message)" -Sev "Error" -tenant $TenantFilter
+ return "Could not delete rules for $($userid). Error: $($_.Exception.Message)"
+ }
+}
diff --git a/Modules/CIPPCore/Public/Remove-CIPPUser.ps1 b/Modules/CIPPCore/Public/Remove-CIPPUser.ps1
new file mode 100644
index 000000000000..788a4e3332d3
--- /dev/null
+++ b/Modules/CIPPCore/Public/Remove-CIPPUser.ps1
@@ -0,0 +1,22 @@
+function Remove-CIPPUser {
+ [CmdletBinding()]
+ param (
+ $ExecutingUser,
+ $userid,
+ $username,
+ $APIName = "Remove User",
+ $TenantFilter
+ )
+
+ try {
+ $DeleteRequest = New-GraphPostRequest -uri "https://graph.microsoft.com/beta/users/$($userid)" -type DELETE -tenant $TenantFilter
+ Write-LogMessage -user $ExecutingUser, -API $APIName -message "Deleted account $username" -Sev "Info" -tenant $TenantFilter
+ return "Deleted the user account $username"
+
+ }
+ catch {
+ Write-LogMessage -user $ExecutingUser, -API $APIName -message "Could not delete $username" -Sev "Error" -tenant $TenantFilter
+ return "Could not delete $username. Error: $($_.Exception.Message)"
+ }
+}
+
diff --git a/Modules/CIPPCore/Public/Revoke-CIPPSessions.ps1 b/Modules/CIPPCore/Public/Revoke-CIPPSessions.ps1
new file mode 100644
index 000000000000..6ee7b05d5686
--- /dev/null
+++ b/Modules/CIPPCore/Public/Revoke-CIPPSessions.ps1
@@ -0,0 +1,21 @@
+function Revoke-CIPPSessions {
+ [CmdletBinding()]
+ param (
+ $ExecutingUser,
+ $userid,
+ $username,
+ $APIName = "Revoke Sessions",
+ $TenantFilter
+ )
+
+ try {
+ $GraphRequest = New-GraphPostRequest -uri "https://graph.microsoft.com/beta/users/$($userid)/invalidateAllRefreshTokens" -tenantid $TenantFilter -type POST -body '{}' -verbose
+ Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APIName -message "Revoked sessions for $($username)" -Sev "Info" -tenant $TenantFilter
+ return "Success. All sessions by $username have been revoked"
+
+ }
+ catch {
+ Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APIName -message "Revoked sessions for $($username)" -Sev "Info" -tenant $TenantFilter
+ return "Revoke Session Failed: $($_.Exception.Message)"
+ }
+}
diff --git a/Modules/CIPPCore/Public/Set-CIPPForwarding.ps1 b/Modules/CIPPCore/Public/Set-CIPPForwarding.ps1
new file mode 100644
index 000000000000..909240cdd596
--- /dev/null
+++ b/Modules/CIPPCore/Public/Set-CIPPForwarding.ps1
@@ -0,0 +1,22 @@
+function Set-CIPPForwarding {
+ [CmdletBinding()]
+ param(
+ $userid,
+ $tenantFilter,
+ $username,
+ $ExecutingUser,
+ $APIName = "Forwarding",
+ $Forward,
+ $KeepCopy
+ )
+
+ try {
+ $permissions = New-ExoRequest -tenantid $tenantFilter -cmdlet "Set-mailbox" -cmdParams @{Identity = $userid; ForwardingAddress = $Forward ; DeliverToMailboxAndForward = [bool]$KeepCopy } -Anchor $username
+ "Forwarding all email for $username to $Forward"
+ Write-LogMessage -user $ExecutingUser -API $APIName -message "Set Forwarding for $($username) to $Forward" -Sev "Info" -tenant $TenantFilter
+ }
+ catch {
+ Write-LogMessage -user $ExecutingUser -API $APIName -message "Could not add forwarding for $($username)" -Sev "Error" -tenant $TenantFilter
+ return "Could not add forwarding for $($username). Error: $($_.Exception.Message)"
+ }
+}
diff --git a/Modules/CIPPCore/Public/Set-CIPPHideFromGAL.ps1 b/Modules/CIPPCore/Public/Set-CIPPHideFromGAL.ps1
new file mode 100644
index 000000000000..601288f97dd4
--- /dev/null
+++ b/Modules/CIPPCore/Public/Set-CIPPHideFromGAL.ps1
@@ -0,0 +1,23 @@
+function Set-CIPPHideFromGAL {
+ [CmdletBinding()]
+ param (
+ $userid,
+ $tenantFilter,
+ $APIName = "Hide From Address List",
+ [bool]$HideFromGAL,
+ $ExecutingUser
+ )
+
+ try {
+ $body = @{
+ showInAddressList = [bool]$HideFromGAL
+ } | ConvertTo-Json -Compress -Depth 1
+ $HideRequest = New-GraphPostRequest -uri "https://graph.microsoft.com/v1.0/users/$($userid)" -tenantid $tenantFilter -type PATCH -body $body -verbose
+ Write-LogMessage -user $ExecutingUser -API $APIName -message "Hid $($userid) from address list" -Sev "Info" -tenant $TenantFilter
+ return "Hidden $($userid) from address list"
+ }
+ catch {
+ Write-LogMessage -user $ExecutingUser -API $APIName -message "Could not hide $($userid) from address list" -Sev "Error" -tenant $TenantFilter
+ return "Could not hide $($userid) from address list. Error: $($_.Exception.Message)"
+ }
+}
diff --git a/Modules/CIPPCore/Public/Set-CIPPMailboxAccess.ps1 b/Modules/CIPPCore/Public/Set-CIPPMailboxAccess.ps1
new file mode 100644
index 000000000000..f19762545ac1
--- /dev/null
+++ b/Modules/CIPPCore/Public/Set-CIPPMailboxAccess.ps1
@@ -0,0 +1,29 @@
+function Set-CIPPMailboxAccess {
+ [CmdletBinding()]
+ param (
+ $userid,
+ $AccessUser,
+ [bool]$Automap,
+ $TenantFilter,
+ $APIName = "Manage Shared Mailbox Access",
+ $ExecutingUser,
+ [array]$AccessRights
+ )
+
+ try {
+ $permissions = New-ExoRequest -tenantid $TenantFilter -cmdlet "Add-MailboxPermission" -cmdParams @{Identity = $userid; user = $AccessUser; automapping = $Automap; accessRights = $AccessRights; InheritanceType = "all" } -Anchor $userid
+
+ if ($Automap) {
+ Write-LogMessage -user $ExecutingUser -API $APIName -message "Gave $AccessRights permissions to $($AccessUser) on $($userid) with automapping" -Sev "Info" -tenant $TenantFilter
+ return "added $($AccessUser) to $($userid) Shared Mailbox with automapping, with the following permissions: $AccessRights"
+ }
+ else {
+ Write-LogMessage -user $ExecutingUser -API $APIName -message "Gave $AccessRights permissions to $($AccessUser) on $($userid) without automapping" -Sev "Info" -tenant $TenantFilter
+ return "added $($AccessUser) to $($userid) Shared Mailbox without automapping, with the following permissions: $AccessRights"
+ }
+ }
+ catch {
+ Write-LogMessage -user $ExecutingUser -API $APIName -message "Could not add mailbox permissions for $($AccessUser) on $($userid)" -Sev "Error" -tenant $TenantFilter
+ return "Could not add shared mailbox permissions for $($userid). Error: $($_.Exception.Message)"
+ }
+}
diff --git a/Modules/CIPPCore/Public/Set-CIPPMailboxType.ps1 b/Modules/CIPPCore/Public/Set-CIPPMailboxType.ps1
new file mode 100644
index 000000000000..e559c4557711
--- /dev/null
+++ b/Modules/CIPPCore/Public/Set-CIPPMailboxType.ps1
@@ -0,0 +1,22 @@
+function Set-CIPPMailboxType {
+ [CmdletBinding()]
+ param (
+ $ExecutingUser,
+ $userid,
+ $username,
+ $APIName = "Mailbox Conversion",
+ $TenantFilter,
+ [Parameter()]
+ [ValidateSet('shared', 'Regular', 'Room', 'Equipment')]$MailboxType
+ )
+
+ try {
+ $Mailbox = New-ExoRequest -tenantid $TenantFilter -cmdlet "Set-mailbox" -cmdParams @{Identity = $userid; type = $MailboxType } -Anchor $username
+ Write-LogMessage -user $ExecutingUser -API $APIName -message "Converted $($username) to a $MailboxType mailbox" -Sev "Info" -tenant $TenantFilter
+ return "Converted $($username) to a $MailboxType mailbox"
+ }
+ catch {
+ Write-LogMessage -user $ExecutingUser -API $APIName -message "Could not convert $username to $MailboxType mailbox" -Sev "Error" -tenant $TenantFilter
+ return "Could not convert $($username) to a $MailboxType mailbox. Error: $($_.Exception.Message)"
+ }
+}
diff --git a/Modules/CIPPCore/Public/Set-CIPPOneDriveAccess.ps1 b/Modules/CIPPCore/Public/Set-CIPPOneDriveAccess.ps1
new file mode 100644
index 000000000000..a576f0b8257c
--- /dev/null
+++ b/Modules/CIPPCore/Public/Set-CIPPOneDriveAccess.ps1
@@ -0,0 +1,46 @@
+function Set-CIPPOnedriveAccess {
+ [CmdletBinding()]
+ param (
+ $userid,
+ $OnedriveAccessUser,
+ $TenantFilter,
+ $APIName = "Manage OneDrive Access",
+ $ExecutingUser
+ )
+
+ try {
+ $URL = (New-GraphGetRequest -uri "https://graph.microsoft.com/beta/reports/getOneDriveUsageAccountDetail(period='D7')?`$format=application/json" -tenantid $TenantFilter | Where-Object -Property 'OwnerPrincipalName' -EQ $userid).siteUrl
+ $OnMicrosoft = (New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/domains?$top=999' -tenantid $TenantFilter | Where-Object -Property isInitial -EQ $true).id.split('.') | Select-Object -First 1
+ $AdminUrl = "https://$($OnMicrosoft)-admin.sharepoint.com"
+ $XML = @"
+
+
+
+
+
+
+
+ $URL
+ $OnedriveAccessUser
+ true
+
+
+
+
+
+"@
+ $request = New-GraphPostRequest -scope "$AdminURL/.default" -tenantid $TenantFilter -Uri "$AdminURL/_vti_bin/client.svc/ProcessQuery" -Type POST -Body $XML -ContentType 'text/xml'
+ if (!$request.ErrorInfo.ErrorMessage) {
+ Write-LogMessage -user $ExecutingUser -API $APIName -message "Gave $($OnedriveAccessUser) access to $($userid) OneDrive" -Sev "Info" -tenant $TenantFilter
+ return "User's OneDrive URL is $URL. Access has been given to $($OnedriveAccessUser)"
+ }
+ else {
+ Write-LogMessage -user $ExecutingUser -API $APIName -message "Failed to give OneDrive Access: $($request.ErrorInfo.ErrorMessage)" -Sev "Info" -tenant $TenantFilter
+ return "Failed to give OneDrive Access: $($request.ErrorInfo.ErrorMessage)"
+ }
+ }
+ catch {
+ Write-LogMessage -user $ExecutingUser -API $APIName -message "Could not add new owner to OneDrive $($OnedriveAccessUser) on $($userid)" -Sev "Error" -tenant $TenantFilter
+ return "Could not add owner to OneDrive for $($userid). Error: $($_.Exception.Message)"
+ }
+}
diff --git a/Modules/CIPPCore/Public/Set-CIPPOutOfoffice.ps1 b/Modules/CIPPCore/Public/Set-CIPPOutOfoffice.ps1
new file mode 100644
index 000000000000..5413ec5261e9
--- /dev/null
+++ b/Modules/CIPPCore/Public/Set-CIPPOutOfoffice.ps1
@@ -0,0 +1,20 @@
+function Set-CIPPOutOfOffice {
+ [CmdletBinding()]
+ param (
+ $userid,
+ $OOO,
+ $TenantFilter,
+ $APIName = "Set Out of Office",
+ $ExecutingUser
+ )
+
+ try {
+ $permissions = New-ExoRequest -tenantid $TenantFilter -cmdlet "Set-MailboxAutoReplyConfiguration" -cmdParams @{Identity = $userid; AutoReplyState = "Enabled"; InternalMessage = $OOO; ExternalMessage = $OOO } -Anchor $userid
+ Write-LogMessage -user $ExecutingUser -API $APIName -message "Set Out-of-office for $($userid)" -Sev "Info" -tenant $TenantFilter
+ return "added Out-of-office to $($userid)"
+ }
+ catch {
+ Write-LogMessage -user $ExecutingUser -API $APIName -message "Could not add OOO for $($userid)" -Sev "Error" -tenant $TenantFilter
+ return "Could not add out of office message for $($userid). Error: $($_.Exception.Message)"
+ }
+}
diff --git a/Modules/CIPPCore/Public/Set-CIPPResetPassword.ps1 b/Modules/CIPPCore/Public/Set-CIPPResetPassword.ps1
new file mode 100644
index 000000000000..1d9c06c2d859
--- /dev/null
+++ b/Modules/CIPPCore/Public/Set-CIPPResetPassword.ps1
@@ -0,0 +1,29 @@
+function Set-CIPPResetPassword {
+ [CmdletBinding()]
+ param(
+ $userid,
+ $tenantFilter,
+ $APIName = "Reset Password",
+ $ExecutingUser,
+ [bool]$forceChangePasswordNextSignIn = $true
+ )
+
+ try {
+ $password = New-passwordString
+ $passwordProfile = @{
+ "passwordProfile" = @{
+ "forceChangePasswordNextSignIn" = $forceChangePasswordNextSignIn
+ "password" = $password
+ }
+ } | ConvertTo-Json -Compress
+
+ $GraphRequest = New-GraphPostRequest -uri "https://graph.microsoft.com/v1.0/users/$($userid)" -tenantid $TenantFilter -type PATCH -body $passwordProfile -verbose
+
+ Write-LogMessage -user $ExecutingUser -API $APIName -message "Reset the password for $($userid)" -Sev "Info" -tenant $TenantFilter
+ return "The new password is $password"
+ }
+ catch {
+ Write-LogMessage -user $ExecutingUser -API $APIName -message "Could not reset password for $($userid)" -Sev "Error" -tenant $TenantFilter
+ return "Could not reset password for $($userid). Error: $($_.Exception.Message)"
+ }
+}
diff --git a/Modules/CIPPCore/Public/Set-CIPPSignInState.ps1 b/Modules/CIPPCore/Public/Set-CIPPSignInState.ps1
new file mode 100644
index 000000000000..bc45d8588979
--- /dev/null
+++ b/Modules/CIPPCore/Public/Set-CIPPSignInState.ps1
@@ -0,0 +1,23 @@
+function Set-CIPPSignInState {
+ [CmdletBinding()]
+ param (
+ $userid,
+ [bool]$AccountEnabled,
+ $TenantFilter,
+ $APIName = "Disable User Sign-in",
+ $ExecutingUser
+ )
+
+ try {
+ $body = @{
+ accountEnabled = [bool]$AccountEnabled
+ } | ConvertTo-Json -Compress -Depth 1
+ $SignInState = New-GraphPostRequest -uri "https://graph.microsoft.com/v1.0/users/$($userid)" -tenantid $TenantFilter -type PATCH -body $body -verbose
+ Write-LogMessage -user $ExecutingUser -API $APIName -message "Disabled $($userid)" -Sev "Info" -tenant $TenantFilter
+ return "Disabled user account for $userid"
+ }
+ catch {
+ Write-LogMessage -user $ExecutingUser -API $APIName -message "Could not disable sign in for $($userid)" -Sev "Error" -tenant $TenantFilter
+ return "Could not disable $($userid). Error: $($_.Exception.Message)"
+ }
+}
diff --git a/Modules/CippEntrypoints/CippEntrypoints.psm1 b/Modules/CippEntrypoints/CippEntrypoints.psm1
new file mode 100644
index 000000000000..b62858046169
--- /dev/null
+++ b/Modules/CippEntrypoints/CippEntrypoints.psm1
@@ -0,0 +1,64 @@
+using namespace System.Net
+
+function Receive-CippHttpTrigger {
+ Param($Request, $TriggerMetadata)
+
+ $APIName = $TriggerMetadata.FunctionName
+
+ $FunctionVerbs = @{
+ 'Get' = '^(:?Ext)?(?List.+$)'
+ 'Update' = '^(:?Ext)?(?Edit.+$)'
+ 'New' = '^(:?Ext)?(?Add.+$)'
+ 'Invoke' = '^(?Exec.+$)'
+ }
+
+ $PermissionActions = @{
+ 'List' = 'readonly'
+ 'Edit' = 'editor'
+ 'Add' = 'editor'
+ 'Exec' = 'admin'
+ }
+
+ foreach ($FunctionVerb in $FunctionVerbs.Keys) {
+ if ($APIName -match $FunctionVerbs.$FunctionVerb) {
+ $FunctionName = '{0}-{1}' -f $FunctionVerb, $Matches.APIName
+ $ApiFunction = $Matches.APIName
+
+ foreach ($Action in $PermissionActions.Keys) {
+ if ($ApiFunction -match "^$Action") {
+ $ApiPermission = $PermissionActions.$Action
+ break
+ }
+ }
+ break
+ }
+ }
+
+ if ($APIName -match '^Ext') {
+ $AccessResult = Confirm-CippApiAccess -Request $Request -AccessLevel $ApiPermission
+ if (!$AccessResult.Authorized) { return }
+ }
+
+ $HttpTrigger = @{
+ Request = $Request
+ TriggerMetadata = $TriggerMetadata
+ }
+
+ & $FunctionName @HttpTrigger
+}
+
+function Receive-CippQueueTrigger {
+ Param($QueueItem, $TriggerMetadata)
+ $APIName = $TriggerMetadata.FunctionName
+
+ $FunctionName = 'Push-{0}' -f $APIName
+ $QueueTrigger = @{
+ QueueItem = $QueueItem
+ TriggerMetadata = $TriggerMetadata
+ }
+
+ & $FunctionName @QueueTrigger
+}
+
+Export-ModuleMember -Function @('Receive-CippHttpTrigger', 'Receive-CippQueueTrigger')
+
diff --git a/Modules/CippExtensions/CippExtensions.psd1 b/Modules/CippExtensions/CippExtensions.psd1
index 3b3a16f620c0..29102a2610bc 100644
Binary files a/Modules/CippExtensions/CippExtensions.psd1 and b/Modules/CippExtensions/CippExtensions.psd1 differ
diff --git a/Modules/CippExtensions/CippExtensions.psm1 b/Modules/CippExtensions/CippExtensions.psm1
index e0ededbec9b8..0b892b97d986 100644
--- a/Modules/CippExtensions/CippExtensions.psm1
+++ b/Modules/CippExtensions/CippExtensions.psm1
@@ -1,12 +1,12 @@
-$Functions = @(Get-ChildItem -Path $PSScriptRoot\Public\*.ps1 -ErrorAction SilentlyContinue) + @(Get-ChildItem -Path $PSScriptRoot\private\*.ps1 -ErrorAction SilentlyContinue)
-foreach ($import in @($Functions))
-{
- try
- {
+$Public = @(Get-ChildItem -Path $PSScriptRoot\Public\*.ps1 -ErrorAction SilentlyContinue)
+$Private = @(Get-ChildItem -Path $PSScriptRoot\private\*.ps1 -ErrorAction SilentlyContinue)
+$Functions = $Public + $Private
+foreach ($import in @($Functions)) {
+ try {
. $import.FullName
- }
- catch
- {
+ } catch {
Write-Error -Message "Failed to import function $($import.FullName): $_"
}
-}
\ No newline at end of file
+}
+
+Export-ModuleMember -Function $Functions.BaseName -Alias *
diff --git a/Modules/CippExtensions/Private/Get-ApiSecretHash.ps1 b/Modules/CippExtensions/Private/Get-ApiSecretHash.ps1
new file mode 100644
index 000000000000..12f996a15ac9
--- /dev/null
+++ b/Modules/CippExtensions/Private/Get-ApiSecretHash.ps1
@@ -0,0 +1,8 @@
+function Get-ApiSecretHash {
+ Param($Secret)
+ $StringBuilder = New-Object System.Text.StringBuilder
+ [System.Security.Cryptography.HashAlgorithm]::Create('SHA256').ComputeHash([System.Text.Encoding]::UTF8.GetBytes($Secret)) | ForEach-Object {
+ [Void]$StringBuilder.Append($_.ToString('x2'))
+ }
+ $StringBuilder.ToString()
+}
diff --git a/Modules/CippExtensions/Private/New-CippApiKey.ps1 b/Modules/CippExtensions/Private/New-CippApiKey.ps1
new file mode 100644
index 000000000000..19574b230442
--- /dev/null
+++ b/Modules/CippExtensions/Private/New-CippApiKey.ps1
@@ -0,0 +1,27 @@
+function New-CippApiKey {
+ Param(
+ [Parameter(Mandatory = $true)]
+ $Description,
+ [ValidateSet('readonly', 'editor')]
+ $AccessLevel = 'readonly'
+ )
+
+ $ClientId = [System.String]([System.Guid]::NewGuid()).Guid
+ $ClientSecret = '{0}{1}' -f ([System.Guid]::NewGuid()).Guid, ([System.Guid]::NewGuid()).Guid
+ $ClientSecretHash = Get-ApiSecretHash -Secret $ClientSecret
+
+ $ApiAccessObject = [PSCustomObject]@{
+ PartitionKey = 'ApiToken'
+ RowKey = $ClientId
+ ClientSecret = $ClientSecret
+ ClientSecretHash = $ClientSecretHash
+ Description = $Description
+ AccessLevel = $AccessLevel
+ }
+
+ $Entity = $ApiAccessObject | Select-Object PartitionKey, RowKey, ClientSecretHash, Description, AccessLevel
+ $Table = Get-CippTable -TableName 'CippApiCredentials'
+ Add-AzDataTableEntity @Table -Force -Entity $Entity
+
+ $ApiAccessObject | Select-Object @{n = 'ClientId'; e = { $_.RowKey } }, ClientSecret, Description, AccessLevel
+}
diff --git a/Modules/CippExtensions/Public/Confirm-CippApiAccess.ps1 b/Modules/CippExtensions/Public/Confirm-CippApiAccess.ps1
new file mode 100644
index 000000000000..0a7e533c4954
--- /dev/null
+++ b/Modules/CippExtensions/Public/Confirm-CippApiAccess.ps1
@@ -0,0 +1,46 @@
+function Confirm-CippApiAccess {
+ Param(
+ $Request,
+ $AccessLevel = 'readonly'
+ )
+ $AuthRequest = [PSCustomObject]@{
+ ClientId = 'None'
+ Authorized = $false
+ }
+
+ $PermissionMap = @{
+ 'editor' = @(
+ 'editor',
+ 'readonly'
+ )
+ 'readonly' = @(
+ 'readonly'
+ )
+ }
+
+ $Auth = $Request.Headers.authorization
+ if ($Auth -match '^Basic (?.+)$') {
+ try {
+ $ClientId, $ClientSecret = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($Matches.Creds)) -split ':'
+ $AuthRequest.ClientId = $ClientId
+ $Filter = "PartitionKey eq 'ApiToken' and RowKey eq '{0}' and ClientSecretHash eq '{1}'" -f $ClientId, (Get-ApiSecretHash -Secret $ClientSecret)
+ $Table = Get-CippTable -TableName CippApiCredentials
+ $Entity = Get-AzDataTableEntity @Table -Filter $Filter
+
+ if ($Entity.RowKey -and $PermissionMap[$Entity.AccessLevel] -contains $AccessLevel) {
+ $AuthRequest.Authorized = $true
+ }
+ } catch {
+ Write-Host "API key validation exception: $($_.Exception.Message)"
+ }
+ }
+
+ if (!$AuthRequest.Authorized) {
+ Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
+ StatusCode = [HttpStatusCode]::Unauthorized
+ Body = 'Unauthorized'
+ })
+ }
+
+ $AuthRequest
+}
diff --git a/Modules/CippExtensions/Public/Invoke-ExecAddCippApiKey.ps1 b/Modules/CippExtensions/Public/Invoke-ExecAddCippApiKey.ps1
new file mode 100644
index 000000000000..a9b286f2d0fa
--- /dev/null
+++ b/Modules/CippExtensions/Public/Invoke-ExecAddCippApiKey.ps1
@@ -0,0 +1,26 @@
+function Add-CippApiKey {
+ # Input bindings are passed in via param block.
+ param($Request = $null, $TriggerMetadata)
+
+ if ($Request) {
+ $APIName = $TriggerMetadata.FunctionName
+ Write-LogMessage -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.'
+ }
+
+ try {
+ $ApiKey = New-CippApiKey -Description $Request.Query.Description -AccessLevel $Request.Query.AccessLevel
+ Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
+ StatusCode = [HttpStatusCode]::OK
+ Body = $ApiKey
+ })
+ } catch {
+ Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
+ StatusCode = [HttpStatusCode]::BadRequest
+ Body = @{Results = $_.Exception.Message }
+ })
+ }
+
+}
diff --git a/Modules/DNSHealth/1.0.5/DNSHealth.psd1 b/Modules/DNSHealth/1.0.6/DNSHealth.psd1
similarity index 98%
rename from Modules/DNSHealth/1.0.5/DNSHealth.psd1
rename to Modules/DNSHealth/1.0.6/DNSHealth.psd1
index e0e645f60e48..257d08264fb1 100644
--- a/Modules/DNSHealth/1.0.5/DNSHealth.psd1
+++ b/Modules/DNSHealth/1.0.6/DNSHealth.psd1
@@ -3,7 +3,7 @@
#
# Generated by: John Duprey
#
-# Generated on: 04/12/2023
+# Generated on: 06/15/2023
#
@{
@@ -12,7 +12,7 @@
RootModule = 'DNSHealth.psm1'
# Version number of this module.
-ModuleVersion = '1.0.5'
+ModuleVersion = '1.0.6'
# Supported PSEditions
# CompatiblePSEditions = @()
diff --git a/Modules/DNSHealth/1.0.5/DNSHealth.psm1 b/Modules/DNSHealth/1.0.6/DNSHealth.psm1
similarity index 99%
rename from Modules/DNSHealth/1.0.5/DNSHealth.psm1
rename to Modules/DNSHealth/1.0.6/DNSHealth.psm1
index 9e955dddbd54..253a66b1d0be 100644
--- a/Modules/DNSHealth/1.0.5/DNSHealth.psm1
+++ b/Modules/DNSHealth/1.0.6/DNSHealth.psm1
@@ -2050,6 +2050,7 @@ function Read-WhoisRecord {
# Whois parser, generic Property: Value format with some multi-line support and comment handlers
$WhoisRegex = '^(?!(?:%|>>>|-+|#|[*]))[^\S\n]*(?.+?):(?:[\r\n]+)?(:?(?!([0-9]|[/]{2}))[^\S\r\n]*(?.+))?$'
+ Write-Verbose "Querying WHOIS Server: $Server"
# TCP Client for Whois
$Client = New-Object System.Net.Sockets.TcpClient($Server, 43)
try {
@@ -2125,7 +2126,7 @@ function Read-WhoisRecord {
$LastResult = $Results
try {
$Results = Read-WhoisRecord -Query $Query -Server $ReferralServer -Port $Port
- if ($Results._Raw -Match '(No match|Not Found|No Data|The queried object does not exist)' -and $TopLevelReferrers -notcontains $Server) {
+ if ([string]::IsNullOrEmpty($Results._Raw) -or ($Results._Raw -Match '(No match|Not Found|No Data|The queried object does not exist)' -and $TopLevelReferrers -notcontains $Server)) {
$Results = $LastResult
} else {
foreach ($s in $Results._ReferralServers) {
@@ -2172,7 +2173,7 @@ function Read-WhoisRecord {
# Return Whois results as PSObject
$WhoisResults
}
-#EndRegion './Public/Records/Read-WhoisRecord.ps1' 174
+#EndRegion './Public/Records/Read-WhoisRecord.ps1' 175
#Region './Public/Resolver/Resolve-DnsHttpsQuery.ps1' 0
function Resolve-DnsHttpsQuery {
<#
diff --git a/Modules/DNSHealth/1.0.5/MailProviders/AppRiver.json b/Modules/DNSHealth/1.0.6/MailProviders/AppRiver.json
similarity index 100%
rename from Modules/DNSHealth/1.0.5/MailProviders/AppRiver.json
rename to Modules/DNSHealth/1.0.6/MailProviders/AppRiver.json
diff --git a/Modules/DNSHealth/1.0.5/MailProviders/BarracudaESS.json b/Modules/DNSHealth/1.0.6/MailProviders/BarracudaESS.json
similarity index 100%
rename from Modules/DNSHealth/1.0.5/MailProviders/BarracudaESS.json
rename to Modules/DNSHealth/1.0.6/MailProviders/BarracudaESS.json
diff --git a/Modules/DNSHealth/1.0.5/MailProviders/Google.json b/Modules/DNSHealth/1.0.6/MailProviders/Google.json
similarity index 100%
rename from Modules/DNSHealth/1.0.5/MailProviders/Google.json
rename to Modules/DNSHealth/1.0.6/MailProviders/Google.json
diff --git a/Modules/DNSHealth/1.0.5/MailProviders/Intermedia.json b/Modules/DNSHealth/1.0.6/MailProviders/Intermedia.json
similarity index 100%
rename from Modules/DNSHealth/1.0.5/MailProviders/Intermedia.json
rename to Modules/DNSHealth/1.0.6/MailProviders/Intermedia.json
diff --git a/Modules/DNSHealth/1.0.5/MailProviders/Microsoft365.json b/Modules/DNSHealth/1.0.6/MailProviders/Microsoft365.json
similarity index 100%
rename from Modules/DNSHealth/1.0.5/MailProviders/Microsoft365.json
rename to Modules/DNSHealth/1.0.6/MailProviders/Microsoft365.json
diff --git a/Modules/DNSHealth/1.0.5/MailProviders/Mimecast.json b/Modules/DNSHealth/1.0.6/MailProviders/Mimecast.json
similarity index 100%
rename from Modules/DNSHealth/1.0.5/MailProviders/Mimecast.json
rename to Modules/DNSHealth/1.0.6/MailProviders/Mimecast.json
diff --git a/Modules/DNSHealth/1.0.5/MailProviders/Null.json b/Modules/DNSHealth/1.0.6/MailProviders/Null.json
similarity index 100%
rename from Modules/DNSHealth/1.0.5/MailProviders/Null.json
rename to Modules/DNSHealth/1.0.6/MailProviders/Null.json
diff --git a/Modules/DNSHealth/1.0.5/MailProviders/Proofpoint.json b/Modules/DNSHealth/1.0.6/MailProviders/Proofpoint.json
similarity index 100%
rename from Modules/DNSHealth/1.0.5/MailProviders/Proofpoint.json
rename to Modules/DNSHealth/1.0.6/MailProviders/Proofpoint.json
diff --git a/Modules/DNSHealth/1.0.5/MailProviders/Reflexion.json b/Modules/DNSHealth/1.0.6/MailProviders/Reflexion.json
similarity index 100%
rename from Modules/DNSHealth/1.0.5/MailProviders/Reflexion.json
rename to Modules/DNSHealth/1.0.6/MailProviders/Reflexion.json
diff --git a/Modules/DNSHealth/1.0.5/MailProviders/Sophos.json b/Modules/DNSHealth/1.0.6/MailProviders/Sophos.json
similarity index 100%
rename from Modules/DNSHealth/1.0.5/MailProviders/Sophos.json
rename to Modules/DNSHealth/1.0.6/MailProviders/Sophos.json
diff --git a/Modules/DNSHealth/1.0.5/MailProviders/SpamTitan.json b/Modules/DNSHealth/1.0.6/MailProviders/SpamTitan.json
similarity index 100%
rename from Modules/DNSHealth/1.0.5/MailProviders/SpamTitan.json
rename to Modules/DNSHealth/1.0.6/MailProviders/SpamTitan.json
diff --git a/Modules/DNSHealth/1.0.5/MailProviders/_template.json b/Modules/DNSHealth/1.0.6/MailProviders/_template.json
similarity index 100%
rename from Modules/DNSHealth/1.0.5/MailProviders/_template.json
rename to Modules/DNSHealth/1.0.6/MailProviders/_template.json
diff --git a/Modules/DNSHealth/1.0.5/PSGetModuleInfo.xml b/Modules/DNSHealth/1.0.6/PSGetModuleInfo.xml
similarity index 82%
rename from Modules/DNSHealth/1.0.5/PSGetModuleInfo.xml
rename to Modules/DNSHealth/1.0.6/PSGetModuleInfo.xml
index 9a8a5290bd27..3ef782dbd75c 100644
--- a/Modules/DNSHealth/1.0.5/PSGetModuleInfo.xml
+++ b/Modules/DNSHealth/1.0.6/PSGetModuleInfo.xml
@@ -7,13 +7,13 @@
DNSHealth
- 1.0.5
+ 1.0.6
Module
CIPP DNS Health Check Module
John Duprey
johnduprey
2023 John Duprey
- 2023-04-12T19:41:15-04:00
+ 2023-06-15T12:02:08-04:00
@@ -36,19 +36,8 @@
- Workflow
+ Function
-
-
-
-
-
- Cmdlet
-
-
-
- Command
-
Read-DmarcPolicy
@@ -69,16 +58,8 @@
- RoleCapability
-
-
-
- DscResource
-
-
-
- Function
-
+ Command
+
Read-DmarcPolicy
@@ -98,6 +79,25 @@
+
+ Workflow
+
+
+
+
+
+
+ RoleCapability
+
+
+
+ Cmdlet
+
+
+
+ DscResource
+
+
@@ -121,25 +121,25 @@
True
True
0
- 23
- 27716
- 4/12/2023 7:41:15 PM -04:00
- 4/12/2023 7:41:15 PM -04:00
- 4/12/2023 7:41:15 PM -04:00
+ 39
+ 27746
+ 6/15/2023 12:02:08 PM -04:00
+ 6/15/2023 12:02:08 PM -04:00
+ 6/15/2023 12:02:08 PM -04:00
PSModule PSFunction_Read-DmarcPolicy PSCommand_Read-DmarcPolicy PSFunction_Read-MtaStsPolicy PSCommand_Read-MtaStsPolicy PSFunction_Read-DkimRecord PSCommand_Read-DkimRecord PSFunction_Read-MtaStsRecord PSCommand_Read-MtaStsRecord PSFunction_Read-MXRecord PSCommand_Read-MXRecord PSFunction_Read-NSRecord PSCommand_Read-NSRecord PSFunction_Read-SPFRecord PSCommand_Read-SPFRecord PSFunction_Read-TlsRptRecord PSCommand_Read-TlsRptRecord PSFunction_Read-WhoisRecord PSCommand_Read-WhoisRecord PSFunction_Resolve-DnsHttpsQuery PSCommand_Resolve-DnsHttpsQuery PSFunction_Set-DnsResolver PSCommand_Set-DnsResolver PSFunction_Test-DNSSEC PSCommand_Test-DNSSEC PSFunction_Test-HttpsCertificate PSCommand_Test-HttpsCertificate PSFunction_Test-MtaSts PSCommand_Test-MtaSts PSIncludes_Function
False
- 2023-04-12T19:41:15Z
- 1.0.5
+ 2023-06-15T12:02:08Z
+ 1.0.6
John Duprey
false
Module
- DNSHealth.nuspec|DNSHealth.psm1|MailProviders\Microsoft365.json|DNSHealth.psd1|MailProviders\Intermedia.json|MailProviders\Proofpoint.json|MailProviders\BarracudaESS.json|MailProviders\Mimecast.json|MailProviders\_template.json|MailProviders\Null.json|MailProviders\Sophos.json|MailProviders\Reflexion.json|MailProviders\SpamTitan.json|MailProviders\AppRiver.json|MailProviders\Google.json
+ DNSHealth.nuspec|MailProviders\SpamTitan.json|DNSHealth.psd1|MailProviders\Null.json|DNSHealth.psm1|MailProviders\BarracudaESS.json|MailProviders\Microsoft365.json|MailProviders\AppRiver.json|MailProviders\Google.json|MailProviders\Sophos.json|MailProviders\_template.json|MailProviders\Reflexion.json|MailProviders\Intermedia.json|MailProviders\Proofpoint.json|MailProviders\Mimecast.json
a300d2b0-d468-46d1-88a3-e442a76b655b
7.0
Unknown
- C:\Users\jduprey.CNS\Documents\GitHub\CIPP-API\Modules\DNSHealth\1.0.5
+ C:\Users\JDDoS\Documents\GitHub\CIPP-API\Modules\DNSHealth\1.0.6
diff --git a/Modules/GraphRequests/GraphRequests.psm1 b/Modules/GraphRequests/GraphRequests.psm1
index 14b1aeab66ae..f584acc34193 100644
--- a/Modules/GraphRequests/GraphRequests.psm1
+++ b/Modules/GraphRequests/GraphRequests.psm1
@@ -1,397 +1,12 @@
-using namespace System.Net
-
-function Get-StringHash {
- Param($String)
- $StringBuilder = New-Object System.Text.StringBuilder
- [System.Security.Cryptography.HashAlgorithm]::Create('SHA1').ComputeHash([System.Text.Encoding]::UTF8.GetBytes($String)) | ForEach-Object {
- [Void]$StringBuilder.Append($_.ToString('x2'))
- }
- $StringBuilder.ToString()
-}
-function Get-GraphRequestList {
- [CmdletBinding()]
- Param(
- $Tenant = $env:TenantId,
- [Parameter(Mandatory = $true)]
- $Endpoint,
- $Parameters = @(),
- $QueueId,
- $CippLink,
- [ValidateSet('v1.0', 'beta')]
- $Version = 'beta',
- $QueueNameOverride,
- [switch]$SkipCache,
- [switch]$ClearCache,
- [switch]$NoPagination,
- [switch]$CountOnly,
- [switch]$NoAuthCheck,
- [switch]$ReverseTenantLookup,
- [string]$ReverseTenantLookupProperty = 'tenantId'
- )
-
- $TableName = ('cache{0}' -f ($Endpoint -replace '[^A-Za-z0-9]'))[0..62] -join ''
- Write-Host "Table: $TableName"
- $DisplayName = ($Endpoint -split '/')[0]
-
- if ($QueueNameOverride) {
- $QueueName = $QueueNameOverride
- } else {
- $TextInfo = (Get-Culture).TextInfo
- $QueueName = $TextInfo.ToTitleCase($DisplayName -csplit '(?=[A-Z])' -ne '' -join ' ')
- }
-
- $GraphQuery = [System.UriBuilder]('https://graph.microsoft.com/{0}/{1}' -f $Version, $Endpoint)
- $ParamCollection = [System.Web.HttpUtility]::ParseQueryString([String]::Empty)
- foreach ($Item in ($Parameters.GetEnumerator() | Sort-Object -CaseSensitive -Property Key)) {
- $ParamCollection.Add($Item.Key, $Item.Value)
- }
- $GraphQuery.Query = $ParamCollection.ToString()
- $PartitionKey = Get-StringHash -String (@($Endpoint, $ParamCollection.ToString()) -join '-')
- Write-Host "PK: $PartitionKey"
-
- Write-Host ( 'GET [ {0} ]' -f $GraphQuery.ToString())
-
- if ($QueueId) {
- $Table = Get-CIPPTable -TableName $TableName
- $Filter = "QueueId eq '{0}'" -f $QueueId
- $Rows = Get-AzDataTableEntity @Table -Filter $Filter
- $Type = 'Queue'
- } elseif ($Tenant -eq 'AllTenants' -or (!$SkipCache.IsPresent -and !$ClearCache.IsPresent -and !$CountOnly.IsPresent)) {
- $Table = Get-CIPPTable -TableName $TableName
- if ($Tenant -eq 'AllTenants') {
- $Filter = "PartitionKey eq '{0}' and QueueType eq 'AllTenants'" -f $PartitionKey
- } else {
- $Filter = "PartitionKey eq '{0}' and Tenant eq '{1}'" -f $PartitionKey, $Tenant
- }
- #Write-Host $Filter
- $Rows = Get-AzDataTableEntity @Table -Filter $Filter | Where-Object { $_.Timestamp.DateTime -gt (Get-Date).ToUniversalTime().AddHours(-1) }
- $Type = 'Cache'
- } else {
- $Type = 'None'
- $Rows = @()
- }
- Write-Host "Cached: $(($Rows | Measure-Object).Count) rows (Type: $($Type))"
-
- $QueueReference = '{0}-{1}' -f $Tenant, $PartitionKey
- $RunningQueue = Get-CippQueue | Where-Object { $_.Reference -eq $QueueReference -and $_.Status -ne 'Completed' -and $_.Status -ne 'Failed' }
-
- if (!$Rows) {
- switch ($Tenant) {
- 'AllTenants' {
- if ($SkipCache) {
- Get-Tenants -IncludeErrors | ForEach-Object -Parallel {
- Import-Module .\GraphHelper.psm1
- $GraphRequestParams = @{
- Tenant = $_.defaultDomainName
- Endpoint = $using:Endpoint
- Parameters = $using:Parameters
- NoPagination = $using:NoPagination.IsPresent
- ReverseTenantLookupProperty = $using:ReverseTenantLookupProperty
- ReverseTenantLookup = $using:ReverseTenantLookup.IsPresent
- SkipCache = $true
- }
-
- try {
- Get-GraphRequestList @GraphRequestParams | Select-Object *, @{l = 'Tenant'; e = { $_.defaultDomainName } }, @{l = 'CippStatus'; e = { 'Good' } }
- } catch {
- [PSCustomObject]@{
- Tenant = $_.defaultDomainName
- CippStatus = "Could not connect to tenant. $($_.Exception.message)"
- }
- }
- }
- } else {
-
- if ($RunningQueue) {
- Write-Host 'Queue currently running'
- Write-Host ($RunningQueue | ConvertTo-Json)
- [PSCustomObject]@{
- Tenant = 'Data still processing, please wait'
- QueueId = $RunningQueue.RowKey
- }
- } else {
- $Queue = New-CippQueueEntry -Name "$QueueName (All Tenants)" -Link $CippLink -Reference $QueueReference
- [PSCustomObject]@{
- QueueMessage = 'Loading data for all tenants. Please check back after the job completes'
- Queued = $true
- QueueId = $Queue.RowKey
- }
- Write-Host 'Pushing output bindings'
- try {
- Get-Tenants -IncludeErrors | ForEach-Object {
- $Tenant = $_.defaultDomainName
- $QueueTenant = @{
- Tenant = $Tenant
- Endpoint = $Endpoint
- QueueId = $Queue.RowKey
- QueueName = $QueueName
- QueueType = 'AllTenants'
- Parameters = $Parameters
- PartitionKey = $PartitionKey
- NoPagination = $NoPagination.IsPresent
- NoAuthCheck = $NoAuthCheck.IsPresent
- ReverseTenantLookupProperty = $ReverseTenantLookupProperty
- ReverseTenantLookup = $ReverseTenantLookup.IsPresent
- } | ConvertTo-Json -Depth 5 -Compress
-
- Push-OutputBinding -Name QueueTenant -Value $QueueTenant
- }
- } catch {
- Write-Host "QUEUE ERROR: $($_.Exception.Message)"
- }
- }
- }
- }
- default {
- $GraphRequest = @{
- uri = $GraphQuery.ToString()
- tenantid = $Tenant
- ComplexFilter = $true
- }
-
- if ($NoPagination.IsPresent) {
- $GraphRequest.noPagination = $NoPagination.IsPresent
- }
-
- if ($CountOnly.IsPresent) {
- $GraphRequest.CountOnly = $CountOnly.IsPresent
- }
-
- if ($NoAuthCheck.IsPresent) {
- $GraphRequest.noauthcheck = $NoAuthCheck.IsPresent
- }
-
- try {
- $QueueThresholdExceeded = $false
- if ($Parameters.'$count' -and !$SkipCache -and !$NoPagination) {
- $Count = New-GraphGetRequest @GraphRequest -CountOnly -ErrorAction Stop
- Write-Host "Total results (`$count): $Count"
- if ($Count -gt 8000) {
- $QueueThresholdExceeded = $true
- if ($RunningQueue) {
- Write-Host 'Queue currently running'
- Write-Host ($RunningQueue | ConvertTo-Json)
- [PSCustomObject]@{
- QueueMessage = 'Data still processing, please wait'
- QueueId = $RunningQueue.RowKey
- Queued = $true
- }
- } else {
- $Queue = New-CippQueueEntry -Name $QueueName -Link $CippLink -Reference $QueueReference
- $QueueTenant = @{
- Tenant = $Tenant
- Endpoint = $Endpoint
- QueueId = $Queue.RowKey
- QueueName = $QueueName
- QueueType = 'SingleTenant'
- Parameters = $Parameters
- PartitionKey = $PartitionKey
- NoAuthCheck = $NoAuthCheck.IsPresent
- ReverseTenantLookupProperty = $ReverseTenantLookupProperty
- ReverseTenantLookup = $ReverseTenantLookup.IsPresent
- } | ConvertTo-Json -Depth 5 -Compress
-
- Push-OutputBinding -Name QueueTenant -Value $QueueTenant
- [PSCustomObject]@{
- QueueMessage = ('Loading {0} rows for {1}. Please check back after the job completes' -f $Count, $Tenant)
- QueueId = $Queue.RowKey
- Queued = $true
- }
- }
- }
- }
-
- if (!$QueueThresholdExceeded) {
- $GraphRequestResults = New-GraphGetRequest @GraphRequest -ErrorAction Stop
- if ($ReverseTenantLookup -and $GraphRequestResults) {
- $TenantInfo = $GraphRequestResults.$ReverseTenantLookupProperty | Sort-Object -Unique | ForEach-Object {
- New-GraphGetRequest -uri "https://graph.microsoft.com/beta/tenantRelationships/findTenantInformationByTenantId(tenantId='$_')" -noauthcheck $true
- }
- foreach ($Result in $GraphRequestResults) {
- $Result | Select-Object @{n = 'TenantInfo'; e = { $TenantInfo | Where-Object { $Result.$ReverseTenantLookupProperty -eq $_.tenantId } } }, *
- }
- } else {
- $GraphRequestResults
- }
- }
-
- } catch {
- throw $_.Exception
- }
- }
- }
- } else {
- $Rows | ForEach-Object {
- $_.Data | ConvertFrom-Json
- }
- }
-}
-
-function Push-GraphRequestListQueue {
- # Input bindings are passed in via param block.
- param($QueueTenant, $TriggerMetadata)
-
- # Write out the queue message and metadata to the information log.
- Write-Host "PowerShell queue trigger function processed work item: $($QueueTenant.Endpoint) - $($QueueTenant.Tenant)"
-
- #Write-Host ($QueueTenant | ConvertTo-Json -Depth 5)
-
- $TenantQueueName = '{0} - {1}' -f $QueueTenant.QueueName, $QueueTenant.Tenant
- Update-CippQueueEntry -RowKey $QueueTenant.QueueId -Status 'Processing' -Name $TenantQueueName
-
- $ParamCollection = [System.Web.HttpUtility]::ParseQueryString([String]::Empty)
- foreach ($Item in ($QueueTenant.Parameters.GetEnumerator() | Sort-Object -CaseSensitive -Property Key)) {
- $ParamCollection.Add($Item.Key, $Item.Value)
- }
-
- $PartitionKey = $QueueTenant.PartitionKey
-
- $TableName = ('cache{0}' -f ($QueueTenant.Endpoint -replace '[^A-Za-z0-9]'))[0..63] -join ''
- Write-Host $TableName
- $Table = Get-CIPPTable -TableName $TableName
-
- $Filter = "PartitionKey eq '{0}' and Tenant eq '{1}'" -f $PartitionKey, $QueueTenant.Tenant
- Write-Host $Filter
- Get-AzDataTableEntity @Table -Filter $Filter | Remove-AzDataTableEntity @Table
-
- $GraphRequestParams = @{
- Tenant = $QueueTenant.Tenant
- Endpoint = $QueueTenant.Endpoint
- Parameters = $QueueTenant.Parameters
- NoPagination = $QueueTenant.NoPagination
- ReverseTenantLookupProperty = $QueueTenant.ReverseTenantLookupProperty
- ReverseTenantLookup = $QueueTenant.ReverseTenantLookup
- SkipCache = $true
- }
-
- $RawGraphRequest = try {
- Get-GraphRequestList @GraphRequestParams | Select-Object *, @{l = 'Tenant'; e = { $QueueTenant.Tenant } }, @{l = 'CippStatus'; e = { 'Good' } }
- } catch {
- [PSCustomObject]@{
- Tenant = $QueueTenant.Tenant
- CippStatus = "Could not connect to tenant. $($_.Exception.message)"
- }
- }
-
- $GraphResults = foreach ($Request in $RawGraphRequest) {
- $Json = ConvertTo-Json -Depth 5 -Compress -InputObject $Request
- [PSCustomObject]@{
- Tenant = [string]$QueueTenant.Tenant
- QueueId = [string]$QueueTenant.QueueId
- QueueType = [string]$QueueTenant.QueueType
- RowKey = [string](New-Guid)
- PartitionKey = [string]$PartitionKey
- Data = [string]$Json
- }
- }
+$Public = @(Get-ChildItem -Path $PSScriptRoot\Public\*.ps1 -ErrorAction SilentlyContinue)
+$Private = @(Get-ChildItem -Path $PSScriptRoot\private\*.ps1 -ErrorAction SilentlyContinue)
+$Functions = $Public + $Private
+foreach ($import in @($Functions)) {
try {
- Add-AzDataTableEntity @Table -Entity $GraphResults -Force | Out-Null
- Update-CippQueueEntry -RowKey $QueueTenant.QueueId -Status 'Completed'
+ . $import.FullName
} catch {
- Write-Host "Queue Error: $($_.Exception.Message)"
- Update-CippQueueEntry -RowKey $QueueTenant.QueueId -Status 'Failed'
+ Write-Error -Message "Failed to import function $($import.FullName): $_"
}
}
-function Get-GraphRequestListHttp {
- # Input bindings are passed in via param block.
- param($Request, $TriggerMetadata)
-
- $APIName = $TriggerMetadata.FunctionName
-
- $Message = 'Accessed this API | Endpoint: {0}' -f $Request.Query.Endpoint
- Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message $Message -Sev 'Debug'
-
- $CippLink = ([System.Uri]$TriggerMetadata.Headers.referer).PathAndQuery
-
- $Parameters = @{}
- if ($Request.Query.'$filter') {
- $Parameters.'$filter' = $Request.Query.'$filter'
- }
-
- if (!$Request.Query.'$filter' -and $Request.Query.graphFilter) {
- $Parameters.'$filter' = $Request.Query.graphFilter
- }
-
- if ($Request.Query.'$select') {
- $Parameters.'$select' = $Request.Query.'$select'
- }
-
- if ($Request.Query.'$expand') {
- $Parameters.'$expand' = $Request.Query.'$expand'
- }
-
- if ($Request.Query.'$top') {
- $Parameters.'$top' = $Request.Query.'$top'
- }
-
- if ($Request.Query.'$count') {
- $Parameters.'$count' = ([string]([System.Boolean]$Request.Query.'$count')).ToLower()
- }
-
- if ($Request.Query.'$orderby') {
- $Parameters.'$orderby' = $Request.Query.'$orderby'
- }
-
- if ($Request.Query.'$search') {
- $Parameters.'$search' = $Request.Query.'$search'
- }
-
- $GraphRequestParams = @{
- Endpoint = $Request.Query.Endpoint
- Parameters = $Parameters
- CippLink = $CippLink
- }
-
- if ($Request.Query.TenantFilter) {
- $GraphRequestParams.Tenant = $Request.Query.TenantFilter
- }
-
- if ($Request.Query.QueueId) {
- $GraphRequestParams.QueueId = $Request.Query.QueueId
- }
-
- if ($Request.Query.Version) {
- $GraphRequestParams.Version = $Request.Query.Version
- }
-
- if ($Request.Query.NoPagination) {
- $GraphRequestParams.NoPagination = [System.Boolean]$Request.Query.NoPagination
- }
-
- if ($Request.Query.CountOnly) {
- $GraphRequestParams.CountOnly = [System.Boolean]$Request.Query.CountOnly
- }
-
- if ($Request.Query.QueueNameOverride) {
- $GraphRequestParams.QueueNameOverride = [System.Boolean]$Request.Query.QueueNameOverride
- }
-
- if ($Request.Query.ReverseTenantLookup) {
- $GraphRequestParams.ReverseTenantLookup = [System.Boolean]$Request.Query.ReverseTenantLookup
- }
-
- if ($Request.Query.ReverseTenantLookupProperty) {
- $GraphRequestParams.ReverseTenantLookupProperty = $Request.Query.ReverseTenantLookupProperty
- }
-
- if ($Request.Query.SkipCache) {
- $GraphRequestParams.SkipCache = [System.Boolean]$Request.Query.SkipCache
- }
-
- Write-Host ($GraphRequestParams | ConvertTo-Json)
- try {
- $GraphRequestData = Get-GraphRequestList @GraphRequestParams
- $StatusCode = [HttpStatusCode]::OK
- } catch {
- $GraphRequestData = "Graph Error: $($_.Exception.Message)"
- $StatusCode = [HttpStatusCode]::BadRequest
- }
-
- Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
- StatusCode = $StatusCode
- Body = @($GraphRequestData)
- })
-}
-
-Export-ModuleMember -Function @('Get-GraphRequestList', 'Get-GraphRequestListHttp', 'Push-GraphRequestListQueue')
+Export-ModuleMember -Function $Public.BaseName -Alias *
diff --git a/Modules/GraphRequests/Private/Get-StringHash.ps1 b/Modules/GraphRequests/Private/Get-StringHash.ps1
new file mode 100644
index 000000000000..a5c94f62b511
--- /dev/null
+++ b/Modules/GraphRequests/Private/Get-StringHash.ps1
@@ -0,0 +1,8 @@
+function Get-StringHash {
+ Param($String)
+ $StringBuilder = New-Object System.Text.StringBuilder
+ [System.Security.Cryptography.HashAlgorithm]::Create('SHA1').ComputeHash([System.Text.Encoding]::UTF8.GetBytes($String)) | ForEach-Object {
+ [Void]$StringBuilder.Append($_.ToString('x2'))
+ }
+ $StringBuilder.ToString()
+}
\ No newline at end of file
diff --git a/Modules/GraphRequests/Public/Get-GraphRequestList.ps1 b/Modules/GraphRequests/Public/Get-GraphRequestList.ps1
new file mode 100644
index 000000000000..59873b095b44
--- /dev/null
+++ b/Modules/GraphRequests/Public/Get-GraphRequestList.ps1
@@ -0,0 +1,217 @@
+function Get-GraphRequestList {
+ [CmdletBinding()]
+ Param(
+ $Tenant = $env:TenantId,
+ [Parameter(Mandatory = $true)]
+ $Endpoint,
+ $Parameters = @(),
+ $QueueId,
+ $CippLink,
+ [ValidateSet('v1.0', 'beta')]
+ $Version = 'beta',
+ $QueueNameOverride,
+ [switch]$SkipCache,
+ [switch]$ClearCache,
+ [switch]$NoPagination,
+ [switch]$CountOnly,
+ [switch]$NoAuthCheck,
+ [switch]$ReverseTenantLookup,
+ [string]$ReverseTenantLookupProperty = 'tenantId'
+ )
+
+ $TableName = ('cache{0}' -f ($Endpoint -replace '[^A-Za-z0-9]'))[0..62] -join ''
+ Write-Host "Table: $TableName"
+ $DisplayName = ($Endpoint -split '/')[0]
+
+ if ($QueueNameOverride) {
+ $QueueName = $QueueNameOverride
+ } else {
+ $TextInfo = (Get-Culture).TextInfo
+ $QueueName = $TextInfo.ToTitleCase($DisplayName -csplit '(?=[A-Z])' -ne '' -join ' ')
+ }
+
+ $GraphQuery = [System.UriBuilder]('https://graph.microsoft.com/{0}/{1}' -f $Version, $Endpoint)
+ $ParamCollection = [System.Web.HttpUtility]::ParseQueryString([String]::Empty)
+ foreach ($Item in ($Parameters.GetEnumerator() | Sort-Object -CaseSensitive -Property Key)) {
+ $ParamCollection.Add($Item.Key, $Item.Value)
+ }
+ $GraphQuery.Query = $ParamCollection.ToString()
+ $PartitionKey = Get-StringHash -String (@($Endpoint, $ParamCollection.ToString()) -join '-')
+ Write-Host "PK: $PartitionKey"
+
+ Write-Host ( 'GET [ {0} ]' -f $GraphQuery.ToString())
+
+ if ($QueueId) {
+ $Table = Get-CIPPTable -TableName $TableName
+ $Filter = "QueueId eq '{0}'" -f $QueueId
+ $Rows = Get-AzDataTableEntity @Table -Filter $Filter
+ $Type = 'Queue'
+ } elseif ($Tenant -eq 'AllTenants' -or (!$SkipCache.IsPresent -and !$ClearCache.IsPresent -and !$CountOnly.IsPresent)) {
+ $Table = Get-CIPPTable -TableName $TableName
+ if ($Tenant -eq 'AllTenants') {
+ $Filter = "PartitionKey eq '{0}' and QueueType eq 'AllTenants'" -f $PartitionKey
+ } else {
+ $Filter = "PartitionKey eq '{0}' and Tenant eq '{1}'" -f $PartitionKey, $Tenant
+ }
+ #Write-Host $Filter
+ $Rows = Get-AzDataTableEntity @Table -Filter $Filter | Where-Object { $_.Timestamp.DateTime -gt (Get-Date).ToUniversalTime().AddHours(-1) }
+ $Type = 'Cache'
+ } else {
+ $Type = 'None'
+ $Rows = @()
+ }
+ Write-Host "Cached: $(($Rows | Measure-Object).Count) rows (Type: $($Type))"
+
+ $QueueReference = '{0}-{1}' -f $Tenant, $PartitionKey
+ $RunningQueue = Get-CippQueue | Where-Object { $_.Reference -eq $QueueReference -and $_.Status -ne 'Completed' -and $_.Status -ne 'Failed' }
+
+ if (!$Rows) {
+ switch ($Tenant) {
+ 'AllTenants' {
+ if ($SkipCache) {
+ Get-Tenants -IncludeErrors | ForEach-Object -Parallel {
+ Import-Module .\GraphHelper.psm1
+ $GraphRequestParams = @{
+ Tenant = $_.defaultDomainName
+ Endpoint = $using:Endpoint
+ Parameters = $using:Parameters
+ NoPagination = $using:NoPagination.IsPresent
+ ReverseTenantLookupProperty = $using:ReverseTenantLookupProperty
+ ReverseTenantLookup = $using:ReverseTenantLookup.IsPresent
+ SkipCache = $true
+ }
+
+ try {
+ Get-GraphRequestList @GraphRequestParams | Select-Object *, @{l = 'Tenant'; e = { $_.defaultDomainName } }, @{l = 'CippStatus'; e = { 'Good' } }
+ } catch {
+ [PSCustomObject]@{
+ Tenant = $_.defaultDomainName
+ CippStatus = "Could not connect to tenant. $($_.Exception.message)"
+ }
+ }
+ }
+ } else {
+ if ($RunningQueue) {
+ Write-Host 'Queue currently running'
+ Write-Host ($RunningQueue | ConvertTo-Json)
+ [PSCustomObject]@{
+ Tenant = 'Data still processing, please wait'
+ QueueId = $RunningQueue.RowKey
+ }
+ } else {
+ $Queue = New-CippQueueEntry -Name "$QueueName (All Tenants)" -Link $CippLink -Reference $QueueReference
+ [PSCustomObject]@{
+ QueueMessage = 'Loading data for all tenants. Please check back after the job completes'
+ Queued = $true
+ QueueId = $Queue.RowKey
+ }
+ Write-Host 'Pushing output bindings'
+ try {
+ Get-Tenants -IncludeErrors | ForEach-Object {
+ $Tenant = $_.defaultDomainName
+ $QueueTenant = @{
+ Tenant = $Tenant
+ Endpoint = $Endpoint
+ QueueId = $Queue.RowKey
+ QueueName = $QueueName
+ QueueType = 'AllTenants'
+ Parameters = $Parameters
+ PartitionKey = $PartitionKey
+ NoPagination = $NoPagination.IsPresent
+ NoAuthCheck = $NoAuthCheck.IsPresent
+ ReverseTenantLookupProperty = $ReverseTenantLookupProperty
+ ReverseTenantLookup = $ReverseTenantLookup.IsPresent
+ } | ConvertTo-Json -Depth 5 -Compress
+
+ Push-OutputBinding -Name QueueItem -Value $QueueTenant
+ }
+ } catch {
+ Write-Host "QUEUE ERROR: $($_.Exception.Message)"
+ }
+ }
+ }
+ }
+ default {
+ $GraphRequest = @{
+ uri = $GraphQuery.ToString()
+ tenantid = $Tenant
+ ComplexFilter = $true
+ }
+
+ if ($NoPagination.IsPresent) {
+ $GraphRequest.noPagination = $NoPagination.IsPresent
+ }
+
+ if ($CountOnly.IsPresent) {
+ $GraphRequest.CountOnly = $CountOnly.IsPresent
+ }
+
+ if ($NoAuthCheck.IsPresent) {
+ $GraphRequest.noauthcheck = $NoAuthCheck.IsPresent
+ }
+
+ try {
+ $QueueThresholdExceeded = $false
+ if ($Parameters.'$count' -and !$SkipCache -and !$NoPagination) {
+ $Count = New-GraphGetRequest @GraphRequest -CountOnly -ErrorAction Stop
+ Write-Host "Total results (`$count): $Count"
+ if ($Count -gt 8000) {
+ $QueueThresholdExceeded = $true
+ if ($RunningQueue) {
+ Write-Host 'Queue currently running'
+ Write-Host ($RunningQueue | ConvertTo-Json)
+ [PSCustomObject]@{
+ QueueMessage = 'Data still processing, please wait'
+ QueueId = $RunningQueue.RowKey
+ Queued = $true
+ }
+ } else {
+ $Queue = New-CippQueueEntry -Name $QueueName -Link $CippLink -Reference $QueueReference
+ $QueueTenant = @{
+ Tenant = $Tenant
+ Endpoint = $Endpoint
+ QueueId = $Queue.RowKey
+ QueueName = $QueueName
+ QueueType = 'SingleTenant'
+ Parameters = $Parameters
+ PartitionKey = $PartitionKey
+ NoAuthCheck = $NoAuthCheck.IsPresent
+ ReverseTenantLookupProperty = $ReverseTenantLookupProperty
+ ReverseTenantLookup = $ReverseTenantLookup.IsPresent
+ } | ConvertTo-Json -Depth 5 -Compress
+
+ Push-OutputBinding -Name QueueItem -Value $QueueTenant
+ [PSCustomObject]@{
+ QueueMessage = ('Loading {0} rows for {1}. Please check back after the job completes' -f $Count, $Tenant)
+ QueueId = $Queue.RowKey
+ Queued = $true
+ }
+ }
+ }
+ }
+
+ if (!$QueueThresholdExceeded) {
+ $GraphRequestResults = New-GraphGetRequest @GraphRequest -ErrorAction Stop
+ if ($ReverseTenantLookup -and $GraphRequestResults) {
+ $TenantInfo = $GraphRequestResults.$ReverseTenantLookupProperty | Sort-Object -Unique | ForEach-Object {
+ New-GraphGetRequest -uri "https://graph.microsoft.com/beta/tenantRelationships/findTenantInformationByTenantId(tenantId='$_')" -noauthcheck $true -asApp:$true -tenant $env:TenantId
+ }
+ foreach ($Result in $GraphRequestResults) {
+ $Result | Select-Object @{n = 'TenantInfo'; e = { $TenantInfo | Where-Object { $Result.$ReverseTenantLookupProperty -eq $_.tenantId } } }, *
+ }
+ } else {
+ $GraphRequestResults
+ }
+ }
+
+ } catch {
+ throw $_.Exception
+ }
+ }
+ }
+ } else {
+ $Rows | ForEach-Object {
+ $_.Data | ConvertFrom-Json
+ }
+ }
+}
\ No newline at end of file
diff --git a/Modules/GraphRequests/Public/Get-ListGraphRequest.ps1 b/Modules/GraphRequests/Public/Get-ListGraphRequest.ps1
new file mode 100644
index 000000000000..fa237c482d9a
--- /dev/null
+++ b/Modules/GraphRequests/Public/Get-ListGraphRequest.ps1
@@ -0,0 +1,106 @@
+
+function Get-ListGraphRequest {
+ # Input bindings are passed in via param block.
+ param($Request, $TriggerMetadata)
+
+ $APIName = $TriggerMetadata.FunctionName
+
+ if ($APIName -match '^Ext') {
+ $Message = 'Accessed this API | Endpoint: {0}' -f $Request.Query.Endpoint
+ Write-LogMessage -user $AccessResult.ClientId -API $APINAME -message $Message -Sev 'Debug'
+ } else {
+ $Message = 'Accessed this API | Endpoint: {0}' -f $Request.Query.Endpoint
+ Write-LogMessage -API $APINAME -message $Message -Sev 'Debug'
+ }
+
+ $CippLink = ([System.Uri]$TriggerMetadata.Headers.referer).PathAndQuery
+
+ $Parameters = @{}
+ if ($Request.Query.'$filter') {
+ $Parameters.'$filter' = $Request.Query.'$filter'
+ }
+
+ if (!$Request.Query.'$filter' -and $Request.Query.graphFilter) {
+ $Parameters.'$filter' = $Request.Query.graphFilter
+ }
+
+ if ($Request.Query.'$select') {
+ $Parameters.'$select' = $Request.Query.'$select'
+ }
+
+ if ($Request.Query.'$expand') {
+ $Parameters.'$expand' = $Request.Query.'$expand'
+ }
+
+ if ($Request.Query.'$top') {
+ $Parameters.'$top' = $Request.Query.'$top'
+ }
+
+ if ($Request.Query.'$count') {
+ $Parameters.'$count' = ([string]([System.Boolean]$Request.Query.'$count')).ToLower()
+ }
+
+ if ($Request.Query.'$orderby') {
+ $Parameters.'$orderby' = $Request.Query.'$orderby'
+ }
+
+ if ($Request.Query.'$search') {
+ $Parameters.'$search' = $Request.Query.'$search'
+ }
+
+ $GraphRequestParams = @{
+ Endpoint = $Request.Query.Endpoint
+ Parameters = $Parameters
+ CippLink = $CippLink
+ }
+
+ if ($Request.Query.TenantFilter) {
+ $GraphRequestParams.Tenant = $Request.Query.TenantFilter
+ }
+
+ if ($Request.Query.QueueId) {
+ $GraphRequestParams.QueueId = $Request.Query.QueueId
+ }
+
+ if ($Request.Query.Version) {
+ $GraphRequestParams.Version = $Request.Query.Version
+ }
+
+ if ($Request.Query.NoPagination) {
+ $GraphRequestParams.NoPagination = [System.Boolean]$Request.Query.NoPagination
+ }
+
+ if ($Request.Query.CountOnly) {
+ $GraphRequestParams.CountOnly = [System.Boolean]$Request.Query.CountOnly
+ }
+
+ if ($Request.Query.QueueNameOverride) {
+ $GraphRequestParams.QueueNameOverride = [System.Boolean]$Request.Query.QueueNameOverride
+ }
+
+ if ($Request.Query.ReverseTenantLookup) {
+ $GraphRequestParams.ReverseTenantLookup = [System.Boolean]$Request.Query.ReverseTenantLookup
+ }
+
+ if ($Request.Query.ReverseTenantLookupProperty) {
+ $GraphRequestParams.ReverseTenantLookupProperty = $Request.Query.ReverseTenantLookupProperty
+ }
+
+ if ($Request.Query.SkipCache) {
+ $GraphRequestParams.SkipCache = [System.Boolean]$Request.Query.SkipCache
+ }
+
+ Write-Host ($GraphRequestParams | ConvertTo-Json)
+ try {
+ $GraphRequestData = Get-GraphRequestList @GraphRequestParams
+ $StatusCode = [HttpStatusCode]::OK
+ } catch {
+ $GraphRequestData = "Graph Error: $($_.Exception.Message) - Endpoint: $($Request.Query.Endpoint)"
+ $StatusCode = [HttpStatusCode]::BadRequest
+ }
+
+ Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
+ StatusCode = $StatusCode
+ Body = @($GraphRequestData)
+ })
+}
\ No newline at end of file
diff --git a/Modules/GraphRequests/Public/Push-ListGraphRequestQueue.ps1 b/Modules/GraphRequests/Public/Push-ListGraphRequestQueue.ps1
new file mode 100644
index 000000000000..a193fcf9a9f4
--- /dev/null
+++ b/Modules/GraphRequests/Public/Push-ListGraphRequestQueue.ps1
@@ -0,0 +1,65 @@
+function Push-ListGraphRequestQueue {
+ # Input bindings are passed in via param block.
+ param($QueueItem, $TriggerMetadata)
+
+ # Write out the queue message and metadata to the information log.
+ Write-Host "PowerShell queue trigger function processed work item: $($QueueItem.Endpoint) - $($QueueItem.Tenant)"
+
+ #Write-Host ($QueueItem | ConvertTo-Json -Depth 5)
+
+ $TenantQueueName = '{0} - {1}' -f $QueueItem.QueueName, $QueueItem.Tenant
+ Update-CippQueueEntry -RowKey $QueueItem.QueueId -Status 'Processing' -Name $TenantQueueName
+
+ $ParamCollection = [System.Web.HttpUtility]::ParseQueryString([String]::Empty)
+ foreach ($Item in ($QueueItem.Parameters.GetEnumerator() | Sort-Object -CaseSensitive -Property Key)) {
+ $ParamCollection.Add($Item.Key, $Item.Value)
+ }
+
+ $PartitionKey = $QueueItem.PartitionKey
+
+ $TableName = ('cache{0}' -f ($QueueItem.Endpoint -replace '[^A-Za-z0-9]'))[0..63] -join ''
+ Write-Host $TableName
+ $Table = Get-CIPPTable -TableName $TableName
+
+ $Filter = "PartitionKey eq '{0}' and Tenant eq '{1}'" -f $PartitionKey, $QueueItem.Tenant
+ Write-Host $Filter
+ Get-AzDataTableEntity @Table -Filter $Filter | Remove-AzDataTableEntity @Table
+
+ $GraphRequestParams = @{
+ Tenant = $QueueItem.Tenant
+ Endpoint = $QueueItem.Endpoint
+ Parameters = $QueueItem.Parameters
+ NoPagination = $QueueItem.NoPagination
+ ReverseTenantLookupProperty = $QueueItem.ReverseTenantLookupProperty
+ ReverseTenantLookup = $QueueItem.ReverseTenantLookup
+ SkipCache = $true
+ }
+
+ $RawGraphRequest = try {
+ Get-GraphRequestList @GraphRequestParams | Select-Object *, @{l = 'Tenant'; e = { $QueueItem.Tenant } }, @{l = 'CippStatus'; e = { 'Good' } }
+ } catch {
+ [PSCustomObject]@{
+ Tenant = $QueueItem.Tenant
+ CippStatus = "Could not connect to tenant. $($_.Exception.message)"
+ }
+ }
+
+ $GraphResults = foreach ($Request in $RawGraphRequest) {
+ $Json = ConvertTo-Json -Depth 5 -Compress -InputObject $Request
+ [PSCustomObject]@{
+ Tenant = [string]$QueueItem.Tenant
+ QueueId = [string]$QueueItem.QueueId
+ QueueType = [string]$QueueItem.QueueType
+ RowKey = [string](New-Guid)
+ PartitionKey = [string]$PartitionKey
+ Data = [string]$Json
+ }
+ }
+ try {
+ Add-AzDataTableEntity @Table -Entity $GraphResults -Force | Out-Null
+ Update-CippQueueEntry -RowKey $QueueItem.QueueId -Status 'Completed'
+ } catch {
+ Write-Host "Queue Error: $($_.Exception.Message)"
+ Update-CippQueueEntry -RowKey $QueueItem.QueueId -Status 'Failed'
+ }
+}
\ No newline at end of file
diff --git a/RemoveScheduledItems/function.json b/RemoveScheduledItems/function.json
new file mode 100644
index 000000000000..925eab5aeae1
--- /dev/null
+++ b/RemoveScheduledItems/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/RemoveScheduledItems/run.ps1 b/RemoveScheduledItems/run.ps1
new file mode 100644
index 000000000000..ce7c0a0dcd9c
--- /dev/null
+++ b/RemoveScheduledItems/run.ps1
@@ -0,0 +1,11 @@
+using namespace System.Net
+param($Request, $TriggerMetadata)
+$APIName = $TriggerMetadata.FunctionName
+Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug'
+$task = $Request.Body | ConvertFrom-Json
+$Table = Get-CIPPTable -TableName 'ScheduledTasks'
+Remove-AzDataTableEntity @Table -PartitionKey 'ScheduledTask' -RowKey $task.TaskID -force
+Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
+ StatusCode = [HttpStatusCode]::OK
+ Body = 'Task removed successfully.'
+})
\ No newline at end of file
diff --git a/Scheduler_Alert/run.ps1 b/Scheduler_Alert/run.ps1
index 3ddaccfe256e..d56c42678c04 100644
--- a/Scheduler_Alert/run.ps1
+++ b/Scheduler_Alert/run.ps1
@@ -17,7 +17,7 @@ try {
$ShippedAlerts = switch ($Alerts) {
{ $_.'AdminPassword' -eq $true } {
try {
- New-GraphGETRequest -uri "https://graph.microsoft.com/beta/roleManagement/directory/roleAssignments?`$filter=roleDefinitionId eq '62e90394-69f5-4237-9190-012177145e10'" -tenantid $($tenant.tenant) | Where-Object -Property 'principalOrganizationId' -EQ $tenant.tenantid | ForEach-Object {
+ New-GraphGETRequest -uri "https://graph.microsoft.com/beta/roleManagement/directory/roleAssignments?`$filter=roleDefinitionId eq '62e90394-69f5-4237-9190-012177145e10'&`$expand=principal" -tenantid $($tenant.tenant) | Where-Object {($_.principalOrganizationId -EQ $tenant.tenantid) -and ($_.principal.'@odata.type' -eq '#microsoft.graph.user')} | ForEach-Object {
$LastChanges = New-GraphGETRequest -uri "https://graph.microsoft.com/beta/users/$($_.principalId)?`$select=UserPrincipalName,lastPasswordChangeDateTime" -tenant $($tenant.tenant)
if ($LastChanges.LastPasswordChangeDateTime -gt (Get-Date).AddDays(-1)) { "Admin password has been changed for $($LastChanges.UserPrincipalName) in last 24 hours" }
}
diff --git a/Scheduler_UserTasks/_functions.json b/Scheduler_UserTasks/_functions.json
new file mode 100644
index 000000000000..a4e981dd588d
--- /dev/null
+++ b/Scheduler_UserTasks/_functions.json
@@ -0,0 +1,15 @@
+{
+ "bindings": [
+ {
+ "name": "Timer",
+ "schedule": "0 */15 * * * *",
+ "direction": "in",
+ "type": "timerTrigger"
+ },
+ {
+ "name": "starter",
+ "type": "durableClient",
+ "direction": "in"
+ }
+ ]
+ }
\ No newline at end of file
diff --git a/Scheduler_UserTasks/run.ps1 b/Scheduler_UserTasks/run.ps1
new file mode 100644
index 000000000000..91432822b602
--- /dev/null
+++ b/Scheduler_UserTasks/run.ps1
@@ -0,0 +1,44 @@
+param($Timer)
+
+$Table = Get-CippTable -tablename 'ScheduledTasks'
+$Filter = "Results eq 'Not Executed'"
+$tasks = Get-AzDataTableEntity @Table -Filter $Filter
+
+foreach ($task in $tasks) {
+ # Check if task has not been executed yet (i.e., 'Results' is 'Not Executed')
+ if ((Get-Date) -ge $task.ExpectedRunTime) {
+ try {
+ Update-AzDataTableEntity @Table -Entity @{
+ PartitionKey = $task.PartitionKey
+ RowKey = $task.RowKey
+ TaskState = 'Running'
+ # Update other properties as needed
+ }
+
+ $results = Invoke-Command -ScriptBlock ([ScriptBlock]::Create($task.Command)) -ArgumentList $task.Parameters
+
+ Update-AzDataTableEntity @Table -Entity @{
+ PartitionKey = $task.PartitionKey
+ RowKey = $task.RowKey
+ Results = "$results"
+ TaskState = 'Completed'
+ # Update other properties as needed
+ }
+
+ Write-LogMessage -API "Scheduler_UserTasks" -tenant $tenant -message "Successfully executed task: $($task.RowKey)" -sev Info
+ }
+ catch {
+ $errorMessage = $_.Exception.Message
+
+ Update-AzDataTableEntity @Table -Entity @{
+ PartitionKey = $task.PartitionKey
+ RowKey = $task.RowKey
+ Results = "$errorMessage"
+ TaskState = 'Failed'
+ # Update other properties as needed
+ }
+
+ Write-LogMessage -API "Scheduler_UserTasks" -tenant $tenant -message "Failed to execute task: $errorMessage" -sev Error
+ }
+ }
+}
\ No newline at end of file
diff --git a/Standards_AnonReportDisable/run.ps1 b/Standards_AnonReportDisable/run.ps1
index a2525675eb6b..c04f87b957d3 100644
--- a/Standards_AnonReportDisable/run.ps1
+++ b/Standards_AnonReportDisable/run.ps1
@@ -1,9 +1,9 @@
param($tenant)
try {
- New-GraphPostRequest -tenantid $tenant -Uri "https://graph.microsoft.com/beta/admin/reportSettings" -Type patch -Body '{"displayConcealedNames": false}' -ContentType "application/json"
+ New-GraphPostRequest -tenantid $tenant -Uri "https://graph.microsoft.com/beta/admin/reportSettings" -Type patch -Body '{"displayConcealedNames": false}' -ContentType "application/json" -AsApp $true
Write-LogMessage -API "Standards" -tenant $tenant -message "Anonymous Reports Disabled." -sev Info
}
catch {
- Write-LogMessage -API "Standards" -tenant $tenant -message "Failed to disable anonymous reports. Error: $($_.exception.message)"
+ Write-LogMessage -API "Standards" -tenant $tenant -message "Failed to disable anonymous reports. Error: $($_.exception.message)" -sev Error
}
\ No newline at end of file
diff --git a/standards_ConditionalAccess/function.json b/Standards_ConditionalAccess/function.json
similarity index 100%
rename from standards_ConditionalAccess/function.json
rename to Standards_ConditionalAccess/function.json
diff --git a/standards_ConditionalAccess/run.ps1 b/Standards_ConditionalAccess/run.ps1
similarity index 100%
rename from standards_ConditionalAccess/run.ps1
rename to Standards_ConditionalAccess/run.ps1
diff --git a/Standards_DisableGuestDirectory/run.ps1 b/Standards_DisableGuestDirectory/run.ps1
index c347598e212a..cc81ded587a3 100644
--- a/Standards_DisableGuestDirectory/run.ps1
+++ b/Standards_DisableGuestDirectory/run.ps1
@@ -7,5 +7,5 @@ try {
Write-LogMessage -API "Standards" -tenant $tenant -message "Disabled Guest access to directory information." -sev Info
}
catch {
- Write-LogMessage -API "Standards" -tenant $tenant -message "Failed to disable Guest access to directory information.: $($_.exception.message)"
+ Write-LogMessage -API "Standards" -tenant $tenant -message "Failed to disable Guest access to directory information.: $($_.exception.message)" -sev "Error"
}
diff --git a/Standards_DisableM365GroupUsers/run.ps1 b/Standards_DisableM365GroupUsers/run.ps1
index 08758135b0b2..b4808a0b466a 100644
--- a/Standards_DisableM365GroupUsers/run.ps1
+++ b/Standards_DisableM365GroupUsers/run.ps1
@@ -8,5 +8,5 @@ try {
Write-LogMessage -API "Standards" -tenant $tenant -message "Standards API: Disabled users from creating Security Groups." -sev Info
}
catch {
- Write-LogMessage -API "Standards" -tenant $tenant -message "Failed to disable users from creating Security Groups: $($_.exception.message)"
+ Write-LogMessage -API "Standards" -tenant $tenant -message "Failed to disable users from creating Security Groups: $($_.exception.message)" -sev "Error"
}
diff --git a/Standards_DisableSecurityGroupUsers/run.ps1 b/Standards_DisableSecurityGroupUsers/run.ps1
index bfc3fd3aa395..459c82c2d72a 100644
--- a/Standards_DisableSecurityGroupUsers/run.ps1
+++ b/Standards_DisableSecurityGroupUsers/run.ps1
@@ -7,5 +7,5 @@ try {
Write-LogMessage -API "Standards" -tenant $tenant -message "Standards API: Disabled users from creating Security Groups." -sev Info
}
catch {
- Write-LogMessage -API "Standards" -tenant $tenant -message "Failed to disable users from creating Security Groups: $($_.exception.message)"
+ Write-LogMessage -API "Standards" -tenant $tenant -message "Failed to disable users from creating Security Groups: $($_.exception.message)" -sev "Error"
}
diff --git a/Standards_DisableSelfServiceLicenses/run.ps1 b/Standards_DisableSelfServiceLicenses/run.ps1
index 55d3b2e82799..50f3be239b63 100644
--- a/Standards_DisableSelfServiceLicenses/run.ps1
+++ b/Standards_DisableSelfServiceLicenses/run.ps1
@@ -1,18 +1,7 @@
param($tenant)
try {
- $AADGraphtoken = (Get-GraphToken -scope 'https://graph.windows.net/.default')
- $tenantid = (Get-Tenants | Where-Object -Property defaultDomainName -EQ $TenantFilter).customerId
- $TrackingGuid = (New-Guid).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'
- $DisableSelfServiceBody = @"
-http://provisioning.microsoftonline.com/IProvisioningWebService/SetCompanySettingsurn: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.svcVersion16false
-"@
- $DisableSelfService = (Invoke-RestMethod -Uri 'https://provisioningapi.microsoftonline.com/provisioningwebservice.svc' -Method post -Body $DisableSelfServiceBody -ContentType 'application/soap+xml; charset=utf-8')
- Write-LogMessage "Standards API: $($tenant) Disabled Self Service for licenses" -sev Info
+ Write-LogMessage "Standards API: $($tenant) failed to disable License Buy Self Service: $($exception.message)" -sev Error
}
catch {
diff --git a/Standards_DisableTenantCreation/run.ps1 b/Standards_DisableTenantCreation/run.ps1
index accfc0c6a96c..39e4057fea98 100644
--- a/Standards_DisableTenantCreation/run.ps1
+++ b/Standards_DisableTenantCreation/run.ps1
@@ -6,5 +6,5 @@ try {
Write-LogMessage -API "Standards" -tenant $tenant -message "Standards API: Disabled users from creating tenants." -sev Info
}
catch {
- Write-LogMessage -API "Standards" -tenant $tenant -message "Failed to disable users from creating tenants: $($_.exception.message)"
+ Write-LogMessage -API "Standards" -tenant $tenant -message "Failed to disable users from creating tenants: $($_.exception.message)" -sev "Error"
}
diff --git a/standards_ExConnector/function.json b/Standards_ExConnector/function.json
similarity index 100%
rename from standards_ExConnector/function.json
rename to Standards_ExConnector/function.json
diff --git a/standards_ExConnector/run.ps1 b/Standards_ExConnector/run.ps1
similarity index 100%
rename from standards_ExConnector/run.ps1
rename to Standards_ExConnector/run.ps1
diff --git a/standards_GroupTemplate/function.json b/Standards_GroupTemplate/function.json
similarity index 100%
rename from standards_GroupTemplate/function.json
rename to Standards_GroupTemplate/function.json
diff --git a/standards_GroupTemplate/run.ps1 b/Standards_GroupTemplate/run.ps1
similarity index 98%
rename from standards_GroupTemplate/run.ps1
rename to Standards_GroupTemplate/run.ps1
index 5fded6c895b6..86f2154e1e08 100644
--- a/standards_GroupTemplate/run.ps1
+++ b/Standards_GroupTemplate/run.ps1
@@ -54,7 +54,7 @@ foreach ($Template in $Setting.TemplateList) {
}
}
catch {
- Write-LogMessage -API "Standards" -tenant $tenant -message "Failed to create group: $($_.exception.message)"
+ Write-LogMessage -API "Standards" -tenant $tenant -message "Failed to create group: $($_.exception.message)" -sev "Error"
}
}
diff --git a/standards_IntuneTemplate/function.json b/Standards_IntuneTemplate/function.json
similarity index 100%
rename from standards_IntuneTemplate/function.json
rename to Standards_IntuneTemplate/function.json
diff --git a/standards_IntuneTemplate/run.ps1 b/Standards_IntuneTemplate/run.ps1
similarity index 98%
rename from standards_IntuneTemplate/run.ps1
rename to Standards_IntuneTemplate/run.ps1
index 8639e23d014f..1db59ef6e28e 100644
--- a/standards_IntuneTemplate/run.ps1
+++ b/Standards_IntuneTemplate/run.ps1
@@ -80,9 +80,9 @@ foreach ($Template in $Setting.TemplateList) {
$assign = New-GraphPOSTRequest -uri "https://graph.microsoft.com/beta/deviceManagement/$TemplateTypeURL('$($CreateRequest.id)')/assign" -tenantid $tenant -type POST -body $AssignBody
Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $($Tenant) -message "Assigned policy $($Displayname) to $AssignTo" -Sev "Info"
}
- Write-LogMessage -API "Standards" -tenant $tenant -message "Successfully added Intune Template policy for $($Tenant)"
+ Write-LogMessage -API "Standards" -tenant $tenant -message "Successfully added Intune Template policy for $($Tenant)" -sev "Info"
}
catch {
- Write-LogMessage -API "Standards" -tenant $tenant -message "Failed to create or update Intune Template: $($_.exception.message)"
+ Write-LogMessage -API "Standards" -tenant $tenant -message "Failed to create or update Intune Template: $($_.exception.message)" -sev "Error"
}
-}
\ No newline at end of file
+}
diff --git a/Standards_LegacyMFA/run.ps1 b/Standards_LegacyMFA/run.ps1
index 3ab557973f42..2347a529547d 100644
--- a/Standards_LegacyMFA/run.ps1
+++ b/Standards_LegacyMFA/run.ps1
@@ -20,5 +20,5 @@ try {
}
catch {
- Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to enable (legacy) per user MFA: $($_.exception.message)"
+ Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to enable (legacy) per user MFA: $($_.exception.message)" -sev "Error"
}
\ No newline at end of file
diff --git a/Standards_LegacyMFACleanup/run.ps1 b/Standards_LegacyMFACleanup/run.ps1
index b6c1f4a84855..ba87338262d1 100644
--- a/Standards_LegacyMFACleanup/run.ps1
+++ b/Standards_LegacyMFACleanup/run.ps1
@@ -59,5 +59,5 @@ try {
}
}
catch {
- Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to clean up (legacy) per user MFA: $($_.exception.message)"
-}
+ Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to clean up (legacy) per user MFA: $($_.exception.message)" -sev "Error"
+}
\ No newline at end of file
diff --git a/Standards_PWdisplayAppInformationRequiredState/run.ps1 b/Standards_PWdisplayAppInformationRequiredState/run.ps1
index 449c10737ef3..e8dc974cf7d1 100644
--- a/Standards_PWdisplayAppInformationRequiredState/run.ps1
+++ b/Standards_PWdisplayAppInformationRequiredState/run.ps1
@@ -9,5 +9,5 @@ try {
Write-LogMessage -API "Standards" -tenant $tenant -message "Enabled passwordless with Information and Number Matching." -sev Info
}
catch {
- Write-LogMessage -API "Standards" -tenant $tenant -message "Failed to enable passwordless with Information and Number Matching. Error: $($_.exception.message)"
+ Write-LogMessage -API "Standards" -tenant $tenant -message "Failed to enable passwordless with Information and Number Matching. Error: $($_.exception.message)" -sev "Error"
}
\ No newline at end of file
diff --git a/Standards_PWnumberMatchingRequiredState/run.ps1 b/Standards_PWnumberMatchingRequiredState/run.ps1
index 1656aaaaeb06..18aa87eef461 100644
--- a/Standards_PWnumberMatchingRequiredState/run.ps1
+++ b/Standards_PWnumberMatchingRequiredState/run.ps1
@@ -9,5 +9,5 @@ try {
Write-LogMessage -API "Standards" -tenant $tenant -message "Enabled passwordless with Number Matching." -sev Info
}
catch {
- Write-LogMessage -API "Standards" -tenant $tenant -message "Failed to enable passwordless with Number Matching. Error: $($_.exception.message)"
+ Write-LogMessage -API "Standards" -tenant $tenant -message "Failed to enable passwordless with Number Matching. Error: $($_.exception.message)" -sev "Error"
}
\ No newline at end of file
diff --git a/Standards_SSPR/run.ps1 b/Standards_SSPR/run.ps1
index 4f3165913a56..3b0bbb7da943 100644
--- a/Standards_SSPR/run.ps1
+++ b/Standards_SSPR/run.ps1
@@ -3,5 +3,5 @@ try {
Write-LogMessage -API "Standards" -tenant $tenant -message "SSPR standard is no longer available" -sev Error
}
catch {
- Write-LogMessage -API "Standards" -tenant $tenant -message "Failed to enable SSPR $($_.exception.message)"
+ Write-LogMessage -API "Standards" -tenant $tenant -message "Failed to enable SSPR $($_.exception.message)" -sev "Error"
}
\ No newline at end of file
diff --git a/Standards_SecurityDefaults/run.ps1 b/Standards_SecurityDefaults/run.ps1
index e3ed1c5476e6..09bb20f772e7 100644
--- a/Standards_SecurityDefaults/run.ps1
+++ b/Standards_SecurityDefaults/run.ps1
@@ -11,5 +11,5 @@ try {
Write-LogMessage -API "Standards" -tenant $tenant -message "Standards API: Security Defaults Enabled." -sev Info
}
catch {
- Write-LogMessage -API "Standards" -tenant $tenant -message "Failed to enable Security Defaults Error: $($_.exception.message)"
-}
+ Write-LogMessage -API "Standards" -tenant $tenant -message "Failed to enable Security Defaults Error: $($_.exception.message)" -sev "Error"
+}
\ No newline at end of file
diff --git a/standards_TransportRuleTemplate/function.json b/Standards_TransportRuleTemplate/function.json
similarity index 100%
rename from standards_TransportRuleTemplate/function.json
rename to Standards_TransportRuleTemplate/function.json
diff --git a/standards_TransportRuleTemplate/run.ps1 b/Standards_TransportRuleTemplate/run.ps1
similarity index 89%
rename from standards_TransportRuleTemplate/run.ps1
rename to Standards_TransportRuleTemplate/run.ps1
index 636a51c656df..41212eb09b4e 100644
--- a/standards_TransportRuleTemplate/run.ps1
+++ b/Standards_TransportRuleTemplate/run.ps1
@@ -19,17 +19,17 @@ foreach ($Template in $Setting.TemplateList) {
Write-Host "Found existing"
$RequestParams | Add-Member -NotePropertyValue $RequestParams.name -NotePropertyName Identity
$GraphRequest = New-ExoRequest -tenantid $Tenant -cmdlet "Set-TransportRule" -cmdParams ($RequestParams | Select-Object -Property * -ExcludeProperty UseLegacyRegex) -useSystemMailbox $true
- Write-LogMessage -API "Standards" -tenant $tenant -message "Successfully set transport rule for $tenant"
+ Write-LogMessage -API "Standards" -tenant $tenant -message "Successfully set transport rule for $tenant" -sev "Info"
}
else {
Write-Host "Creating new"
$GraphRequest = New-ExoRequest -tenantid $Tenant -cmdlet "New-TransportRule" -cmdParams $RequestParams -useSystemMailbox $true
- Write-LogMessage -API "Standards" -tenant $tenant -message "Successfully created transport rule for $tenant"
+ Write-LogMessage -API "Standards" -tenant $tenant -message "Successfully created transport rule for $tenant" -sev "Info"
}
- Write-LogMessage -API $APINAME -tenant $Tenant -message "Created transport rule for $($tenantfilter)" -sev Debug
+ Write-LogMessage -API $APINAME -tenant $Tenant -message "Created transport rule for $($tenantfilter)" -sev "Debug"
}
catch {
- Write-LogMessage -API "Standards" -tenant $tenant -message "Could not create transport rule for $($tenantfilter): $($_.Exception.message)"
+ Write-LogMessage -API "Standards" -tenant $tenant -message "Could not create transport rule for $($tenantfilter): $($_.Exception.message)" -sev "Error"
}
}
\ No newline at end of file
diff --git a/UpdatePermissionsQueue/run.ps1 b/UpdatePermissionsQueue/run.ps1
index a31c4d6776d6..4853558dde82 100644
--- a/UpdatePermissionsQueue/run.ps1
+++ b/UpdatePermissionsQueue/run.ps1
@@ -37,8 +37,7 @@ $GraphRequest = $ExpectedPermissions.requiredResourceAccess | ForEach-Object {
$AppBody = @"
{
"ApplicationGrants":[ $(ConvertTo-Json -InputObject $RequiredCPVPerms -Compress -Depth 10)],
- "ApplicationId": "$($env:ApplicationID)",
- "DisplayName": "CIPP-SAM"
+ "ApplicationId": "$($env:ApplicationID)"
}
"@
$CPVConsent = New-GraphpostRequest -body $AppBody -Type POST -noauthcheck $true -uri "https://api.partnercenter.microsoft.com/v1/customers/$($TenantFilter.customerId)/applicationconsents" -scope "https://api.partnercenter.microsoft.com/.default" -tenantid $env:TenantID
@@ -53,6 +52,33 @@ $GraphRequest = $ExpectedPermissions.requiredResourceAccess | ForEach-Object {
"Could not set CPV permissions for $PermissionsName. Does the Tenant have a license for this API? Error: $($_.Exception.message)"
}
}
+$ourSVCPrincipal = New-GraphGETRequest -uri "https://graph.microsoft.com/beta/servicePrincipals(appId='$($ENV:applicationid)')" -tenantid $Tenantfilter
+
+# if the app svc principal exists, consent app permissions
+$apps = $ExpectedPermissions
+$Grants = foreach ($App in $apps.requiredResourceAccess) {
+ try {
+ $svcPrincipalId = New-GraphGETRequest -uri "https://graph.microsoft.com/v1.0/servicePrincipals(appId='$($app.resourceAppId)')" -tenantid $tenantfilter
+ }
+ catch {
+ continue
+ }
+ foreach ($SingleResource in $app.ResourceAccess | Where-Object -Property Type -EQ "Role") {
+ [pscustomobject]@{
+ principalId = $($ourSVCPrincipal.id)
+ resourceId = $($svcPrincipalId.id)
+ appRoleId = "$($SingleResource.Id)"
+ }
+ }
+}
+foreach ($Grant in $grants) {
+ try {
+ $SettingsRequest = New-GraphPOSTRequest -body ($grant | ConvertTo-Json) -uri "https://graph.microsoft.com/beta/servicePrincipals/$($ourSVCPrincipal.id)/appRoleAssignedTo" -tenantid $tenantfilter -type POST
+ }
+ catch {
+ "Failed to grant $($grant.appRoleId) to $($grant.resourceId): $($_.Exception.Message). "
+ }
+}
$GraphRequest = @{
LastApply = "$((Get-Date).ToString())"
diff --git a/profile.ps1 b/profile.ps1
index 069460965fdd..7e80bf71358f 100644
--- a/profile.ps1
+++ b/profile.ps1
@@ -14,6 +14,9 @@
Import-Module .\GraphHelper.psm1
Import-Module Az.KeyVault
Import-Module Az.Accounts
+Import-Module GraphRequests
+Import-Module CippExtensions
+Import-module CippCore
try {
Disable-AzContextAutosave -Scope Process | Out-Null
diff --git a/version_latest.txt b/version_latest.txt
index d1428a7e96ac..47b6be3fafec 100644
--- a/version_latest.txt
+++ b/version_latest.txt
@@ -1 +1 @@
-3.6.1
\ No newline at end of file
+3.7.2
\ No newline at end of file