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