diff --git a/AddCAPolicy/run.ps1 b/AddCAPolicy/run.ps1 index 0e9785487833..ae7ec4a3584d 100644 --- a/AddCAPolicy/run.ps1 +++ b/AddCAPolicy/run.ps1 @@ -8,54 +8,10 @@ Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME - $Tenants = ($Request.body | Select-Object Select_*).psobject.properties.value if ("AllTenants" -in $Tenants) { $Tenants = (Get-Tenants).defaultDomainName } -$displayname = ($request.body.RawJSON | ConvertFrom-Json).Displayname -function Remove-EmptyArrays ($Object) { - if ($Object -is [Array]) { - foreach ($Item in $Object) { Remove-EmptyArrays $Item } - } - elseif ($Object -is [HashTable]) { - foreach ($Key in @($Object.get_Keys())) { - if ($Object[$Key] -is [Array] -and $Object[$Key].get_Count() -eq 0) { - $Object.Remove($Key) - } - else { Remove-EmptyArrays $Object[$Key] } - } - } - elseif ($Object -is [PSCustomObject]) { - foreach ($Name in @($Object.psobject.properties.Name)) { - if ($Object.$Name -is [Array] -and $Object.$Name.get_Count() -eq 0) { - $Object.PSObject.Properties.Remove($Name) - } - elseif ($object.$name -eq $null) { - $Object.PSObject.Properties.Remove($Name) - } - else { Remove-EmptyArrays $Object.$Name } - } - } -} - -$JSONObj = $request.body.RawJSON | ConvertFrom-Json | Select-Object * -ExcludeProperty ID, GUID, *time* -Remove-EmptyArrays $JSONObj -#Remove context as it does not belong in the payload. -$JsonObj.grantControls.PSObject.Properties.Remove('authenticationStrength@odata.context') -if ($JSONObj.conditions.users.excludeGuestsOrExternalUsers.externalTenants.Members) { - $JsonObj.conditions.users.excludeGuestsOrExternalUsers.externalTenants.PSObject.Properties.Remove('@odata.context') - $JsonObj.conditions.users.excludeGuestsOrExternalUsers.externalTenants.PSObject.Properties.Remove('@odata.type') -} -if ($Request.body.newstate -and $Request.body.newstate -ne 'donotchange') { - $Jsonobj.state = $Request.body.newstate -} -$RawJSON = $JSONObj | ConvertTo-Json -Depth 10 $results = foreach ($Tenant in $tenants) { try { - $CheckExististing = New-GraphGETRequest -uri "https://graph.microsoft.com/v1.0/identity/conditionalAccess/policies" -tenantid $tenant - $PolicyName = ($RawJSON | ConvertFrom-Json).displayName - if ($PolicyName -in $CheckExististing.displayName) { - Throw "Conditional Access Policy with Display Name $($Displayname) Already exists" - } - - $CreateRequest = New-GraphPOSTRequest -uri "https://graph.microsoft.com/beta/identity/conditionalAccess/policies" -tenantid $tenant -type POST -body $RawJSON + $CAPolicy = New-CIPPCAPolicy -TenantFilter $tenant -state $request.body.NewState -RawJSON $Request.body.RawJSON -APIName $APIName -ExecutingUser $request.headers.'x-ms-client-principal' Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $($Tenant) -message "Added Conditional Access Policy $($Displayname)" -Sev "Error" "Successfully added Conditional Access Policy for $($Tenant)" } diff --git a/AddCATemplate/run.ps1 b/AddCATemplate/run.ps1 index 08991414ae6b..19d45859c879 100644 --- a/AddCATemplate/run.ps1 +++ b/AddCATemplate/run.ps1 @@ -5,8 +5,8 @@ param($Request, $TriggerMetadata) $APIName = $TriggerMetadata.FunctionName Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Accessed this API" -Sev "Debug" -Write-Host ($request | ConvertTo-Json -Compress) +$TenantFilter = $Request.Query.TenantFilter try { $GUID = (New-Guid).GUID $JSON = if ($request.body.rawjson) { @@ -18,7 +18,28 @@ try { $_ | Select-Object -Property $NonEmptyProperties } } - $JSON = ($JSON | ConvertTo-Json -Depth 10) + + $includelocations = New-Object System.Collections.ArrayList + $IncludeJSON = foreach ($Location in $JSON.conditions.locations.includeLocations) { + $locationinfo = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/identity/conditionalAccess/namedLocations" -tenantid $TenantFilter | Where-Object -Property id -EQ $location | Select-Object * -ExcludeProperty id, *time* + $null = if ($locationinfo) { $includelocations.add($locationinfo.displayName) } else { $includelocations.add($location) } + $locationinfo + } + if ($includelocations) { $JSON.conditions.locations.includeLocations = $includelocations } + + + $excludelocations = New-Object System.Collections.ArrayList + $ExcludeJSON = foreach ($Location in $JSON.conditions.locations.excludeLocations) { + $locationinfo = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/identity/conditionalAccess/namedLocations" -tenantid $TenantFilter | Where-Object -Property id -EQ $location | Select-Object * -ExcludeProperty id, *time* + $null = if ($locationinfo) { $excludelocations.add($locationinfo.displayName) } else { $excludelocations.add($location) } + $locationinfo + } + + if ($excludelocations) { $JSON.conditions.locations.excludeLocations = $excludelocations } + + $JSON | Add-Member -NotePropertyName 'LocationInfo' -NotePropertyValue @($IncludeJSON, $ExcludeJSON) + + $JSON = ($JSON | ConvertTo-Json -Depth 100) $Table = Get-CippTable -tablename 'templates' $Table.Force = $true Add-AzDataTableEntity @Table -Entity @{ diff --git a/AddNamedLocation/run.ps1 b/AddNamedLocation/run.ps1 index a21d0e148829..723b573c8bb3 100644 --- a/AddNamedLocation/run.ps1 +++ b/AddNamedLocation/run.ps1 @@ -13,6 +13,7 @@ Write-Host "PowerShell HTTP trigger function processed a request." # Input bindings are passed in via param block. $Tenants = $request.body.selectedTenants.defaultDomainName Write-Host ($Request.body | ConvertTo-Json) +if ($Tenants -eq "AllTenants") { $Tenants = (Get-Tenants).defaultDomainName } $results = foreach ($Tenant in $tenants) { try { $ObjBody = if ($Request.body.Type -eq "IPLocation") { diff --git a/Config/CIPPDefaultTable.BPATemplate.json b/Config/CIPPDefaultTable.BPATemplate.json index 4c5e653ee1e6..7261b2b95ab6 100644 --- a/Config/CIPPDefaultTable.BPATemplate.json +++ b/Config/CIPPDefaultTable.BPATemplate.json @@ -20,9 +20,9 @@ { "name": "OAuthAppConsent", "API": "Graph", - "URL": "https://graph.microsoft.com/beta/policies/authorizationPolicy/authorizationPolicy", - "ExtractFields": "permissionGrantPolicyIdsAssignedToDefaultUserRole", - "where": "'ManagePermissionGrantsForSelf.microsoft-user-default-legacy' -notin $_.permissionGrantPolicyIdsAssignedToDefaultUserRole", + "URL": "https://graph.microsoft.com/v1.0/policies/authorizationPolicy?$select=defaultUserRolePermissions", + "ExtractFields": "defaultuserrolepermissions", + "where": "'ManagePermissionGrantsForSelf.microsoft-user-default-legacy' -notin $_.defaultuserrolepermissions.permissionGrantPoliciesAssigned", "StoreAs": "bool", "FrontendFields": [ { diff --git a/DomainAnalyser_GetTenantDomains/run.ps1 b/DomainAnalyser_GetTenantDomains/run.ps1 index def86a78461b..333d50847e0a 100644 --- a/DomainAnalyser_GetTenantDomains/run.ps1 +++ b/DomainAnalyser_GetTenantDomains/run.ps1 @@ -9,7 +9,7 @@ $TenantDomains = $Tenants | ForEach-Object -Parallel { $Tenant = $_ # Get Domains to Lookup try { - $Domains = New-GraphGetRequest -uri 'https://graph.microsoft.com/v1.0/domains' -tenantid $Tenant.defaultDomainName | Where-Object { ($_.id -notlike '*.onmicrosoft.com' -and $_.id -notlike '*.microsoftonline.com' -and $_.id -NotLike '*.exclaimer.cloud' -and $_.id -NotLike '*.codetwo.online' -and $_.id -NotLike '*.call2teams.com' -and $_.isVerified) } + $Domains = New-GraphGetRequest -uri 'https://graph.microsoft.com/v1.0/domains' -tenantid $Tenant.defaultDomainName | Where-Object { ($_.id -notlike '*.microsoftonline.com' -and $_.id -NotLike '*.exclaimer.cloud' -and $_.id -NotLike '*.codetwo.online' -and $_.id -NotLike '*.call2teams.com' -and $_.isVerified) } foreach ($d in $domains) { [PSCustomObject]@{ Tenant = $Tenant.defaultDomainName @@ -23,8 +23,7 @@ $TenantDomains = $Tenants | ForEach-Object -Parallel { SupportedServices = $d.supportedServices } } - } - catch { + } catch { Write-LogMessage -API 'DomainAnalyser' -tenant $tenant.defaultDomainName -message "DNS Analyser GraphGetRequest Exception: $($_.Exception.Message)" -sev Error } } | Sort-Object -Unique -Property Domain @@ -40,7 +39,6 @@ foreach ($Exclude in $ExcludedTenants) { } } - $TenantCount = ($TenantDomains | Measure-Object).Count if ($TenantCount -gt 0) { Write-Host "$TenantCount tenant Domains" @@ -75,8 +73,7 @@ if ($TenantCount -gt 0) { $DomainObject.MailProviders = $OldDomain.MailProviders } $Domain = $DomainObject - } - else { + } else { $Domain.TenantDetails = $TenantDetails if ($OldDomain) { $Domain.DkimSelectors = $OldDomain.DkimSelectors @@ -90,8 +87,6 @@ if ($TenantCount -gt 0) { # Batch insert all tenant domains try { Add-AzDataTableEntity @DomainTable -Entity $TenantDomainObjects -Force - } - 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)" -sev "Error"} + } 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)" -sev 'Error' } } diff --git a/EditTenant/run.ps1 b/EditTenant/run.ps1 index 1962da601c60..c4c113433c59 100644 --- a/EditTenant/run.ps1 +++ b/EditTenant/run.ps1 @@ -17,7 +17,7 @@ $tokens = try { $tenantObjectId = $allTenantsDetails.value | Where-Object { $_.customerContextId -eq $customerContextId } | Select-Object 'objectId' } catch { - "Failed to retrieve list of tenants. Error: $($_.Exception.Message)" + $Results = "Failed to retrieve list of tenants. Error: $($_.Exception.Message)" Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $($tenantDisplayName) -message "Failed to retrieve list of tenants. Error: $($_.Exception.Message)" -Sev 'Error' } @@ -27,18 +27,26 @@ if ($tenantObjectId) { $bodyToPatch = '{"displayName":"' + $tenantDisplayName + '","defaultDomainName":"' + $tenantDefaultDomainName + '"}' $patchTenant = (Invoke-RestMethod -Method PATCH -Uri "https://graph.windows.net/myorganization/contracts/$($tenantObjectId.objectId)?api-version=1.6" -Body $bodyToPatch -ContentType 'application/json' -Headers $AADGraphtoken -ErrorAction Stop) $Filter = "PartitionKey eq 'Tenants' and defaultDomainName eq '{0}'" -f $tenantDefaultDomainName - $TenantsTable = Get-CippTable -tablename Tenants - $Tenant = Get-AzDataTableEntity @TenantsTable -Filter $Filter - $Tenant.displayName = $tenantDisplayName - Update-AzDataTableEntity @TenantsTable -Entity $Tenant + try { + $TenantsTable = Get-CippTable -tablename Tenants + $Tenant = Get-AzDataTableEntity @TenantsTable -Filter $Filter + $Tenant.displayName = $tenantDisplayName + Update-AzDataTableEntity @TenantsTable -Entity $Tenant + } + catch { + $AddedText = "but could not edit the tenant cache. Clear the tenant cache to display the updated details" + } Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $tenantDisplayName -message "Edited tenant $tenantDisplayName" -Sev 'Info' - $results = "Successfully amended details for $($Tenant.displayName)" + $results = "Successfully amended details for $($Tenant.displayName) $AddedText" } catch { $results = "Failed to amend details for $tenantDisplayName : $($_.Exception.Message)" Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $tenantDisplayName -message "Failed amending details $tenantDisplayName. Error: $($_.Exception.Message)" -Sev 'Error' } } +else { + $Results = "Could not find the tenant to edit in the contract endpoint. Please ensure you have a reseller relationship with the tenant you are trying to edit." +} $body = [pscustomobject]@{'Results' = $results } diff --git a/ExecAccessChecks/run.ps1 b/ExecAccessChecks/run.ps1 index 216027ff5a56..3516ae88709d 100644 --- a/ExecAccessChecks/run.ps1 +++ b/ExecAccessChecks/run.ps1 @@ -43,7 +43,7 @@ if ($Request.query.Permissions -eq 'true') { $KeyVaultRefresh = Get-AzKeyVaultSecret -VaultName $kv -Name 'RefreshToken' -AsPlainText if ($ENV:RefreshToken -ne $KeyVaultRefresh) { $Success = $false - $Messages.Add('Your refresh token does not match key vault, follow the Clear Token Cache procedure.') | Out-Null + $Messages.Add('Your refresh token does not match key vault, clear your cache or wait 30 minutes.') | Out-Null $Links.Add([PSCustomObject]@{ Text = 'Clear Token Cache' Href = 'https://cipp.app/docs/general/troubleshooting/#clear-token-cache' diff --git a/ExecCPVPermissions/run.ps1 b/ExecCPVPermissions/run.ps1 index c70a37e2217f..9948143c1908 100644 --- a/ExecCPVPermissions/run.ps1 +++ b/ExecCPVPermissions/run.ps1 @@ -90,10 +90,10 @@ foreach ($Grant in $grants) { "Failed to grant $($grant.appRoleId) to $($grant.resourceId): $($_.Exception.Message). " } } -$StatusCode = [HttpStatusCode]::OK + # Associate values to output bindings by calling 'Push-OutputBinding'. Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = $StatusCode - Body = @(@{Results = $GraphRequest }) + StatusCode = [HttpStatusCode]::OK + Body = @{Results = $GraphRequest } }) diff --git a/ExecDnsConfig/run.ps1 b/ExecDnsConfig/run.ps1 index 0dbe45a7b1e7..fc76d2772178 100644 --- a/ExecDnsConfig/run.ps1 +++ b/ExecDnsConfig/run.ps1 @@ -42,8 +42,7 @@ try { if ($ValidResolvers -contains $Resolver) { try { $Config.Resolver = $Resolver - } - catch { + } catch { $Config = @{ Resolver = $Resolver } @@ -53,28 +52,48 @@ try { } if ($updated) { Add-AzDataTableEntity @ConfigTable -Entity $Config -Force - Write-LogMessage -API $APINAME -tenant 'Global' -user $request.headers.'x-ms-client-principal' -message 'DNS configuration updated' -Sev 'Info' + Write-LogMessage -API $APINAME -tenant 'Global' -user $request.headers.'x-ms-client-principal' -message 'DNS configuration updated' -Sev 'Info' $body = [pscustomobject]@{'Results' = 'Success: DNS configuration updated.' } - } - else { + } else { $StatusCode = [HttpStatusCode]::BadRequest $body = [pscustomobject]@{'Results' = 'Error: No DNS resolver provided.' } } } + 'SetDkimConfig' { + $Domain = $Request.Query.Domain + $Selector = ($Request.Query.Selector).trim() -split '\s*,\s*' + $DomainTable = Get-CIPPTable -Table 'Domains' + $Filter = "RowKey eq '{0}'" -f $Domain + $DomainInfo = Get-AzDataTableEntity @DomainTable -Filter $Filter + $DkimSelectors = [string]($Selector | ConvertTo-Json -Compress) + if ($DomainInfo) { + $DomainInfo.DkimSelectors = $DkimSelectors + } else { + $DomainInfo = @{ + 'RowKey' = $Request.Query.Domain + 'PartitionKey' = 'ManualEntry' + 'TenantId' = 'NoTenant' + 'MailProviders' = '' + 'TenantDetails' = '' + 'DomainAnalyser' = '' + 'DkimSelectors' = $DkimSelectors + } + } + Add-AzDataTableEntity @DomainTable -Entity $DomainInfo -Force + } 'GetConfig' { $body = [pscustomobject]$Config - Write-LogMessage -API $APINAME -tenant 'Global' -user $request.headers.'x-ms-client-principal' -message 'Retrieved DNS configuration' -Sev 'Info' + Write-LogMessage -API $APINAME -tenant 'Global' -user $request.headers.'x-ms-client-principal' -message 'Retrieved DNS configuration' -Sev 'Info' } 'RemoveDomain' { $Filter = "RowKey eq '{0}'" -f $Request.Query.Domain - $DomainRow = Get-AzDataTableEntity @DomainTable -Filter $Filter + $DomainRow = Get-AzDataTableEntity @DomainTable -Filter $Filter Remove-AzDataTableEntity @DomainTable -Entity $DomainRow Write-LogMessage -API $APINAME -tenant 'Global' -user $request.headers.'x-ms-client-principal' -message "Removed Domain - $($Request.Query.Domain) " -Sev 'Info' $body = [pscustomobject]@{ 'Results' = "Domain removed - $($Request.Query.Domain)" } } } -} -catch { +} catch { Write-LogMessage -API $APINAME -tenant $($name) -user $request.headers.'x-ms-client-principal' -message "DNS Config API failed. $($_.Exception.Message)" -Sev 'Error' $body = [pscustomobject]@{'Results' = "Failed. $($_.Exception.Message)" } $StatusCode = [HttpStatusCode]::BadRequest diff --git a/ExecExtensionsConfig/run.ps1 b/ExecExtensionsConfig/run.ps1 index 1d805b34ec28..54ddde3e3c63 100644 --- a/ExecExtensionsConfig/run.ps1 +++ b/ExecExtensionsConfig/run.ps1 @@ -26,7 +26,7 @@ $results = try { if ($request.body.$APIKey.APIKey) { $null = Set-AzKeyVaultSecret -VaultName $ENV:WEBSITE_DEPLOYMENT_ID -Name $APIKey -SecretValue (ConvertTo-SecureString -String $request.body.$APIKey.APIKey -AsPlainText -Force) } - $request.body.$APIKey = @{ APIKey = "SentToKeyVault" } + $request.body.$APIKey.APIKey = "SentToKeyVault" } } $body = $request.body | Select-Object * -ExcludeProperty APIKey, Enabled | ConvertTo-Json -Depth 10 -Compress diff --git a/ExecGDAPInviteApproved_Timer/function.json b/ExecGDAPInviteApproved_Timer/function.json index 29e66b24b4ce..6b68992375e9 100644 --- a/ExecGDAPInviteApproved_Timer/function.json +++ b/ExecGDAPInviteApproved_Timer/function.json @@ -4,7 +4,7 @@ "name": "Timer", "type": "timerTrigger", "direction": "in", - "schedule": "0 0 */3 * * 0" + "schedule": "0 0 */3 * * *" }, { "type": "queue", diff --git a/ExecSAMSetup/run.ps1 b/ExecSAMSetup/run.ps1 index 1dc03af180f8..c7a15b0f9a1e 100644 --- a/ExecSAMSetup/run.ps1 +++ b/ExecSAMSetup/run.ps1 @@ -28,6 +28,11 @@ if ($env:MSI_SECRET) { Disable-AzContextAutosave -Scope Process | Out-Null $AzSession = Connect-AzAccount -Identity } +if (!$ENV:SetFromProfile) { + Write-Host "We're reloading from KV" + Get-CIPPAuthentication +} + $KV = $ENV:WEBSITE_DEPLOYMENT_ID $Table = Get-CIPPTable -TableName SAMWizard $Rows = Get-AzDataTableEntity @Table | Where-Object -Property Timestamp -GT (Get-Date).AddMinutes(-10) @@ -40,7 +45,7 @@ try { if ($request.body.RefreshToken) { Set-AzKeyVaultSecret -VaultName $kv -Name 'RefreshToken' -SecretValue (ConvertTo-SecureString -String $request.body.RefreshToken -AsPlainText -Force) } if ($request.body.applicationid) { Set-AzKeyVaultSecret -VaultName $kv -Name 'applicationid' -SecretValue (ConvertTo-SecureString -String $request.body.applicationid -AsPlainText -Force) } if ($request.body.applicationsecret) { Set-AzKeyVaultSecret -VaultName $kv -Name 'applicationsecret' -SecretValue (ConvertTo-SecureString -String $request.body.applicationsecret -AsPlainText -Force) } - $Results = @{ Results = "Replaced keys successfully. Please clear your token cache or wait 24 hours for the cache to be cleared." } + $Results = @{ Results = "The keys have been replaced. Please perform a permissions check." } } if ($Request.query.error -eq 'invalid_client') { $Results = "Client ID was not found in Azure. Try waiting 10 seconds to try again, if you have gotten this error after 5 minutes, please restart the process." } if ($request.query.code) { @@ -177,7 +182,7 @@ try { Remove-AzDataTableEntity @Table -Entity $Rows $step = 5 - $Results = @{"message" = "Installation completed. You must perform a token cache clear. For instructions click "; step = $step ; url = "https://cipp.app/docs/general/troubleshooting/#clear-token-cache" + $Results = @{"message" = "Installation completed."; step = $step } } } diff --git a/GraphHelper.psm1 b/GraphHelper.psm1 index 283912865ef0..8cb6ede0850e 100644 --- a/GraphHelper.psm1 +++ b/GraphHelper.psm1 @@ -23,10 +23,10 @@ function Get-NormalizedError { '*Provide valid credential.*' { 'Error 400: There is an issue with your Exchange Token configuration. Please perform an access check for this tenant' } '*This indicate that a subscription within the tenant has lapsed*' { 'There is no exchange subscription available, or it has lapsed. Check licensing information.' } '*User was not found.*' { 'The relationship between this tenant and the partner has been dissolved from the tenant side.' } - '*The user or administrator has not consented to use the application*' { 'Please perform a CPV permissions refresh, or you are using GDAP and have not added the CIPP user to the correct group.' } + '*The user or administrator has not consented to use the application*' { 'CIPP cannot access this tenant. Perform a CPV Refresh and Access Check via the settings menu' } '*AADSTS50020*' { 'AADSTS50020: The user you have used for your Secure Application Model is a guest in this tenant, or your are using GDAP and have not added the user to the correct group. Please delete the guest user to gain access to this tenant' } '*AADSTS50177' { 'AADSTS50177: The user you have used for your Secure Application Model is a guest in this tenant, or your are using GDAP and have not added the user to the correct group. Please delete the guest user to gain access to this tenant' } - '*invalid or malformed*' { 'The request is malformed. You have entered incorrect tokens or have not performed a clear of the token cache after entering new tokens. Please see the troubleshooting documentation on how to execute a clear of the token cache.' } + '*invalid or malformed*' { 'The request is malformed. Have you finished the SAM Setup?' } '*Windows Store repository apps feature is not supported for this tenant*' { 'This tenant does not have WinGet support available' } '*AADSTS650051*' { 'The application does not exist yet. Try again in 30 seconds.' } '*AppLifecycle_2210*' { 'Failed to call Intune APIs: Does the tenant have a license available?' } @@ -39,7 +39,7 @@ function Get-NormalizedError { function Get-GraphToken($tenantid, $scope, $AsApp, $AppID, $refreshToken, $ReturnRefresh) { if (!$scope) { $scope = 'https://graph.microsoft.com/.default' } - + if (!$env:setfromprofile) { $CIPPAuth = Get-CIPPAuthentication; Write-Host "Could not get Refreshtoken from environment variable. Reloading token." } $AuthBody = @{ client_id = $env:ApplicationID client_secret = $env:ApplicationSecret @@ -515,7 +515,7 @@ function Remove-CIPPCache { $BPATable = Get-CippTable -tablename 'cachebpa' $ClearBPARows = Get-AzDataTableEntity @BPATable Remove-AzDataTableEntity @BPATable -Entity $ClearBPARows - + $ENV:SetFromProfile = $null $Script:SkipListCache = $Null $Script:SkipListCacheEmpty = $Null $Script:IncludedTenantsCache = $Null diff --git a/ListCAtemplates/run.ps1 b/ListCAtemplates/run.ps1 index f87eb062c7ce..6c348ce0e721 100644 --- a/ListCAtemplates/run.ps1 +++ b/ListCAtemplates/run.ps1 @@ -27,16 +27,16 @@ $Templates = Get-ChildItem "Config\*.CATemplate.json" | ForEach-Object { $Table = Get-CippTable -tablename 'templates' $Filter = "PartitionKey eq 'CATemplate'" $Templates = (Get-AzDataTableEntity @Table -Filter $Filter) | ForEach-Object { - $data = $_.JSON | ConvertFrom-Json - $data | Add-Member -NotePropertyName "GUID" -NotePropertyValue $_.GUID + $data = $_.JSON | ConvertFrom-Json -Depth 100 + $data | Add-Member -NotePropertyName "GUID" -NotePropertyValue $_.GUID -Force $data } | Sort-Object -Property displayName if ($Request.query.ID) { $Templates = $Templates | Where-Object -Property GUID -EQ $Request.query.id } - +$Templates = ConvertTo-Json -InputObject @($Templates) -Depth 100 # Associate values to output bindings by calling 'Push-OutputBinding'. Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::OK - Body = @($Templates) + Body = $Templates }) diff --git a/ListDomainHealth/run.ps1 b/ListDomainHealth/run.ps1 index 86b5462ff7c6..b4982b21ec53 100644 --- a/ListDomainHealth/run.ps1 +++ b/ListDomainHealth/run.ps1 @@ -13,8 +13,7 @@ try { $ValidResolvers = @('Google', 'CloudFlare', 'Quad9') if ($ValidResolvers -contains $Config.Resolver) { $Resolver = $Config.Resolver - } - else { + } else { $Resolver = 'Google' $Config = @{ PartitionKey = 'Domains' @@ -23,8 +22,7 @@ try { } Add-AzDataTableEntity @ConfigTable -Entity $Config -Force } -} -catch { +} catch { $Resolver = 'Google' } @@ -46,6 +44,9 @@ try { $Filter = "RowKey eq '{0}'" -f $Request.Query.Domain $DomainInfo = Get-AzDataTableEntity @DomainTable -Filter $Filter switch ($Request.Query.Action) { + 'ListDomainInfo' { + $Body = $DomainInfo + } 'GetDkimSelectors' { $Body = ($DomainInfo.DkimSelectors | ConvertFrom-Json) -join ',' } @@ -73,13 +74,12 @@ try { } if ($Request.Query.Selector) { $DkimQuery.Selectors = ($Request.Query.Selector).trim() -split '\s*,\s*' - + if ('admin' -in $UserCreds.userRoles -or 'editor' -in $UserCreds.userRoles) { $DkimSelectors = [string]($DkimQuery.Selectors | ConvertTo-Json -Compress) if ($DomainInfo) { $DomainInfo.DkimSelectors = $DkimSelectors - } - else { + } else { $DomainInfo = @{ 'RowKey' = $Request.Query.Domain 'PartitionKey' = 'ManualEntry' @@ -93,12 +93,11 @@ try { Write-Host $DomainInfo Add-AzDataTableEntity @DomainTable -Entity $DomainInfo -Force } - } - elseif (![string]::IsNullOrEmpty($DomainInfo.DkimSelectors)) { + } elseif (![string]::IsNullOrEmpty($DomainInfo.DkimSelectors)) { $DkimQuery.Selectors = [System.Collections.Generic.List[string]]($DomainInfo.DkimSelectors | ConvertFrom-Json) } $Body = Read-DkimRecord @DkimQuery - + } 'ReadMXRecord' { $Body = Read-MXRecord -Domain $Request.Query.Domain @@ -118,8 +117,7 @@ try { } if ($Request.Query.Subdomains) { $HttpsQuery.Subdomains = ($Request.Query.Subdomains).trim() -split '\s*,\s*' - } - else { + } else { $HttpsQuery.Subdomains = 'www' } @@ -132,13 +130,11 @@ try { $Body = Test-MtaSts @HttpsQuery } } - } - else { + } else { $body = [pscustomobject]@{'Results' = "Domain: $($Request.Query.Domain) is invalid" } } } -} -catch { +} catch { Write-LogMessage -API $APINAME -tenant $($name) -user $request.headers.'x-ms-client-principal' -message "DNS Helper API failed. $($_.Exception.Message)" -Sev 'Error' $body = [pscustomobject]@{'Results' = "Failed. $($_.Exception.Message)" } } diff --git a/ListLicensesAllTenants/run.ps1 b/ListLicensesAllTenants/run.ps1 index f5ca7c1f2630..16ea3176dff1 100644 --- a/ListLicensesAllTenants/run.ps1 +++ b/ListLicensesAllTenants/run.ps1 @@ -10,17 +10,20 @@ $RawGraphRequest = Get-Tenants | ForEach-Object -Parallel { Import-Module '.\Modules\AzBobbyTables' Import-Module '.\Modules\CIPPCore' try { + Write-Host "Processing $domainName" Get-CIPPLicenseOverview -TenantFilter $domainName } catch { - [pscustomobject]@{ + [pscustomobject]@{ Tenant = [string]$domainName - License = "Could not connect to client" + License = "Could not connect to client: $($_.Exception.Message)" 'PartitionKey' = 'License' - 'RowKey' = "$($domainName) - Could not connect to client" + 'RowKey' = "$($domainName)-$(New-Guid)" } } } $Table = Get-CIPPTable -TableName cachelicenses -Add-AzDataTableEntity @Table -Entity $RawGraphRequest -Force | Out-Null \ No newline at end of file +foreach ($GraphRequest in $RawGraphRequest) { + Add-AzDataTableEntity @Table -Entity $GraphRequest -Force | Out-Null +} \ No newline at end of file diff --git a/ListSharepointQuota/run.ps1 b/ListSharepointQuota/run.ps1 index f4ec77408445..df0f0abf3f0f 100644 --- a/ListSharepointQuota/run.ps1 +++ b/ListSharepointQuota/run.ps1 @@ -13,30 +13,31 @@ Write-Host 'PowerShell HTTP trigger function processed a request' $TenantFilter = $Request.Query.TenantFilter if ($Request.Query.TenantFilter -eq 'AllTenants') { - $UsedStoragePercentage = 'Not Supported' + $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) - } + $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 | Sort-Object -Property GeoUsedStorageMB -Descending | Select-Object -First 1 + + if ($sharepointQuota) { + $UsedStoragePercentage = [int](($sharepointQuota.GeoUsedStorageMB / $sharepointQuota.TenantStorageMB) * 100) + } } $sharepointQuotaDetails = @{ GeoUsedStorageMB = $sharepointQuota.GeoUsedStorageMB - TenantStorageMB = $sharepointQuota.TenantStorageMB - Percentage = $UsedStoragePercentage - Dashboard = "$($UsedStoragePercentage) / 100" + 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 + StatusCode = $StatusCode + Body = $sharepointQuotaDetails + }) \ No newline at end of file diff --git a/Modules/CIPPCore/Public/Get-CIPPAuthentication.ps1 b/Modules/CIPPCore/Public/Get-CIPPAuthentication.ps1 new file mode 100644 index 000000000000..6e4efbaf9841 --- /dev/null +++ b/Modules/CIPPCore/Public/Get-CIPPAuthentication.ps1 @@ -0,0 +1,25 @@ + +function Get-CIPPAuthentication { + [CmdletBinding()] + param ( + $APIName = "Get Keyvault Authentication" + ) + + try { + Connect-AzAccount -Identity + $ENV:applicationid = (Get-AzKeyVaultSecret -VaultName $ENV:WEBSITE_DEPLOYMENT_ID -Name "ApplicationId" -AsPlainText) + $ENV:applicationsecret = (Get-AzKeyVaultSecret -VaultName $ENV:WEBSITE_DEPLOYMENT_ID -Name "ApplicationSecret" -AsPlainText) + $ENV:tenantid = (Get-AzKeyVaultSecret -VaultName $ENV:WEBSITE_DEPLOYMENT_ID -Name "TenantId" -AsPlainText) + $ENV:refreshtoken = (Get-AzKeyVaultSecret -VaultName $ENV:WEBSITE_DEPLOYMENT_ID -Name "RefreshToken" -AsPlainText) + $ENV:SetFromProfile = $true + Write-LogMessage -message "Reloaded authentication data from KeyVault" -Sev 'info' + + return $true + } + catch { + Write-LogMessage -message "Could not retrieve keys from Keyvault: $($_.Exception.Message)" -Sev 'CRITICAL' + return $false + } +} + + diff --git a/Modules/CIPPCore/Public/New-CIPPCAPolicy.ps1 b/Modules/CIPPCore/Public/New-CIPPCAPolicy.ps1 new file mode 100644 index 000000000000..4459cb91da8f --- /dev/null +++ b/Modules/CIPPCore/Public/New-CIPPCAPolicy.ps1 @@ -0,0 +1,119 @@ + +function New-CIPPCAPolicy { + [CmdletBinding()] + param ( + $RawJSON, + $TenantFilter, + $State, + $Overwrite, + $APIName = "Create CA Policy", + $ExecutingUser + ) + function Remove-EmptyArrays ($Object) { + if ($Object -is [Array]) { + foreach ($Item in $Object) { Remove-EmptyArrays $Item } + } + elseif ($Object -is [HashTable]) { + foreach ($Key in @($Object.get_Keys())) { + if ($Object[$Key] -is [Array] -and $Object[$Key].get_Count() -eq 0) { + $Object.Remove($Key) + } + else { Remove-EmptyArrays $Object[$Key] } + } + } + elseif ($Object -is [PSCustomObject]) { + foreach ($Name in @($Object.psobject.properties.Name)) { + if ($Object.$Name -is [Array] -and $Object.$Name.get_Count() -eq 0) { + $Object.PSObject.Properties.Remove($Name) + } + elseif ($object.$name -eq $null) { + $Object.PSObject.Properties.Remove($Name) + } + else { Remove-EmptyArrays $Object.$Name } + } + } + } + + $displayname = ($RawJSON | ConvertFrom-Json).Displayname + + $JSONObj = $RawJSON | ConvertFrom-Json | Select-Object * -ExcludeProperty ID, GUID, *time* + Remove-EmptyArrays $JSONObj + #Remove context as it does not belong in the payload. + $JsonObj.grantControls.PSObject.Properties.Remove('authenticationStrength@odata.context') + if ($JSONObj.conditions.users.excludeGuestsOrExternalUsers.externalTenants.Members) { + $JsonObj.conditions.users.excludeGuestsOrExternalUsers.externalTenants.PSObject.Properties.Remove('@odata.context') + $JsonObj.conditions.users.excludeGuestsOrExternalUsers.externalTenants.PSObject.Properties.Remove('@odata.type') + } + if ($State -and $State -ne 'donotchange') { + $Jsonobj.state = $State + } + + #for each of the locations, check if they exist, if not create them. These are in $jsonobj.LocationInfo + $LocationLookupTable = foreach ($location in $jsonobj.LocationInfo) { + if (!$location.displayName) { continue } + $CheckExististing = New-GraphGETRequest -uri "https://graph.microsoft.com/beta/identity/conditionalAccess/namedLocations" -tenantid $TenantFilter + if ($Location.displayName -in $CheckExististing.displayName) { + [pscustomobject]@{ + id = ($CheckExististing | Where-Object -Property displayName -EQ $Location.displayName).id + name = ($CheckExististing | Where-Object -Property displayName -EQ $Location.displayName).displayName + } + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Matched a CA policy with the existing Named Location: $($location.displayName)" -Sev "Info" + + } + else { + $Body = ConvertTo-Json -InputObject $Location + $GraphRequest = New-GraphPOSTRequest -uri "https://graph.microsoft.com/beta/identity/conditionalAccess/namedLocations" -body $body -Type POST -tenantid $tenantfilter + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Created new Named Location: $($location.displayName)" -Sev "Info" + [pscustomobject]@{ + id = $GraphRequest.id + name = $GraphRequest.displayName + } + } + } + Write-Host ($LocationLookupTable | ConvertTo-Json) + foreach ($location in $JSONObj.conditions.locations.includeLocations) { + Write-Host "Replacting $location" + $lookup = $LocationLookupTable | Where-Object -Property name -EQ $location + Write-Host "Found $lookup" + if (!$lookup) { continue } + $index = [array]::IndexOf($JSONObj.conditions.locations.includeLocations, $location) + $JSONObj.conditions.locations.includeLocations[$index] = $lookup.id + } + + foreach ($location in $JSONObj.conditions.locations.excludeLocations) { + $lookup = $LocationLookupTable | Where-Object -Property name -EQ $location + if (!$lookup) { continue } + $index = [array]::IndexOf($JSONObj.conditions.locations.excludeLocations, $location) + $JSONObj.conditions.locations.excludeLocations[$index] = $lookup.id + } + + $JsonObj.PSObject.Properties.Remove('LocationInfo') + $RawJSON = $JSONObj | ConvertTo-Json -Depth 10 + Write-Host $RawJSON + try { + Write-Host "Checking" + $CheckExististing = New-GraphGETRequest -uri "https://graph.microsoft.com/v1.0/identity/conditionalAccess/policies" -tenantid $TenantFilter + if ($displayname -in $CheckExististing.displayName) { + if ($Overwrite -ne $true) { + Throw "Conditional Access Policy with Display Name $($Displayname) Already exists" + return $false + } + else { + Write-Host "overwriting" + $PatchRequest = New-GraphPOSTRequest -uri "https://graph.microsoft.com/beta/identity/conditionalAccess/policies/$($CheckExististing.id)" -tenantid $tenantfilter -type PATCH -body $RawJSON + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $($Tenant) -message "Updated Conditional Access Policy $($JSONObj.Displayname) to the template standard." -Sev "Info" + return "Updated policy $displayname for $tenantfilter" + } + } + else { + Write-Host "Creating" + $CreateRequest = New-GraphPOSTRequest -uri "https://graph.microsoft.com/beta/identity/conditionalAccess/policies" -tenantid $tenantfilter -type POST -body $RawJSON + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $($Tenant) -message "Added Conditional Access Policy $($JSONObj.Displayname)" -Sev "Info" + return "Created policy $displayname for $tenantfilter" + } + } + catch { + throw $_ + Write-LogMessage -API "Standards" -tenant $tenant -message "Failed to create or update conditional access rule $($JSONObj.displayName): $($_.exception.message)" -sev "Error" + } +} diff --git a/Modules/CIPPCore/Public/Set-CIPPGDAPInviteGroups.ps1 b/Modules/CIPPCore/Public/Set-CIPPGDAPInviteGroups.ps1 index af3564351c9b..a2ebdc386192 100644 --- a/Modules/CIPPCore/Public/Set-CIPPGDAPInviteGroups.ps1 +++ b/Modules/CIPPCore/Public/Set-CIPPGDAPInviteGroups.ps1 @@ -3,16 +3,16 @@ function Set-CIPPGDAPInviteGroups { $Table = Get-CIPPTable -TableName 'GDAPInvites' $InviteList = Get-AzDataTableEntity @Table - $LastDay = Get-Date (Get-Date).AddHours(-26) -UFormat '+%Y-%m-%dT%H:%M:%S.000Z' - $NewActivations = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/tenantRelationships/delegatedAdminRelationships?`$filter=((status eq 'active') and (activatedDateTime gt $LastDay))" + if (($InviteList | Measure-Object).Count -gt 0) { + #$LastDay = Get-Date (Get-Date).AddHours(-26) -UFormat '+%Y-%m-%dT%H:%M:%S.000Z' + #$NewActivations = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/tenantRelationships/delegatedAdminRelationships?`$filter=((status eq 'active') and (activatedDateTime gt $LastDay))" + $Activations = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/tenantRelationships/delegatedAdminRelationships?`$filter=status eq 'active'" - $NewActivations - - $InviteList - foreach ($NewActivation in $NewActivations) { - if ($InviteList.RowKey -contains $NewActivation.id) { - Write-Host "Mapping groups for GDAP relationship: $($NewActivation.id)" - Push-OutputBinding -Name Msg -Value $NewActivation.id + foreach ($Activation in $Activations) { + if ($InviteList.RowKey -contains $Activation.id) { + Write-Host "Mapping groups for GDAP relationship: $($Activation.id)" + Push-OutputBinding -Name Msg -Value $Activation.id + } } } -} \ No newline at end of file +} diff --git a/Modules/GraphRequests/Public/Get-ListGraphRequest.ps1 b/Modules/GraphRequests/Public/Get-ListGraphRequest.ps1 index c93d35f40fc1..cfcb4eab2d08 100644 --- a/Modules/GraphRequests/Public/Get-ListGraphRequest.ps1 +++ b/Modules/GraphRequests/Public/Get-ListGraphRequest.ps1 @@ -6,7 +6,7 @@ function Get-ListGraphRequest { $APIName = $TriggerMetadata.FunctionName $Message = 'Accessed this API | Endpoint: {0}' -f $Request.Query.Endpoint - Write-LogMessage -API $APINAME -message $Message -Sev 'Debug' + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message $Message -Sev 'Debug' $CippLink = ([System.Uri]$TriggerMetadata.Headers.referer).PathAndQuery diff --git a/Modules/GraphRequests/Public/Push-ListGraphRequestQueue.ps1 b/Modules/GraphRequests/Public/Push-ListGraphRequestQueue.ps1 index a193fcf9a9f4..8d3ea706de4b 100644 --- a/Modules/GraphRequests/Public/Push-ListGraphRequestQueue.ps1 +++ b/Modules/GraphRequests/Public/Push-ListGraphRequestQueue.ps1 @@ -36,7 +36,7 @@ function Push-ListGraphRequestQueue { } $RawGraphRequest = try { - Get-GraphRequestList @GraphRequestParams | Select-Object *, @{l = 'Tenant'; e = { $QueueItem.Tenant } }, @{l = 'CippStatus'; e = { 'Good' } } + Get-GraphRequestList @GraphRequestParams } catch { [PSCustomObject]@{ Tenant = $QueueItem.Tenant diff --git a/PublicWebhooks/run.ps1 b/PublicWebhooks/run.ps1 index 587fe02fe862..3e06a1cea831 100644 --- a/PublicWebhooks/run.ps1 +++ b/PublicWebhooks/run.ps1 @@ -7,7 +7,7 @@ $Webhooks = Get-AzDataTableEntity @WebhookTable Write-Host "Received request" Write-Host "CIPPID: $($request.Query.CIPPID)" $url = ($request.headers.'x-ms-original-url').split('/API') | Select-Object -First 1 -write-host $url +Write-Host $url if ($Request.CIPPID -in $Webhooks.CIPPID) { Write-Host "Found matching CIPPID" @@ -17,6 +17,7 @@ if ($Request.CIPPID -in $Webhooks.CIPPID) { Write-Host "Validation token received" $body = $request.query.ValidationToken } + foreach ($ReceivedItem In ($Request.body)) { $ReceivedItem = [pscustomobject]$ReceivedItem $TenantFilter = (Get-Tenants | Where-Object -Property customerId -EQ $ReceivedItem.TenantId).defaultDomainName @@ -26,7 +27,15 @@ if ($Request.CIPPID -in $Webhooks.CIPPID) { Write-Host "Operations to process for this client: $($Webhookinfo.Operations)" foreach ($Item in $Data) { Write-Host "Processing $($item.operation)" - Invoke-CippWebhookProcessing -TenantFilter $TenantFilter -Data $Item -CIPPPURL $url -allowedlocations $Webhookinfo.AllowedLocations -Operations $operations + if ($item.operation -in $operations) { + Invoke-CippWebhookProcessing -TenantFilter $TenantFilter -Data $Item -CIPPPURL $url -allowedlocations $Webhookinfo.AllowedLocations -Operations $operations + } + if ($item.operation -eq "UserLoggedIn" -and "UserLoggedInFromUnknownLocation" -in $operations) { + Invoke-CippWebhookProcessing -TenantFilter $TenantFilter -Data $Item -CIPPPURL $url -allowedlocations $Webhookinfo.AllowedLocations -Operations $operations + } + if ($item.operation -eq "UserLoggedIn" -and "AdminLoggedIn" -in $operations) { + Invoke-CippWebhookProcessing -TenantFilter $TenantFilter -Data $Item -CIPPPURL $url -allowedlocations $Webhookinfo.AllowedLocations -Operations $operations + } $body = "OK" } } diff --git a/Scheduler_Alert/run.ps1 b/Scheduler_Alert/run.ps1 index e4831ef2be9e..524cc8c8d993 100644 --- a/Scheduler_Alert/run.ps1 +++ b/Scheduler_Alert/run.ps1 @@ -24,7 +24,7 @@ try { } catch { - "Could not get admin password changes for $($Tenant.tenant): $($_.Exception.message)" + "Could not get admin password changes for $($Tenant.tenant): $(Get-NormalizedError -message $_.Exception.message)" } } { $_.'DefenderMalware' -eq $true } { @@ -96,7 +96,7 @@ try { } } catch { - "Could not get MFA status for admins for $($Tenant.tenant): $($_.Exception.message)" + "Could not get MFA status for admins for $($Tenant.tenant): $(Get-NormalizedError -message $_.Exception.message)" } } { $_.'MFAAlertUsers' -eq $true } { @@ -119,7 +119,7 @@ try { } catch { - "Could not get MFA status for users for $($Tenant.tenant): $($_.Exception.message)" + "Could not get MFA status for users for $($Tenant.tenant): $(Get-NormalizedError -message $_.Exception.message)" } } @@ -152,7 +152,7 @@ try { } } catch { - "Could not get get role changes for $($Tenant.tenant): $($_.Exception.message)" + "Could not get get role changes for $($Tenant.tenant): $(Get-NormalizedError -message $_.Exception.message)" } } diff --git a/Scheduler_Orchestration/run.ps1 b/Scheduler_Orchestration/run.ps1 index f1b8d63aa4b9..0014b630fe38 100644 --- a/Scheduler_Orchestration/run.ps1 +++ b/Scheduler_Orchestration/run.ps1 @@ -13,7 +13,7 @@ try { $ParallelTasks = foreach ($Item in $Batch) { try { - Invoke-DurableActivity -FunctionName "Scheduler_$($item['Type'])" -Input $item -NoWait -RetryOptions $RetryOptions + Invoke-DurableActivity -FunctionName "Scheduler_$($item['Type'])" -Input $item -NoWait -RetryOptions $RetryOptions -ErrorAction Stop } catch { Write-Host 'Could not start:' diff --git a/Standards_ConditionalAccess/run.ps1 b/Standards_ConditionalAccess/run.ps1 index ae81899cf09f..80758f996af8 100644 --- a/Standards_ConditionalAccess/run.ps1 +++ b/Standards_ConditionalAccess/run.ps1 @@ -1,4 +1,5 @@ param($tenant) + $ConfigTable = Get-CippTable -tablename 'standards' $Setting = ((Get-AzDataTableEntity @ConfigTable -Filter "PartitionKey eq 'standards' and RowKey eq '$tenant'").JSON | ConvertFrom-Json).standards.ConditionalAccess if (!$Setting) { @@ -6,62 +7,13 @@ if (!$Setting) { } $APINAME = "Standards" -function Remove-EmptyArrays ($Object) { - if ($Object -is [Array]) { - foreach ($Item in $Object) { Remove-EmptyArrays $Item } - } - elseif ($Object -is [HashTable]) { - foreach ($Key in @($Object.get_Keys())) { - if ($Object[$Key] -is [Array] -and $Object[$Key].get_Count() -eq 0) { - $Object.Remove($Key) - } - else { Remove-EmptyArrays $Object[$Key] } - } - } - elseif ($Object -is [PSCustomObject]) { - foreach ($Name in @($Object.psobject.properties.Name)) { - if ($Object.$Name -is [Array] -and $Object.$Name.get_Count() -eq 0) { - $Object.PSObject.Properties.Remove($Name) - } - elseif ($object.$name -eq $null) { - $Object.PSObject.Properties.Remove($Name) - } - else { Remove-EmptyArrays $Object.$Name } - } - } -} - - - foreach ($Template in $Setting.TemplateList) { try { $Table = Get-CippTable -tablename 'templates' $Filter = "PartitionKey eq 'CATemplate' and RowKey eq '$($Template.value)'" - $Request = @{body = $null } - $JSONObj = (Get-AzDataTableEntity @Table -Filter $Filter).JSON | ConvertFrom-Json - Remove-EmptyArrays $JSONObj - #Remove context as it does not belong in the payload. - try { - $JsonObj.grantControls.PSObject.Properties.Remove('authenticationStrength@odata.context') - $JsonObj.conditions.users.excludeGuestsOrExternalUsers.externalTenants.PSObject.Properties.Remove('@odata.type') - } - catch { - #no action required, failure allowed. - } - $RawJSON = $JSONObj | Select-Object * -ExcludeProperty Id, *time* | ConvertTo-Json -Depth 10 - $PolicyName = $JSONObj.displayName - $CheckExististing = New-GraphGETRequest -uri "https://graph.microsoft.com/v1.0/identity/conditionalAccess/policies" -tenantid $tenant | Where-Object displayName -EQ $JSONObj.displayName - if ($PolicyName -in $CheckExististing.displayName) { - #patch the conditional access policy to restore our config. - $PatchRequest = New-GraphPOSTRequest -uri "https://graph.microsoft.com/beta/identity/conditionalAccess/policies/$($CheckExististing.id)" -tenantid $tenant -type PATCH -body $RawJSON - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $($Tenant) -message "Updated Conditional Access Policy $($JSONObj.Displayname) to the template standard." -Sev "Info" - - } - else { - $CreateRequest = New-GraphPOSTRequest -uri "https://graph.microsoft.com/beta/identity/conditionalAccess/policies" -tenantid $tenant -type POST -body $RawJSON - Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -tenant $($Tenant) -message "Added Conditional Access Policy $($JSONObj.Displayname)" -Sev "Info" - } + $JSONObj = (Get-AzDataTableEntity @Table -Filter $Filter).JSON + $CAPolicy = New-CIPPCAPolicy -TenantFilter $tenant -state $request.body.NewState -RawJSON $JSONObj -Overwrite $true -APIName $APIName -ExecutingUser $request.headers.'x-ms-client-principal' } catch { Write-LogMessage -API "Standards" -tenant $tenant -message "Failed to create or update conditional access rule $($JSONObj.displayName): $($_.exception.message)" -sev "Error" diff --git a/Standards_DisableBasicAuthSMTP/function.json b/Standards_DisableBasicAuthSMTP/function.json new file mode 100644 index 000000000000..2d4ea9094b24 --- /dev/null +++ b/Standards_DisableBasicAuthSMTP/function.json @@ -0,0 +1,9 @@ +{ + "bindings": [ + { + "name": "tenant", + "direction": "in", + "type": "activityTrigger" + } + ] +} \ No newline at end of file diff --git a/Standards_DisableBasicAuthSMTP/run.ps1 b/Standards_DisableBasicAuthSMTP/run.ps1 new file mode 100644 index 000000000000..8bef6658e5cd --- /dev/null +++ b/Standards_DisableBasicAuthSMTP/run.ps1 @@ -0,0 +1,9 @@ +param($tenant) + +try { + $Request = New-ExoRequest -tenantid $Tenant -cmdlet "Set-TransportConfig" -cmdParams @{ SmtpClientAuthenticationDisabled = $true } + Write-LogMessage -API "Standards" -tenant $tenant -message "Disabled SMTP Basic Authentication" -sev Info +} +catch { + Write-LogMessage -API "Standards" -tenant $tenant -message "Failed to disable SMTP Basic Authentication: $($_.exception.message)" -sev Error +} \ No newline at end of file diff --git a/profile.ps1 b/profile.ps1 index f5947f1314cc..1c0b9569580f 100644 --- a/profile.ps1 +++ b/profile.ps1 @@ -23,6 +23,16 @@ try { } catch {} +try { + if (!$ENV:SetFromProfile) { + Write-Host "We're reloading from KV" + $Auth = Get-CIPPAuthentication + } +} +catch { + Write-LogMessage -message "Could not retrieve keys from Keyvault: $($_.Exception.Message)" -Sev 'CRITICAL' +} + # Uncomment the next line to enable legacy AzureRm alias in Azure PowerShell. # Enable-AzureRmAlias diff --git a/version_latest.txt b/version_latest.txt index cc868b62c301..99eba4de9311 100644 --- a/version_latest.txt +++ b/version_latest.txt @@ -1 +1 @@ -4.0.1 \ No newline at end of file +4.1.0 \ No newline at end of file