diff --git a/AddMSPApp/datto.app.xml b/AddMSPApp/datto.app.xml index 145d910d8ef3..ca6f1636c64c 100644 --- a/AddMSPApp/datto.app.xml +++ b/AddMSPApp/datto.app.xml @@ -1,15 +1,15 @@ - - install.ps1 - 705 - datto.intunewin - install.ps1 - - sL/LP/JZ4F4cBSykm6usgJoV1PMoqd62C6JUwuo2z24= - PEpeqeoX7jAWxb0xHGfCkKFxh4/YRfoMTVXrP+uZWzM= - ulFPA+vYjaxX0pvq0BMAKQ== - 28ZFU4AT1OznwF8pfqO8i+WFUNSf9024H4Jw2H7UJWs= - ProfileVersion1 - YEb+QNQCko/uZyedA+JfcP/RDm+nZOIjFN04CfhwN4c= - SHA256 - + + install.ps1 + 693 + datto.intunewin + install.ps1 + + jobB9Ga7J3CbO6acWJyvBRE56nFXwqGfcnGfZRMsJC4= + 53SOzs0l6Po2btsGFSMZgkV8vwhH+PxTN8BZDUcfWfg= + VjM/osrvPElbu79J+mdXuw== + UZZXO53Np/tG6Ms+qvwLcNOeD1GRH6NRPFg/TuMz39M= + ProfileVersion1 + KtAWAl29064LG0eyDinbDs0JUbK+EK7GsJovu8obBM4= + SHA256 + \ No newline at end of file diff --git a/AddMSPApp/datto.intunewin b/AddMSPApp/datto.intunewin index 607b0725032b..96b3f27c3c62 100644 Binary files a/AddMSPApp/datto.intunewin and b/AddMSPApp/datto.intunewin differ diff --git a/CIPPTimers.json b/CIPPTimers.json index cbd1366a5363..3532f6fafe42 100644 --- a/CIPPTimers.json +++ b/CIPPTimers.json @@ -80,6 +80,15 @@ "RunOnProcessor": true, "PreferredProcessor": "standards" }, + { + "Id": "4d80205c-674d-4fc1-abeb-a1ec37e0d796", + "Command": "Start-DriftStandardsOrchestrator", + "Description": "Orchestrator to process drift standards", + "Cron": "0 0 */1 * * *", + "Priority": 5, + "RunOnProcessor": true, + "PreferredProcessor": "standards" + }, { "Id": "97145a1d-28f0-4bb2-b929-5a43517d23cc", "Command": "Start-SchedulerOrchestrator", @@ -197,4 +206,4 @@ "RunOnProcessor": true, "IsSystem": true } -] +] \ No newline at end of file diff --git a/ConversionTable.csv b/ConversionTable.csv index 2658524b8cf2..9407f70d6cb7 100644 --- a/ConversionTable.csv +++ b/ConversionTable.csv @@ -240,6 +240,9 @@ Dynamics 365 Enterprise Edition - Additional Production Instance for Government, "Dynamics 365 Field Service, Enterprise Edition - Resource Scheduling Optimization",CRM_AUTO_ROUTING_ADDON,977464c4-bfaf-4b67-b761-a9bb735a2196,CRM_AUTO_ROUTING_ENGINE_ADDON,24435e4b-87d0-4d7d-8beb-63a9b1573022,Field Service – Automated Routing Engine Add-On "Dynamics 365 Field Service, Enterprise Edition - Resource Scheduling Optimization",CRM_AUTO_ROUTING_ADDON,977464c4-bfaf-4b67-b761-a9bb735a2196,CRM_AUTO_ROUTING_ADDON,2ba394e0-6f18-4b77-b45f-a5663bbab540,RETIRED - Field Service – Automated Routing Engine Add-On "Dynamics 365 Field Service, Enterprise Edition - Resource Scheduling Optimization",CRM_AUTO_ROUTING_ADDON,977464c4-bfaf-4b67-b761-a9bb735a2196,EXCHANGE_S_FOUNDATION,113feb6c-3fe4-4440-bddc-54d774bf0318,Exchange Foundation +Dynamics 365 Field Service Contractor,D365_FIELD_SERVICE_CONTRACTOR,23e6e135-e869-4ce4-9ae4-5710cd69ac13,CDS_FIELD_SERVICE_CONTRACTOR,f4614a66-d632-443a-bc77-afe92987b322,Common Data Service Field service Part Time Contractors +Dynamics 365 Field Service Contractor,D365_FIELD_SERVICE_CONTRACTOR,23e6e135-e869-4ce4-9ae4-5710cd69ac13,EXCHANGE_S_FOUNDATION,113feb6c-3fe4-4440-bddc-54d774bf0318,Exchange Foundation +Dynamics 365 Field Service Contractor,D365_FIELD_SERVICE_CONTRACTOR,23e6e135-e869-4ce4-9ae4-5710cd69ac13,POWERAPPS_DYN_APPS,874fc546-6efe-4d22-90b8-5c4e7aa59f4b,Power Apps for Dynamics 365 Dynamics 365 Field Service Contractor for Government,D365_FIELD_SERVICE_CONTRACTOR_GOV,e7965e3a-1f49-4d67-a3de-ad1ce460bbcc,CDS_FIELD_SERVICE_CONTRACTOR_GCC,2457fe40-65be-48a1-935f-924ad6e62dba,Common Data Service Field service Part Time Contractors for Government Dynamics 365 Field Service Contractor for Government,D365_FIELD_SERVICE_CONTRACTOR_GOV,e7965e3a-1f49-4d67-a3de-ad1ce460bbcc,EXCHANGE_S_FOUNDATION_GOV,922ba911-5694-4e99-a794-73aed9bfeec8,Exchange Foundation for Government Dynamics 365 Field Service Contractor for Government,D365_FIELD_SERVICE_CONTRACTOR_GOV,e7965e3a-1f49-4d67-a3de-ad1ce460bbcc,POWERAPPS_DYN_APPS_GOV,3089c02b-e533-4b73-96a5-01fa648c3c3c,PowerApps for Dynamics 365 for Government @@ -2246,6 +2249,93 @@ Microsoft 365 E5,SPE_E5,06ebc4ee-1bb5-47dd-8120-11324bc54e06,AAD_PREMIUM_P2,eec0 Microsoft 365 E5,SPE_E5,06ebc4ee-1bb5-47dd-8120-11324bc54e06,INTUNE_A,c1ec4a95-1f05-45b3-a911-aa3fa01094f5,Microsoft Intune Plan 1 Microsoft 365 E5,SPE_E5,06ebc4ee-1bb5-47dd-8120-11324bc54e06,FLOW_O365_P3,07699545-9485-468e-95b6-2fca3738be01,Power Automate for Office 365 Microsoft 365 E5,SPE_E5,06ebc4ee-1bb5-47dd-8120-11324bc54e06,POWER_VIRTUAL_AGENTS_O365_P3,ded3d325-1bdc-453e-8432-5bac26d7a014,Power Virtual Agents for Office 365 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MESH_AVATARS_FOR_TEAMS,dcf9d2f4-772e-4434-b757-77a453cfbc02,Avatars for Teams +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MESH_AVATARS_ADDITIONAL_FOR_TEAMS,3efbd4ed-8958-4824-8389-1321f8730af8,Avatars for Teams (additional) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,CDS_O365_P3,afa73018-811e-46e9-988f-f75d2b1b8430,Common Data Service for Teams +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,LOCKBOX_ENTERPRISE,9f431833-0334-42de-a7dc-70aa40db46db,Customer Lockbox +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,CustomerLockboxA_Enterprise,3ec18638-bd4c-4d3b-8905-479ed636b83e,Customer Lockbox (A) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MIP_S_Exchange,cd31b152-6326-4d1b-ae1b-997b625182e6,Data Classification in Microsoft 365 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,EXCHANGE_S_ENTERPRISE,efb87545-963c-4e0d-99df-69c6916d9eb0,Exchange Online (Plan 2) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,GRAPH_CONNECTORS_SEARCH_INDEX,a6520331-d7d4-4276-95f5-15c0933bc757,Graph Connectors Search with Index +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MESH_IMMERSIVE_FOR_TEAMS,f0ff6ac6-297d-49cd-be34-6dfef97f0c28,Immersive spaces for Teams +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,INFORMATION_BARRIERS,c4801e8a-cb58-4c35-aca6-f2dcc106f287,Information Barriers +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,Content_Explorer,d9fa6af4-e046-4c89-9226-729a0786685d,Information Protection and Governance Analytics - Premium +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,ContentExplorer_Standard,2b815d45-56e4-4e3a-b65c-66cb9175b560,Information Protection and Governance Analytics – Standard +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MIP_S_CLP2,efb0351d-3b08-4503-993d-383af8de41e3,Information Protection for Office 365 - Premium +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MIP_S_CLP1,5136a095-5cf0-4aff-bec3-e84448b38ea5,Information Protection for Office 365 - Standard +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MYANALYTICS_P2,33c4f319-9bdd-48d6-9c4d-410b750a4a5a,Insights by MyAnalytics +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,M365_ADVANCED_AUDITING,2f442157-a11c-46b9-ae5b-6e39ff4e5849,Microsoft 365 Advanced Auditing +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,OFFICESUBSCRIPTION,43de0ff5-c92c-492b-9116-175376d08c38,Microsoft 365 Apps for enterprise +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MCOMEETADV,3e26ee1f-8a5f-4d52-aee2-b81ce45c8f40,Microsoft 365 Audio Conferencing +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,M365_AUDIT_PLATFORM,f6de4823-28fa-440b-b886-4783fa86ddba,Microsoft 365 Audit Platform +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MICROSOFT_COMMUNICATION_COMPLIANCE,a413a9ff-720c-4822-98ef-2f37c2a21f4c,Microsoft 365 Communication Compliance +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MTP,bf28f719-7844-4079-9c78-c1307898e192,Microsoft 365 Defender +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,M365_LIGHTHOUSE_CUSTOMER_PLAN1,6f23d6a9-adbf-481c-8538-b4c095654487,Microsoft 365 Lighthouse (Plan 1) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MCOEV,4828c8ec-dc2e-4779-b502-87ac9ce28ab7,Microsoft 365 Phone System +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MICROSOFTBOOKINGS,199a5c09-e0ca-4e37-8f7c-b05d533e1ea2,Microsoft Bookings +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,CLIPCHAMP,a1ace008-72f3-4ea0-8dac-33b3a23a2472,Microsoft Clipchamp +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,COMMUNICATIONS_DLP,6dc145d6-95dd-4191-b9c3-185575ee6f6b,Microsoft Communications DLP +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,CUSTOMER_KEY,6db1f1db-2b46-403f-be40-e39395f08dbb,Microsoft Customer Key +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,ATP_ENTERPRISE,f20fedf3-f3c3-43c3-8267-2bfdd51c0939,Microsoft Defender for Office 365 (Plan 1) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,THREAT_INTELLIGENCE,8e0c0a52-6a6c-4d40-8370-dd62790dcd70,Microsoft Defender for Office 365 (Plan 2) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,EXCEL_PREMIUM,531ee2f8-b1cb-453b-9c21-d2180d014ca5,Microsoft Excel Advanced Analytics +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,FORMS_PLAN_E5,e212cbc7-0961-4c40-9825-01117710dcb1,Microsoft Forms (Plan E5) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,INFO_GOVERNANCE,e26c2fcc-ab91-4a61-b35c-03cdc8dddf66,Microsoft Information Governance +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,INSIDER_RISK,d587c7a3-bda9-4f99-8776-9bcf59c84f75,Microsoft Insider Risk Management +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,INSIDER_RISK_MANAGEMENT,9d0c4ee5-e4a1-4625-ab39-d82b619b1a34,Microsoft Insider Risk Management - Exchange +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,KAIZALA_STANDALONE,0898bdbb-73b0-471a-81e5-20f1fe4dd66e,Microsoft Kaizala Pro +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MICROSOFT_LOOP,c4b8c31a-fb44-4c65-9837-a21f55fcabda,Microsoft Loop +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,ML_CLASSIFICATION,d2d51368-76c9-4317-ada2-a12c004c432f,Microsoft ML-Based Classification +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,EXCHANGE_ANALYTICS,34c0d7a0-a70f-4668-9238-47f9fc208882,Microsoft MyAnalytics (Full) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,PROJECTWORKMANAGEMENT,b737dad2-2f6c-4c65-90e3-ca563267e8b9,Microsoft Planner +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,RECORDS_MANAGEMENT,65cc641f-cccd-4643-97e0-a17e3045e541,Microsoft Records Management +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MICROSOFT_SEARCH,94065c59-bc8e-4e8b-89e5-5138d471eaff,Microsoft Search +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,Deskless,8c7d2df8-86f0-4902-b2ed-a0458298f3b3,Microsoft StaffHub +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,STREAM_O365_E5,6c6042f5-6f01-4d67-b8c1-eb99d36eed3e,Microsoft Stream for Office 365 E5 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,INTUNE_O365,882e1d05-acd1-4ccb-8708-6ee03664b117,Mobile Device Management for Office 365 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,Nucleus,db4d623d-b514-490b-b7ef-8885eee514de,Nucleus +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,EQUIVIO_ANALYTICS,4de31727-a228-4ec3-a5bf-8e45b5ca48cc,Office 365 Advanced eDiscovery +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,ADALLOM_S_O365,8c098270-9dd4-4350-9b30-ba4703f3b36b,Office 365 Cloud App Security +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,PAM_ENTERPRISE,b1188c4c-1b36-4018-b48b-ee07604f6feb,Office 365 Privileged Access Management +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,SAFEDOCS,bf6f5520-59e3-4f82-974b-7dbbc4fd27c7,Office 365 SafeDocs +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,SHAREPOINTWAC,e95bec33-7c88-4a70-8e19-b10bd9d0c014,Office for the Web +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,PEOPLE_SKILLS_FOUNDATION,13b6da2c-0d84-450e-9f69-a33e221387ca,People Skills - Foundation +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,POWERAPPS_O365_P3,9c0dab89-a30c-4117-86e7-97bda240acd2,Power Apps for Office 365 (Plan 3) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,BI_AZURE_P2,70d33638-9c74-4d01-bfd3-562de28bd4ba,Power BI Pro +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,PREMIUM_ENCRYPTION,617b097b-4b93-4ede-83de-5f075bb5fb2f,Premium Encryption in Office 365 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,PROJECT_O365_P3,b21a6b06-1988-436e-a07b-51ec6d9f52ad,Project for Office (Plan E5) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,PURVIEW_DISCOVERY,c948ea65-2053-4a5a-8a62-9eaaaf11b522,Purview Discovery +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,Bing_Chat_Enterprise,0d0c0d31-fae7-41f2-b909-eaf4d7f26dba,RETIRED - Commercial data protection for Microsoft Copilot +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,COMMUNICATIONS_COMPLIANCE,41fcdd7d-4733-4863-9cf4-c65b83ce2df4,RETIRED - Microsoft Communications Compliance +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,DATA_INVESTIGATIONS,46129a58-a698-46f0-aa5b-17f6586297d9,Retired - Microsoft Data Investigations +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,PLACES_CORE,1fe6227d-3e01-46d0-9510-0acad4ff6e94,RETIRED - Places Core +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,SHAREPOINTENTERPRISE,5dbe027f-2339-4123-9542-606e4d348a72,SharePoint (Plan 2) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MCOSTANDARD,0feaeb32-d00e-4d66-bd5a-43b5b83db82c,Skype for Business Online (Plan 2) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,SWAY,a23b959c-7ce8-4e57-9140-b90eb88a9e97,Sway +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,BPOS_S_TODO_3,3fb82609-8c27-4f7b-bd51-30634711ee67,To-Do (Plan 3) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,VIVAENGAGE_CORE,a82fbf69-b4d7-49f4-83a6-915b2cf354f4,Viva Engage Core +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,VIVA_LEARNING_SEEDED,b76fb638-6ba6-402a-b9f9-83d28acb3d86,Viva Learning Seeded +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,WHITEBOARD_PLAN3,4a51bca5-1eff-43f5-878c-177680f191af,Whiteboard (Plan 3) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,YAMMER_ENTERPRISE,7547a3fe-08ee-4ccb-b430-5077c5041653,Yammer Enterprise +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,WINDEFATP,871d91ec-ec1a-452b-a83f-bd76c7d770ef,Microsoft Defender for Endpoint +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MICROSOFTENDPOINTDLP,64bfac92-2b17-4482-b5e5-a0304429de3e,Microsoft Endpoint DLP +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,UNIVERSAL_PRINT_01,795f6fe0-cc4d-4773-b050-5dde4dc704c9,Universal Print +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,WIN10_PRO_ENT_SUB,21b439ba-a0ca-424f-a6cc-52f954a5b111,Windows 10/11 Enterprise (Original) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,Windows_Autopatch,9a6eeb79-0b4b-4bf0-9808-39d99a2cd5a3,Windows Autopatch +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,WINDOWSUPDATEFORBUSINESS_DEPLOYMENTSERVICE,7bf960f6-2cd9-443a-8046-5dbff9558365,Windows Update for Business Deployment Service +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,RMS_S_PREMIUM,6c57d4b6-3b23-47a5-9bc9-69f17b4947b3,Azure Information Protection Premium P1 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,RMS_S_PREMIUM2,5689bec4-755d-4753-8b61-40975025187c,Azure Information Protection Premium P2 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,RMS_S_ENTERPRISE,bea4c11e-220a-4e6d-8eb8-8ea15d019f90,Azure Rights Management +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,DYN365_CDS_O365_P3,28b0fa46-c39a-4188-89e2-58e979a6b014,Common Data Service +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,Defender_for_Iot_Enterprise,99cd49a9-0e54-4e07-aea1-d8d9f5f704f5,Defender for IoT - Enterprise IoT Security +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MFA_PREMIUM,8a256a2b-b617-496d-b51b-e76466e88db0,Microsoft Azure Multi-Factor Authentication +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,ADALLOM_S_STANDALONE,2e2ddb96-6af9-4b1d-a3f0-d6ecfd22edb2,Microsoft Defender for Cloud Apps +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,ATA,14ab5db5-e6c4-4b20-b4bc-13e36fd2227f,Microsoft Defender for Identity +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,AAD_PREMIUM,41781fb2-bc02-4b7c-bd55-b576c07bb09d,Microsoft Entra ID P1 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,AAD_PREMIUM_P2,eec0eb4f-6444-4f95-aba0-50c24d67f998,Microsoft Entra ID P2 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,INTUNE_A,c1ec4a95-1f05-45b3-a911-aa3fa01094f5,Microsoft Intune Plan 1 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,FLOW_O365_P3,07699545-9485-468e-95b6-2fca3738be01,Power Automate for Office 365 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,POWER_VIRTUAL_AGENTS_O365_P3,ded3d325-1bdc-453e-8432-5bac26d7a014,Power Virtual Agents for Office 365 Microsoft 365 E5 EEA (no Teams) with Calling Minutes,Microsoft_365_E5_EEA_(no_Teams)_with_Calling_Minutes,6ee4114a-9b2d-4577-9e7a-49fa43d222d3,CDS_O365_P3,afa73018-811e-46e9-988f-f75d2b1b8430,Common Data Service for Teams Microsoft 365 E5 EEA (no Teams) with Calling Minutes,Microsoft_365_E5_EEA_(no_Teams)_with_Calling_Minutes,6ee4114a-9b2d-4577-9e7a-49fa43d222d3,LOCKBOX_ENTERPRISE,9f431833-0334-42de-a7dc-70aa40db46db,Customer Lockbox Microsoft 365 E5 EEA (no Teams) with Calling Minutes,Microsoft_365_E5_EEA_(no_Teams)_with_Calling_Minutes,6ee4114a-9b2d-4577-9e7a-49fa43d222d3,MIP_S_Exchange,cd31b152-6326-4d1b-ae1b-997b625182e6,Data Classification in Microsoft 365 @@ -5127,6 +5217,11 @@ Power Apps Premium,POWERAPPS_PER_USER,b30411f5-fea1-4a59-9ad9-3db7c7ead579,DYN36 Power Apps Premium,POWERAPPS_PER_USER,b30411f5-fea1-4a59-9ad9-3db7c7ead579,CDSAICAPACITY_PERUSER,91f50f7b-2204-4803-acac-5cf5668b8b39,DO NOT USE - AI Builder capacity Per User add-on Power Apps Premium,POWERAPPS_PER_USER,b30411f5-fea1-4a59-9ad9-3db7c7ead579,POWERAPPS_PER_USER,ea2cf03b-ac60-46ae-9c1d-eeaeb63cec86,Power Apps per User Plan Power Apps Premium,POWERAPPS_PER_USER,b30411f5-fea1-4a59-9ad9-3db7c7ead579,Flow_PowerApps_PerUser,dc789ed8-0170-4b65-a415-eb77d5bb350a,Power Automate for Power Apps per User Plan +Power Apps Premium embedded,POWERAPPS_PER_USER_ISVEMB,2a6fb3c6-30cc-4558-a69d-032425c1a3ba,Power_Pages_Internal_User,60bf28f9-2b70-4522-96f7-335f5e06c941,Power Pages Internal User +Power Apps Premium embedded,POWERAPPS_PER_USER_ISVEMB,2a6fb3c6-30cc-4558-a69d-032425c1a3ba,EXCHANGE_S_FOUNDATION,113feb6c-3fe4-4440-bddc-54d774bf0318,Exchange Foundation +Power Apps Premium embedded,POWERAPPS_PER_USER_ISVEMB,2a6fb3c6-30cc-4558-a69d-032425c1a3ba,DYN365_CDS_P2,6ea4c1ef-c259-46df-bce2-943342cd3cb2,Common Data Service +Power Apps Premium embedded,POWERAPPS_PER_USER_ISVEMB,2a6fb3c6-30cc-4558-a69d-032425c1a3ba,POWERAPPS_PER_USER,ea2cf03b-ac60-46ae-9c1d-eeaeb63cec86,Power Apps per User Plan +Power Apps Premium embedded,POWERAPPS_PER_USER_ISVEMB,2a6fb3c6-30cc-4558-a69d-032425c1a3ba,Flow_PowerApps_PerUser,dc789ed8-0170-4b65-a415-eb77d5bb350a,Power Automate for Power Apps per User Plan Power Apps Premium for Government,POWERAPPS_PER_USER_GCC,8e4c6baa-f2ff-4884-9c38-93785d0d7ba1,CDSAICAPACITY_PERUSER,91f50f7b-2204-4803-acac-5cf5668b8b39,AI Builder capacity Per User add-on Power Apps Premium for Government,POWERAPPS_PER_USER_GCC,8e4c6baa-f2ff-4884-9c38-93785d0d7ba1,CDSAICAPACITY_PERUSER_NEW,74d93933-6f22-436e-9441-66d205435abb,AI Builder capacity Per User add-on Power Apps Premium for Government,POWERAPPS_PER_USER_GCC,8e4c6baa-f2ff-4884-9c38-93785d0d7ba1,DYN365_CDS_P2_GOV,37396c73-2203-48e6-8be1-d882dae53275,Common Data Service for Government diff --git a/Modules/CIPPCore/Public/Add-CIPPAlias.ps1 b/Modules/CIPPCore/Public/Add-CIPPAlias.ps1 index 075f114f767e..6a0fc8615eb1 100644 --- a/Modules/CIPPCore/Public/Add-CIPPAlias.ps1 +++ b/Modules/CIPPCore/Public/Add-CIPPAlias.ps1 @@ -1,9 +1,9 @@ function Add-CIPPAlias { [CmdletBinding()] param ( - $user, + $User, $Aliases, - $UserprincipalName, + $UserPrincipalName, $TenantFilter, $APIName = 'Add Alias', $Headers @@ -11,16 +11,17 @@ function Add-CIPPAlias { try { foreach ($Alias in $Aliases) { - Write-Host "Adding alias $Alias to $user" - New-GraphPostRequest -uri "https://graph.microsoft.com/beta/users/$user" -tenantid $TenantFilter -type 'patch' -body "{`"mail`": `"$Alias`"}" -verbose + Write-Host "Adding alias $Alias to $User" + New-GraphPostRequest -uri "https://graph.microsoft.com/beta/users/$User" -tenantid $TenantFilter -type 'patch' -body "{`"mail`": `"$Alias`"}" -verbose } Write-Host "Resetting primary alias to $User" - New-GraphPostRequest -uri "https://graph.microsoft.com/beta/users/$($user)" -tenantid $TenantFilter -type 'patch' -body "{`"mail`": `"$User`"}" -verbose - Write-LogMessage -headers $Headers -API $APINAME -tenant $($TenantFilter) -message "Added alias $($Alias) to $($UserprincipalName)" -Sev 'Info' + New-GraphPostRequest -uri "https://graph.microsoft.com/beta/users/$User" -tenantid $TenantFilter -type 'patch' -body "{`"mail`": `"$User`"}" -verbose + Write-LogMessage -headers $Headers -API $APIName -tenant $($TenantFilter) -message "Added alias $($Alias) to $($UserPrincipalName)" -Sev 'Info' return ("Added Aliases: $($Aliases -join ',')") } catch { - Write-LogMessage -headers $Headers -API $APINAME -tenant $($TenantFilter) -message "Failed to set alias. Error:$($_.Exception.Message)" -Sev 'Error' - throw "Failed to set alias: $($_.Exception.Message)" + $ErrorMessage = Get-CippException -Exception $_ + Write-LogMessage -headers $Headers -API $APIName -tenant $($TenantFilter) -message "Failed to set alias. Error:$($ErrorMessage.NormalizedError)" -Sev 'Error' -LogData $ErrorMessage + throw "Failed to set alias: $($ErrorMessage.NormalizedError)" } } diff --git a/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertLicenseAssignmentErrors.ps1 b/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertLicenseAssignmentErrors.ps1 new file mode 100644 index 000000000000..f72d83ac37cb --- /dev/null +++ b/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertLicenseAssignmentErrors.ps1 @@ -0,0 +1,91 @@ +function Get-CIPPAlertLicenseAssignmentErrors { + <# + .FUNCTIONALITY + Entrypoint + #> + [CmdletBinding()] + Param ( + [Parameter(Mandatory)] + $TenantFilter, + [Alias('input')] + $InputValue + ) + + # Define error code translations for human-readable messages + $ErrorTranslations = @( + @{ + ErrorCode = "CountViolation" + Description = "Not enough licenses available - the organization has exceeded the number of available licenses for this SKU" + }, + @{ + ErrorCode = "MutuallyExclusiveViolation" + Description = "Conflicting licenses assigned - this license cannot be assigned alongside another license the user already has" + }, + @{ + ErrorCode = "ProhibitedInUsageLocationViolation" + Description = "License not available in user's location - this license cannot be assigned to users in the user's current usage location" + }, + @{ + ErrorCode = "UniquenessViolation" + Description = "Duplicate license assignment - this license can only be assigned once per user" + }, + @{ + ErrorCode = "Unknown" + Description = "Unknown license assignment error - an unspecified error occurred during license assignment" + } + ) + + try { + # Get all users with license assignment states from Graph API + $Users = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/users?`$select=id,userPrincipalName,displayName,licenseAssignmentStates&`$top=999" -tenantid $TenantFilter + + # Filter users who have license assignment violations + $UsersWithViolations = $Users | Where-Object { + $_.licenseAssignmentStates -and + ($_.licenseAssignmentStates | Where-Object { + $_.error -and ( + $_.error -like "*CountViolation*" -or + $_.error -like "*MutuallyExclusiveViolation*" -or + $_.error -like "*ProhibitedInUsageLocationViolation*" -or + $_.error -like "*UniquenessViolation*" -or + $_.error -like "*Unknown*" + ) + }) + } + + # Build alert messages for users with violations + $LicenseAssignmentErrors = foreach ($User in $UsersWithViolations) { + $ViolationErrors = $User.licenseAssignmentStates | Where-Object { + $_.error -and ( + $_.error -like "*CountViolation*" -or + $_.error -like "*MutuallyExclusiveViolation*" -or + $_.error -like "*ProhibitedInUsageLocationViolation*" -or + $_.error -like "*UniquenessViolation*" -or + $_.error -like "*Unknown*" + ) + } + + foreach ($Violation in $ViolationErrors) { + # Find matching error translation + $ErrorTranslation = $ErrorTranslations | Where-Object { $Violation.error -like "*$($_.ErrorCode)*" } | Select-Object -First 1 + $HumanReadableError = if ($ErrorTranslation) { + $ErrorTranslation.Description + } else { + "Unknown license assignment error: $($Violation.error)" + } + + $PrettyName = Convert-SKUname -skuID $Violation.skuId + + "$($User.userPrincipalName): $HumanReadableError (License: $PrettyName)" + } + } + + # If errors are found, write alert + if ($LicenseAssignmentErrors) { + Write-AlertTrace -cmdletName $MyInvocation.MyCommand -tenantFilter $TenantFilter -data $LicenseAssignmentErrors + } + + } catch { + Write-LogMessage -message "Failed to check license assignment errors: $($_.exception.message)" -API 'License Assignment Alerts' -tenant $TenantFilter -sev Error + } +} diff --git a/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertNewAppApproval.ps1 b/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertNewAppApproval.ps1 index 883413405345..c6da6950077f 100644 --- a/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertNewAppApproval.ps1 +++ b/Modules/CIPPCore/Public/Alerts/Get-CIPPAlertNewAppApproval.ps1 @@ -15,6 +15,7 @@ function Get-CIPPAlertNewAppApproval { try { $Approvals = New-GraphGetRequest -Uri "https://graph.microsoft.com/beta/identityGovernance/appConsent/appConsentRequests?`$filter=userConsentRequests/any (u:u/status eq 'InProgress')" -tenantid $TenantFilter if ($Approvals.count -gt 0) { + $TenantGUID = (Get-Tenants -TenantFilter $TenantFilter -SkipDomains).customerId $AlertData = [System.Collections.Generic.List[PSCustomObject]]::new() foreach ($App in $Approvals) { $userConsentRequests = New-GraphGetRequest -Uri "https://graph.microsoft.com/v1.0/identityGovernance/appConsent/appConsentRequests/$($App.id)/userConsentRequests" -tenantid $TenantFilter @@ -29,13 +30,17 @@ function Get-CIPPAlertNewAppApproval { } $Message = [PSCustomObject]@{ + RequestId = $_.id AppName = $App.appDisplayName RequestUser = $_.createdBy.user.userPrincipalName Reason = $_.reason + RequestDate = $_.createdDateTime + Status = $_.status # Will allways be InProgress as we filter to only get these but this will reduce confusion when an alert is generated AppId = $App.appId Scopes = ($App.pendingScopes.displayName -join ', ') ConsentURL = $consentUrl Tenant = $TenantFilter + TenantId = $TenantGUID } $AlertData.Add($Message) } diff --git a/Modules/CIPPCore/Public/AuditLogs/Get-CippAuditLogSearchResults.ps1 b/Modules/CIPPCore/Public/AuditLogs/Get-CippAuditLogSearchResults.ps1 index 2ced11b10908..ecff809d72b2 100644 --- a/Modules/CIPPCore/Public/AuditLogs/Get-CippAuditLogSearchResults.ps1 +++ b/Modules/CIPPCore/Public/AuditLogs/Get-CippAuditLogSearchResults.ps1 @@ -28,6 +28,6 @@ function Get-CippAuditLogSearchResults { $GraphRequest.CountOnly = $true } - New-GraphGetRequest @GraphRequest -ErrorAction Stop + New-GraphGetRequest @GraphRequest -ErrorAction Stop | Sort-Object -Property createdDateTime -Descending } } diff --git a/Modules/CIPPCore/Public/AuditLogs/New-CippAuditLogSearch.ps1 b/Modules/CIPPCore/Public/AuditLogs/New-CippAuditLogSearch.ps1 index a840dac13486..2cbbd76e9e8d 100644 --- a/Modules/CIPPCore/Public/AuditLogs/New-CippAuditLogSearch.ps1 +++ b/Modules/CIPPCore/Public/AuditLogs/New-CippAuditLogSearch.ps1 @@ -125,7 +125,7 @@ function New-CippAuditLogSearch { ) $SearchParams = @{ - displayName = 'CIPP Audit Search - ' + (Get-Date).ToString('yyyy-MM-dd HH:mm:ss') + displayName = $DisplayName filterStartDateTime = $StartTime.ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ss') filterEndDateTime = $EndTime.AddHours(1).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ss') } diff --git a/Modules/CIPPCore/Public/ConversionTable.csv b/Modules/CIPPCore/Public/ConversionTable.csv index 2658524b8cf2..9407f70d6cb7 100644 --- a/Modules/CIPPCore/Public/ConversionTable.csv +++ b/Modules/CIPPCore/Public/ConversionTable.csv @@ -240,6 +240,9 @@ Dynamics 365 Enterprise Edition - Additional Production Instance for Government, "Dynamics 365 Field Service, Enterprise Edition - Resource Scheduling Optimization",CRM_AUTO_ROUTING_ADDON,977464c4-bfaf-4b67-b761-a9bb735a2196,CRM_AUTO_ROUTING_ENGINE_ADDON,24435e4b-87d0-4d7d-8beb-63a9b1573022,Field Service – Automated Routing Engine Add-On "Dynamics 365 Field Service, Enterprise Edition - Resource Scheduling Optimization",CRM_AUTO_ROUTING_ADDON,977464c4-bfaf-4b67-b761-a9bb735a2196,CRM_AUTO_ROUTING_ADDON,2ba394e0-6f18-4b77-b45f-a5663bbab540,RETIRED - Field Service – Automated Routing Engine Add-On "Dynamics 365 Field Service, Enterprise Edition - Resource Scheduling Optimization",CRM_AUTO_ROUTING_ADDON,977464c4-bfaf-4b67-b761-a9bb735a2196,EXCHANGE_S_FOUNDATION,113feb6c-3fe4-4440-bddc-54d774bf0318,Exchange Foundation +Dynamics 365 Field Service Contractor,D365_FIELD_SERVICE_CONTRACTOR,23e6e135-e869-4ce4-9ae4-5710cd69ac13,CDS_FIELD_SERVICE_CONTRACTOR,f4614a66-d632-443a-bc77-afe92987b322,Common Data Service Field service Part Time Contractors +Dynamics 365 Field Service Contractor,D365_FIELD_SERVICE_CONTRACTOR,23e6e135-e869-4ce4-9ae4-5710cd69ac13,EXCHANGE_S_FOUNDATION,113feb6c-3fe4-4440-bddc-54d774bf0318,Exchange Foundation +Dynamics 365 Field Service Contractor,D365_FIELD_SERVICE_CONTRACTOR,23e6e135-e869-4ce4-9ae4-5710cd69ac13,POWERAPPS_DYN_APPS,874fc546-6efe-4d22-90b8-5c4e7aa59f4b,Power Apps for Dynamics 365 Dynamics 365 Field Service Contractor for Government,D365_FIELD_SERVICE_CONTRACTOR_GOV,e7965e3a-1f49-4d67-a3de-ad1ce460bbcc,CDS_FIELD_SERVICE_CONTRACTOR_GCC,2457fe40-65be-48a1-935f-924ad6e62dba,Common Data Service Field service Part Time Contractors for Government Dynamics 365 Field Service Contractor for Government,D365_FIELD_SERVICE_CONTRACTOR_GOV,e7965e3a-1f49-4d67-a3de-ad1ce460bbcc,EXCHANGE_S_FOUNDATION_GOV,922ba911-5694-4e99-a794-73aed9bfeec8,Exchange Foundation for Government Dynamics 365 Field Service Contractor for Government,D365_FIELD_SERVICE_CONTRACTOR_GOV,e7965e3a-1f49-4d67-a3de-ad1ce460bbcc,POWERAPPS_DYN_APPS_GOV,3089c02b-e533-4b73-96a5-01fa648c3c3c,PowerApps for Dynamics 365 for Government @@ -2246,6 +2249,93 @@ Microsoft 365 E5,SPE_E5,06ebc4ee-1bb5-47dd-8120-11324bc54e06,AAD_PREMIUM_P2,eec0 Microsoft 365 E5,SPE_E5,06ebc4ee-1bb5-47dd-8120-11324bc54e06,INTUNE_A,c1ec4a95-1f05-45b3-a911-aa3fa01094f5,Microsoft Intune Plan 1 Microsoft 365 E5,SPE_E5,06ebc4ee-1bb5-47dd-8120-11324bc54e06,FLOW_O365_P3,07699545-9485-468e-95b6-2fca3738be01,Power Automate for Office 365 Microsoft 365 E5,SPE_E5,06ebc4ee-1bb5-47dd-8120-11324bc54e06,POWER_VIRTUAL_AGENTS_O365_P3,ded3d325-1bdc-453e-8432-5bac26d7a014,Power Virtual Agents for Office 365 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MESH_AVATARS_FOR_TEAMS,dcf9d2f4-772e-4434-b757-77a453cfbc02,Avatars for Teams +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MESH_AVATARS_ADDITIONAL_FOR_TEAMS,3efbd4ed-8958-4824-8389-1321f8730af8,Avatars for Teams (additional) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,CDS_O365_P3,afa73018-811e-46e9-988f-f75d2b1b8430,Common Data Service for Teams +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,LOCKBOX_ENTERPRISE,9f431833-0334-42de-a7dc-70aa40db46db,Customer Lockbox +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,CustomerLockboxA_Enterprise,3ec18638-bd4c-4d3b-8905-479ed636b83e,Customer Lockbox (A) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MIP_S_Exchange,cd31b152-6326-4d1b-ae1b-997b625182e6,Data Classification in Microsoft 365 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,EXCHANGE_S_ENTERPRISE,efb87545-963c-4e0d-99df-69c6916d9eb0,Exchange Online (Plan 2) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,GRAPH_CONNECTORS_SEARCH_INDEX,a6520331-d7d4-4276-95f5-15c0933bc757,Graph Connectors Search with Index +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MESH_IMMERSIVE_FOR_TEAMS,f0ff6ac6-297d-49cd-be34-6dfef97f0c28,Immersive spaces for Teams +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,INFORMATION_BARRIERS,c4801e8a-cb58-4c35-aca6-f2dcc106f287,Information Barriers +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,Content_Explorer,d9fa6af4-e046-4c89-9226-729a0786685d,Information Protection and Governance Analytics - Premium +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,ContentExplorer_Standard,2b815d45-56e4-4e3a-b65c-66cb9175b560,Information Protection and Governance Analytics – Standard +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MIP_S_CLP2,efb0351d-3b08-4503-993d-383af8de41e3,Information Protection for Office 365 - Premium +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MIP_S_CLP1,5136a095-5cf0-4aff-bec3-e84448b38ea5,Information Protection for Office 365 - Standard +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MYANALYTICS_P2,33c4f319-9bdd-48d6-9c4d-410b750a4a5a,Insights by MyAnalytics +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,M365_ADVANCED_AUDITING,2f442157-a11c-46b9-ae5b-6e39ff4e5849,Microsoft 365 Advanced Auditing +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,OFFICESUBSCRIPTION,43de0ff5-c92c-492b-9116-175376d08c38,Microsoft 365 Apps for enterprise +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MCOMEETADV,3e26ee1f-8a5f-4d52-aee2-b81ce45c8f40,Microsoft 365 Audio Conferencing +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,M365_AUDIT_PLATFORM,f6de4823-28fa-440b-b886-4783fa86ddba,Microsoft 365 Audit Platform +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MICROSOFT_COMMUNICATION_COMPLIANCE,a413a9ff-720c-4822-98ef-2f37c2a21f4c,Microsoft 365 Communication Compliance +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MTP,bf28f719-7844-4079-9c78-c1307898e192,Microsoft 365 Defender +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,M365_LIGHTHOUSE_CUSTOMER_PLAN1,6f23d6a9-adbf-481c-8538-b4c095654487,Microsoft 365 Lighthouse (Plan 1) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MCOEV,4828c8ec-dc2e-4779-b502-87ac9ce28ab7,Microsoft 365 Phone System +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MICROSOFTBOOKINGS,199a5c09-e0ca-4e37-8f7c-b05d533e1ea2,Microsoft Bookings +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,CLIPCHAMP,a1ace008-72f3-4ea0-8dac-33b3a23a2472,Microsoft Clipchamp +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,COMMUNICATIONS_DLP,6dc145d6-95dd-4191-b9c3-185575ee6f6b,Microsoft Communications DLP +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,CUSTOMER_KEY,6db1f1db-2b46-403f-be40-e39395f08dbb,Microsoft Customer Key +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,ATP_ENTERPRISE,f20fedf3-f3c3-43c3-8267-2bfdd51c0939,Microsoft Defender for Office 365 (Plan 1) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,THREAT_INTELLIGENCE,8e0c0a52-6a6c-4d40-8370-dd62790dcd70,Microsoft Defender for Office 365 (Plan 2) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,EXCEL_PREMIUM,531ee2f8-b1cb-453b-9c21-d2180d014ca5,Microsoft Excel Advanced Analytics +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,FORMS_PLAN_E5,e212cbc7-0961-4c40-9825-01117710dcb1,Microsoft Forms (Plan E5) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,INFO_GOVERNANCE,e26c2fcc-ab91-4a61-b35c-03cdc8dddf66,Microsoft Information Governance +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,INSIDER_RISK,d587c7a3-bda9-4f99-8776-9bcf59c84f75,Microsoft Insider Risk Management +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,INSIDER_RISK_MANAGEMENT,9d0c4ee5-e4a1-4625-ab39-d82b619b1a34,Microsoft Insider Risk Management - Exchange +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,KAIZALA_STANDALONE,0898bdbb-73b0-471a-81e5-20f1fe4dd66e,Microsoft Kaizala Pro +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MICROSOFT_LOOP,c4b8c31a-fb44-4c65-9837-a21f55fcabda,Microsoft Loop +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,ML_CLASSIFICATION,d2d51368-76c9-4317-ada2-a12c004c432f,Microsoft ML-Based Classification +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,EXCHANGE_ANALYTICS,34c0d7a0-a70f-4668-9238-47f9fc208882,Microsoft MyAnalytics (Full) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,PROJECTWORKMANAGEMENT,b737dad2-2f6c-4c65-90e3-ca563267e8b9,Microsoft Planner +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,RECORDS_MANAGEMENT,65cc641f-cccd-4643-97e0-a17e3045e541,Microsoft Records Management +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MICROSOFT_SEARCH,94065c59-bc8e-4e8b-89e5-5138d471eaff,Microsoft Search +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,Deskless,8c7d2df8-86f0-4902-b2ed-a0458298f3b3,Microsoft StaffHub +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,STREAM_O365_E5,6c6042f5-6f01-4d67-b8c1-eb99d36eed3e,Microsoft Stream for Office 365 E5 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,INTUNE_O365,882e1d05-acd1-4ccb-8708-6ee03664b117,Mobile Device Management for Office 365 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,Nucleus,db4d623d-b514-490b-b7ef-8885eee514de,Nucleus +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,EQUIVIO_ANALYTICS,4de31727-a228-4ec3-a5bf-8e45b5ca48cc,Office 365 Advanced eDiscovery +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,ADALLOM_S_O365,8c098270-9dd4-4350-9b30-ba4703f3b36b,Office 365 Cloud App Security +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,PAM_ENTERPRISE,b1188c4c-1b36-4018-b48b-ee07604f6feb,Office 365 Privileged Access Management +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,SAFEDOCS,bf6f5520-59e3-4f82-974b-7dbbc4fd27c7,Office 365 SafeDocs +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,SHAREPOINTWAC,e95bec33-7c88-4a70-8e19-b10bd9d0c014,Office for the Web +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,PEOPLE_SKILLS_FOUNDATION,13b6da2c-0d84-450e-9f69-a33e221387ca,People Skills - Foundation +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,POWERAPPS_O365_P3,9c0dab89-a30c-4117-86e7-97bda240acd2,Power Apps for Office 365 (Plan 3) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,BI_AZURE_P2,70d33638-9c74-4d01-bfd3-562de28bd4ba,Power BI Pro +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,PREMIUM_ENCRYPTION,617b097b-4b93-4ede-83de-5f075bb5fb2f,Premium Encryption in Office 365 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,PROJECT_O365_P3,b21a6b06-1988-436e-a07b-51ec6d9f52ad,Project for Office (Plan E5) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,PURVIEW_DISCOVERY,c948ea65-2053-4a5a-8a62-9eaaaf11b522,Purview Discovery +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,Bing_Chat_Enterprise,0d0c0d31-fae7-41f2-b909-eaf4d7f26dba,RETIRED - Commercial data protection for Microsoft Copilot +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,COMMUNICATIONS_COMPLIANCE,41fcdd7d-4733-4863-9cf4-c65b83ce2df4,RETIRED - Microsoft Communications Compliance +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,DATA_INVESTIGATIONS,46129a58-a698-46f0-aa5b-17f6586297d9,Retired - Microsoft Data Investigations +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,PLACES_CORE,1fe6227d-3e01-46d0-9510-0acad4ff6e94,RETIRED - Places Core +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,SHAREPOINTENTERPRISE,5dbe027f-2339-4123-9542-606e4d348a72,SharePoint (Plan 2) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MCOSTANDARD,0feaeb32-d00e-4d66-bd5a-43b5b83db82c,Skype for Business Online (Plan 2) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,SWAY,a23b959c-7ce8-4e57-9140-b90eb88a9e97,Sway +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,BPOS_S_TODO_3,3fb82609-8c27-4f7b-bd51-30634711ee67,To-Do (Plan 3) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,VIVAENGAGE_CORE,a82fbf69-b4d7-49f4-83a6-915b2cf354f4,Viva Engage Core +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,VIVA_LEARNING_SEEDED,b76fb638-6ba6-402a-b9f9-83d28acb3d86,Viva Learning Seeded +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,WHITEBOARD_PLAN3,4a51bca5-1eff-43f5-878c-177680f191af,Whiteboard (Plan 3) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,YAMMER_ENTERPRISE,7547a3fe-08ee-4ccb-b430-5077c5041653,Yammer Enterprise +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,WINDEFATP,871d91ec-ec1a-452b-a83f-bd76c7d770ef,Microsoft Defender for Endpoint +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MICROSOFTENDPOINTDLP,64bfac92-2b17-4482-b5e5-a0304429de3e,Microsoft Endpoint DLP +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,UNIVERSAL_PRINT_01,795f6fe0-cc4d-4773-b050-5dde4dc704c9,Universal Print +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,WIN10_PRO_ENT_SUB,21b439ba-a0ca-424f-a6cc-52f954a5b111,Windows 10/11 Enterprise (Original) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,Windows_Autopatch,9a6eeb79-0b4b-4bf0-9808-39d99a2cd5a3,Windows Autopatch +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,WINDOWSUPDATEFORBUSINESS_DEPLOYMENTSERVICE,7bf960f6-2cd9-443a-8046-5dbff9558365,Windows Update for Business Deployment Service +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,RMS_S_PREMIUM,6c57d4b6-3b23-47a5-9bc9-69f17b4947b3,Azure Information Protection Premium P1 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,RMS_S_PREMIUM2,5689bec4-755d-4753-8b61-40975025187c,Azure Information Protection Premium P2 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,RMS_S_ENTERPRISE,bea4c11e-220a-4e6d-8eb8-8ea15d019f90,Azure Rights Management +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,DYN365_CDS_O365_P3,28b0fa46-c39a-4188-89e2-58e979a6b014,Common Data Service +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,Defender_for_Iot_Enterprise,99cd49a9-0e54-4e07-aea1-d8d9f5f704f5,Defender for IoT - Enterprise IoT Security +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MFA_PREMIUM,8a256a2b-b617-496d-b51b-e76466e88db0,Microsoft Azure Multi-Factor Authentication +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,ADALLOM_S_STANDALONE,2e2ddb96-6af9-4b1d-a3f0-d6ecfd22edb2,Microsoft Defender for Cloud Apps +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,ATA,14ab5db5-e6c4-4b20-b4bc-13e36fd2227f,Microsoft Defender for Identity +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,AAD_PREMIUM,41781fb2-bc02-4b7c-bd55-b576c07bb09d,Microsoft Entra ID P1 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,AAD_PREMIUM_P2,eec0eb4f-6444-4f95-aba0-50c24d67f998,Microsoft Entra ID P2 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,INTUNE_A,c1ec4a95-1f05-45b3-a911-aa3fa01094f5,Microsoft Intune Plan 1 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,FLOW_O365_P3,07699545-9485-468e-95b6-2fca3738be01,Power Automate for Office 365 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,POWER_VIRTUAL_AGENTS_O365_P3,ded3d325-1bdc-453e-8432-5bac26d7a014,Power Virtual Agents for Office 365 Microsoft 365 E5 EEA (no Teams) with Calling Minutes,Microsoft_365_E5_EEA_(no_Teams)_with_Calling_Minutes,6ee4114a-9b2d-4577-9e7a-49fa43d222d3,CDS_O365_P3,afa73018-811e-46e9-988f-f75d2b1b8430,Common Data Service for Teams Microsoft 365 E5 EEA (no Teams) with Calling Minutes,Microsoft_365_E5_EEA_(no_Teams)_with_Calling_Minutes,6ee4114a-9b2d-4577-9e7a-49fa43d222d3,LOCKBOX_ENTERPRISE,9f431833-0334-42de-a7dc-70aa40db46db,Customer Lockbox Microsoft 365 E5 EEA (no Teams) with Calling Minutes,Microsoft_365_E5_EEA_(no_Teams)_with_Calling_Minutes,6ee4114a-9b2d-4577-9e7a-49fa43d222d3,MIP_S_Exchange,cd31b152-6326-4d1b-ae1b-997b625182e6,Data Classification in Microsoft 365 @@ -5127,6 +5217,11 @@ Power Apps Premium,POWERAPPS_PER_USER,b30411f5-fea1-4a59-9ad9-3db7c7ead579,DYN36 Power Apps Premium,POWERAPPS_PER_USER,b30411f5-fea1-4a59-9ad9-3db7c7ead579,CDSAICAPACITY_PERUSER,91f50f7b-2204-4803-acac-5cf5668b8b39,DO NOT USE - AI Builder capacity Per User add-on Power Apps Premium,POWERAPPS_PER_USER,b30411f5-fea1-4a59-9ad9-3db7c7ead579,POWERAPPS_PER_USER,ea2cf03b-ac60-46ae-9c1d-eeaeb63cec86,Power Apps per User Plan Power Apps Premium,POWERAPPS_PER_USER,b30411f5-fea1-4a59-9ad9-3db7c7ead579,Flow_PowerApps_PerUser,dc789ed8-0170-4b65-a415-eb77d5bb350a,Power Automate for Power Apps per User Plan +Power Apps Premium embedded,POWERAPPS_PER_USER_ISVEMB,2a6fb3c6-30cc-4558-a69d-032425c1a3ba,Power_Pages_Internal_User,60bf28f9-2b70-4522-96f7-335f5e06c941,Power Pages Internal User +Power Apps Premium embedded,POWERAPPS_PER_USER_ISVEMB,2a6fb3c6-30cc-4558-a69d-032425c1a3ba,EXCHANGE_S_FOUNDATION,113feb6c-3fe4-4440-bddc-54d774bf0318,Exchange Foundation +Power Apps Premium embedded,POWERAPPS_PER_USER_ISVEMB,2a6fb3c6-30cc-4558-a69d-032425c1a3ba,DYN365_CDS_P2,6ea4c1ef-c259-46df-bce2-943342cd3cb2,Common Data Service +Power Apps Premium embedded,POWERAPPS_PER_USER_ISVEMB,2a6fb3c6-30cc-4558-a69d-032425c1a3ba,POWERAPPS_PER_USER,ea2cf03b-ac60-46ae-9c1d-eeaeb63cec86,Power Apps per User Plan +Power Apps Premium embedded,POWERAPPS_PER_USER_ISVEMB,2a6fb3c6-30cc-4558-a69d-032425c1a3ba,Flow_PowerApps_PerUser,dc789ed8-0170-4b65-a415-eb77d5bb350a,Power Automate for Power Apps per User Plan Power Apps Premium for Government,POWERAPPS_PER_USER_GCC,8e4c6baa-f2ff-4884-9c38-93785d0d7ba1,CDSAICAPACITY_PERUSER,91f50f7b-2204-4803-acac-5cf5668b8b39,AI Builder capacity Per User add-on Power Apps Premium for Government,POWERAPPS_PER_USER_GCC,8e4c6baa-f2ff-4884-9c38-93785d0d7ba1,CDSAICAPACITY_PERUSER_NEW,74d93933-6f22-436e-9441-66d205435abb,AI Builder capacity Per User add-on Power Apps Premium for Government,POWERAPPS_PER_USER_GCC,8e4c6baa-f2ff-4884-9c38-93785d0d7ba1,DYN365_CDS_P2_GOV,37396c73-2203-48e6-8be1-d882dae53275,Common Data Service for Government diff --git a/Modules/CIPPCore/Public/DeltaQueries/New-GraphDeltaQuery.ps1 b/Modules/CIPPCore/Public/DeltaQueries/New-GraphDeltaQuery.ps1 new file mode 100644 index 000000000000..9ec6c2bbdd0c --- /dev/null +++ b/Modules/CIPPCore/Public/DeltaQueries/New-GraphDeltaQuery.ps1 @@ -0,0 +1,64 @@ +function New-GraphDeltaQuery { + <# + .SYNOPSIS + Creates a new Graph Delta Query. + .DESCRIPTION + This function creates a new Graph Delta Query to track changes in a specified resource. + .PARAMETER Resource + The resource to track changes for (e.g., 'users', 'groups'). + .PARAMETER TenantFilter + The tenant to filter the query on. + #> + [CmdletBinding(DefaultParameterSetName = 'NewDeltaQuery')] + param( + [Parameter(Mandatory = $true, ParameterSetName = 'NewDeltaQuery')] + [Parameter(Mandatory = $true, ParameterSetName = 'DeltaUrl')] + [string]$TenantFilter, + + [Parameter(ParameterSetName = 'NewDeltaQuery', Mandatory = $true)] + [ValidateSet('users', 'groups', 'contacts', 'devices', 'applications', 'servicePrincipals', 'directoryObjects', 'administrativeUnits')] + [string]$Resource, + + [Parameter(ParameterSetName = 'NewDeltaQuery', Mandatory = $false)] + [hashtable]$Parameters = @{}, + + [Parameter(ParameterSetName = 'DeltaUrl', Mandatory = $true)] + [string]$DeltaUrl + ) + + try { + if ($DeltaUrl) { + $GraphQuery = [System.UriBuilder]$DeltaUrl + } else { + $GraphQuery = [System.UriBuilder]('https://graph.microsoft.com/beta/{0}/delta' -f $Resource) + $QueryParams = @{ + '$deltaToken' = 'latest' + } + $ParamCollection = [System.Web.HttpUtility]::ParseQueryString([String]::Empty) + + foreach ($key in $QueryParams.Keys) { + if ($QueryParams[$key]) { + $ParamCollection.Add($key, $QueryParams[$key]) + } + } + + foreach ($Item in ($Parameters.GetEnumerator() | Sort-Object -CaseSensitive -Property Key)) { + if ($Item.Value -is [System.Boolean]) { + $Item.Value = $Item.Value.ToString().ToLower() + } + if ($Item.Value) { + $ParamCollection.Add($Item.Key, $Item.Value) + } + } + $GraphQuery.Query = $ParamCollection.ToString() + } + + #Write-Information "Creating Delta Query for $Resource with parameters: $($GraphQuery.Query)" + $response = New-GraphGetRequest -tenantid $TenantFilter -uri $GraphQuery.ToString() -ReturnRawResponse + Write-Information "Delta Query created successfully for $Resource. Response: $($response | ConvertTo-Json -Depth 5)" + return $response.Content + } catch { + Write-Error "Failed to create Delta Query: $(Get-NormalizedError -Message $_.Exception.message)" + Write-Warning $_.InvocationInfo.PositionMessage + } +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-ExecAppApprovalTemplate.ps1 b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-ExecAppApprovalTemplate.ps1 index acde7892b480..e6eec613a662 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-ExecAppApprovalTemplate.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-ExecAppApprovalTemplate.ps1 @@ -10,7 +10,7 @@ function Push-ExecAppApprovalTemplate { $TemplateId = $Item.templateId if (!$TemplateId) { Write-LogMessage -message 'No template specified' -tenant $Item.Tenant -API 'Add Multitenant App' -sev Error - return + return $false } # Get the template data to determine if it's a Gallery Template or Enterprise App @@ -19,7 +19,7 @@ function Push-ExecAppApprovalTemplate { if (!$Template) { Write-LogMessage -message "Template $TemplateId not found" -tenant $Item.Tenant -API 'Add Multitenant App' -sev Error - return + return $false } $TemplateData = $Template.JSON | ConvertFrom-Json @@ -37,14 +37,14 @@ function Push-ExecAppApprovalTemplate { $GalleryTemplateId = $TemplateData.GalleryTemplateId if (!$GalleryTemplateId) { Write-LogMessage -message 'Gallery Template ID not found in template data' -tenant $Item.Tenant -API 'Add Multitenant App' -sev Error - return + return $false } # Check if the app already exists in the tenant $ServicePrincipalList = New-GraphGETRequest -uri "https://graph.microsoft.com/beta/servicePrincipals?`$select=AppId,id,displayName&`$top=999" -tenantid $Item.Tenant if ($TemplateData.GalleryTemplateId -in $ServicePrincipalList.applicationTemplateId) { Write-LogMessage -message "Gallery Template app $($TemplateData.AppName) already exists in tenant $($Item.Tenant)" -tenant $Item.Tenant -API 'Add Gallery App' -sev Info - return + return $true } # Instantiate the gallery template @@ -74,14 +74,36 @@ function Push-ExecAppApprovalTemplate { $ApplicationManifest = $TemplateData.ApplicationManifest if (!$ApplicationManifest) { Write-LogMessage -message 'Application Manifest not found in template data' -tenant $Item.Tenant -API 'Add Multitenant App' -sev Error - return + return $false } # Check for existing application by display name - $ExistingApp = New-GraphGETRequest -uri "https://graph.microsoft.com/beta/applications?`$filter=displayName eq '$($TemplateData.AppName)'&`$top=1" -tenantid $Item.Tenant -NoAuthCheck $true - if ($ExistingApp -and $ExistingApp.value) { - Write-LogMessage -message "Application Manifest $($TemplateData.AppName) already exists in tenant $($Item.Tenant)" -tenant $Item.Tenant -API 'Add App Manifest' -sev Info - return + $ServicePrincipalList = New-GraphGETRequest -uri "https://graph.microsoft.com/beta/servicePrincipals?`$select=AppId,id,displayName&`$top=999" -tenantid $Item.Tenant + $ExistingApp = $ServicePrincipalList | Where-Object { $_.displayName -eq $TemplateData.AppName } + if ($ExistingApp) { + Write-LogMessage -message "Application with name '$($TemplateData.AppName)' already exists in tenant $($Item.Tenant)" -tenant $Item.Tenant -API 'Add App Manifest' -sev Info + + # get existing application + $App = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/applications(appId='$($ExistingApp.appId)')" -tenantid $Item.Tenant + + # compare permissions + $ExistingPermissions = $App.requiredResourceAccess | ConvertTo-Json -Depth 10 + $NewPermissions = $ApplicationManifest.requiredResourceAccess | ConvertTo-Json -Depth 10 + if ($ExistingPermissions -ne $NewPermissions) { + Write-LogMessage -message "Updating permissions for existing application '$($TemplateData.AppName)' in tenant $($Item.Tenant)" -tenant $Item.Tenant -API 'Add App Manifest' -sev Info + + # Update permissions for existing application + $UpdateBody = @{ + requiredResourceAccess = $ApplicationManifest.requiredResourceAccess + } | ConvertTo-Json -Depth 10 + $null = New-GraphPostRequest -type PATCH -uri "https://graph.microsoft.com/beta/applications(appId='$($ExistingApp.appId)')" -tenantid $Item.Tenant -body $UpdateBody + + # consent new permissions + Add-CIPPDelegatedPermission -RequiredResourceAccess $ApplicationManifest.requiredResourceAccess -ApplicationId $ExistingApp.appId -Tenantfilter $Item.Tenant + Add-CIPPApplicationPermission -RequiredResourceAccess $ApplicationManifest.requiredResourceAccess -ApplicationId $ExistingApp.appId -Tenantfilter $Item.Tenant + } + + return $true } $PropertiesToRemove = @('appId', 'id', 'createdDateTime', 'publisherDomain', 'servicePrincipalLockConfiguration', 'identifierUris', 'applicationIdUris') @@ -103,22 +125,20 @@ function Push-ExecAppApprovalTemplate { appId = $CreatedApp.appId } | ConvertTo-Json - $ServicePrincipal = New-GraphPostRequest -uri 'https://graph.microsoft.com/beta/servicePrincipals' -type POST -tenantid $Item.tenant -body $ServicePrincipalBody + $null = New-GraphPostRequest -uri 'https://graph.microsoft.com/beta/servicePrincipals' -type POST -tenantid $Item.tenant -body $ServicePrincipalBody Write-LogMessage -message "Successfully deployed Application Manifest $($TemplateData.AppName) to tenant $($Item.Tenant). Application ID: $($CreatedApp.appId)" -tenant $Item.Tenant -API 'Add App Manifest' -sev Info - $DelegateResourceAccess = $ApplicationManifest.requiredResourceAccess - $ApplicationResourceAccess = $ApplicationManifest.requiredResourceAccess - if ($ApplicationManifest.requiredResourceAccess) { - Add-CIPPDelegatedPermission -RequiredResourceAccess $ApplicationManifest.requiredResourceAccess -ApplicationId $App -Tenantfilter $Tenant - Add-CIPPApplicationPermission -RequiredResourceAccess $ApplicationManifest.requiredResourceAccess -ApplicationId $App -Tenantfilter $Tenant - } + if ($CreatedApp.requiredResourceAccess) { + Add-CIPPDelegatedPermission -RequiredResourceAccess $CreatedApp.requiredResourceAccess -ApplicationId $CreatedApp.appId -Tenantfilter $Item.Tenant + Add-CIPPApplicationPermission -RequiredResourceAccess $CreatedApp.requiredResourceAccess -ApplicationId $CreatedApp.appId -Tenantfilter $Item.Tenant + } } else { Write-LogMessage -message "Application Manifest deployment failed - no application ID returned for $($TemplateData.AppName) in tenant $($Item.Tenant)" -tenant $Item.Tenant -API 'Add App Manifest' -sev Error } } catch { Write-LogMessage -message "Error creating application from manifest in tenant $($Item.Tenant) - $($_.Exception.Message)" -tenant $Item.Tenant -API 'Add App Manifest' -sev Error - throw + throw $_.Exception.Message } } else { diff --git a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-SchedulerCIPPNotifications.ps1 b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-SchedulerCIPPNotifications.ps1 index 23e199078efc..438511222545 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-SchedulerCIPPNotifications.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-SchedulerCIPPNotifications.ps1 @@ -106,7 +106,7 @@ function Push-SchedulerCIPPNotifications { try { Write-Information $($config | ConvertTo-Json) Write-Information $config.webhook - if ($Config.webhook -ne '' -and $null) { + if (![string]::IsNullOrEmpty($config.webhook)) { if ($Currentlog) { $JSONContent = $Currentlog | ConvertTo-Json -Compress Send-CIPPAlert -Type 'webhook' -JSONContent $JSONContent -TenantFilter $Tenant -APIName 'Alerts' diff --git a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Standards/Push-CIPPDriftManagement.ps1 b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Standards/Push-CIPPDriftManagement.ps1 new file mode 100644 index 000000000000..e5d7fe008cbb --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Standards/Push-CIPPDriftManagement.ps1 @@ -0,0 +1,79 @@ +function Push-CippDriftManagement { + <# + .FUNCTIONALITY + Entrypoint + #> + param ( + $Item + ) + + Write-Information "Received drift standard item for $($Item.Tenant)" + + try { + $Drift = Get-CIPPDrift -TenantFilter $Item.Tenant + if ($Drift.newDeviationsCount -gt 0) { + $Settings = (Get-CIPPTenantAlignment -TenantFilter $Item.Tenant | Where-Object -Property standardType -EQ 'drift') + $email = $Settings.driftAlertEmail + $webhook = $Settings.driftAlertWebhook + $CippConfigTable = Get-CippTable -tablename Config + $CippConfig = Get-CIPPAzDataTableEntity @CippConfigTable -Filter "PartitionKey eq 'InstanceProperties' and RowKey eq 'CIPPURL'" + $CIPPURL = 'https://{0}' -f $CippConfig.Value + $Data = $Drift.currentDeviations | ForEach-Object { + $currentValue = if ($_.receivedValue -and $_.receivedValue.Length -gt 200) { + $_.receivedValue.Substring(0, 200) + '...' + } else { + $_.receivedValue + } + [PSCustomObject]@{ + Standard = $_.standardDisplayName ? $_.standardDisplayName : $_.standardName + 'Expected Value' = $_.expectedValue + 'Current Value' = $currentValue + Status = $_.status + } + } + + $GenerateEmail = New-CIPPAlertTemplate -format 'html' -data $Data -CIPPURL $CIPPURL -Tenant $Item.Tenant -InputObject 'driftStandard' -AuditLogLink $drift.standardId + $CIPPAlert = @{ + Type = 'email' + Title = $GenerateEmail.title + HTMLContent = $GenerateEmail.htmlcontent + TenantFilter = $Item.Tenant + } + Write-Host 'Going to send the mail' + Send-CIPPAlert @CIPPAlert -altEmail $email + $WebhookData = @{ + Title = $GenerateEmail.title + ActionUrl = $GenerateEmail.ButtonUrl + ActionText = $GenerateEmail.ButtonText + AlertData = $Data + Tenant = $Item.Tenant + } | ConvertTo-Json -Depth 15 -Compress + $CippAlert = @{ + Type = 'webhook' + Title = $GenerateEmail.title + JSONContent = $WebhookData + TenantFilter = $Item.Tenant + } + Write-Host 'Sending Webhook Content' + Send-CIPPAlert @CippAlert -altWebhook $webhook + #Always do PSA. + $CIPPAlert = @{ + Type = 'psa' + Title = $GenerateEmail.title + HTMLContent = $GenerateEmail.htmlcontent + TenantFilter = $Item.Tenant + } + Send-CIPPAlert @CIPPAlert + return $true + } else { + Write-LogMessage -API 'DriftStandards' -tenant $Item.Tenant -message "No new drift deviations found for tenant $($Item.Tenant)" -sev Info + return $true + } + Write-Information "Drift management completed for tenant $($Item.Tenant)" + } catch { + Write-LogMessage -API 'DriftStandards' -tenant $Item.Tenant -message "Error running Drift Check for tenant $($Item.Tenant) - $($_.Exception.Message)" -sev Error -LogData (Get-CippException -Exception $_) + Write-Warning "Error running drift standards for tenant $($Item.Tenant) - $($_.Exception.Message)" + Write-Information $_.InvocationInfo.PositionMessage + throw $_.Exception.Message + } +} diff --git a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Webhooks/Push-AuditLogTenantDownload.ps1 b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Webhooks/Push-AuditLogTenantDownload.ps1 index 88ae358c98a2..f7171d1cdac2 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Webhooks/Push-AuditLogTenantDownload.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Webhooks/Push-AuditLogTenantDownload.ps1 @@ -1,6 +1,5 @@ function Push-AuditLogTenantDownload { - Param($Item) - $ConfigTable = Get-CippTable -TableName 'WebhookRules' + param($Item) $TenantFilter = $Item.TenantFilter try { @@ -37,47 +36,43 @@ function Push-AuditLogTenantDownload { } else { $CIPPURL = 'https://{0}' -f $CippConfig.Value } } - # Get webhook rules - $ConfigEntries = Get-CIPPAzDataTableEntity @ConfigTable $LogSearchesTable = Get-CippTable -TableName 'AuditLogSearches' - $Configuration = $ConfigEntries | Where-Object { ($_.Tenants -match $TenantFilter -or $_.Tenants -match 'AllTenants') } - if ($Configuration) { - try { - $LogSearches = Get-CippAuditLogSearches -TenantFilter $TenantFilter -ReadyToProcess | Select-Object -First 10 - Write-Information ('Audit Logs: Found {0} searches, begin downloading' -f $LogSearches.Count) - foreach ($Search in $LogSearches) { - $SearchEntity = Get-CIPPAzDataTableEntity @LogSearchesTable -Filter "Tenant eq '$($TenantFilter)' and RowKey eq '$($Search.id)'" - $SearchEntity.CippStatus = 'Processing' - Add-CIPPAzDataTableEntity @LogSearchesTable -Entity $SearchEntity -Force - try { - Write-Information "Audit Log search: Processing search ID: $($Search.id) for tenant: $TenantFilter" - $Downloads = New-CIPPAuditLogSearchResultsCache -TenantFilter $TenantFilter -searchId $Search.id - $SearchEntity.CippStatus = 'Downloaded' - } catch { - if ($_.Exception.Message -match 'Request rate is large. More Request Units may be needed, so no changes were made. Please retry this request later.') { - $SearchEntity.CippStatus = 'Pending' - Write-Information "Audit Log search: Rate limit hit for $($SearchEntity.RowKey)." - if ($SearchEntity.PSObject.Properties.Name -contains 'RetryCount') { - $SearchEntity.RetryCount++ - } else { - $SearchEntity | Add-Member -MemberType NoteProperty -Name RetryCount -Value 1 - } + try { + $LogSearches = Get-CippAuditLogSearches -TenantFilter $TenantFilter -ReadyToProcess | Select-Object -First 10 + Write-Information ('Audit Logs: Found {0} searches, begin downloading' -f $LogSearches.Count) + foreach ($Search in $LogSearches) { + $SearchEntity = Get-CIPPAzDataTableEntity @LogSearchesTable -Filter "Tenant eq '$($TenantFilter)' and RowKey eq '$($Search.id)'" + $SearchEntity.CippStatus = 'Processing' + Add-CIPPAzDataTableEntity @LogSearchesTable -Entity $SearchEntity -Force + try { + Write-Information "Audit Log search: Processing search ID: $($Search.id) for tenant: $TenantFilter" + $Downloads = New-CIPPAuditLogSearchResultsCache -TenantFilter $TenantFilter -searchId $Search.id + $SearchEntity.CippStatus = 'Downloaded' + } catch { + if ($_.Exception.Message -match 'Request rate is large. More Request Units may be needed, so no changes were made. Please retry this request later.') { + $SearchEntity.CippStatus = 'Pending' + Write-Information "Audit Log search: Rate limit hit for $($SearchEntity.RowKey)." + if ($SearchEntity.PSObject.Properties.Name -contains 'RetryCount') { + $SearchEntity.RetryCount++ } else { - $Exception = [string](ConvertTo-Json -Compress -InputObject (Get-CippException -Exception $_)) - $SearchEntity | Add-Member -MemberType NoteProperty -Name Error -Value $Exception - $SearchEntity.CippStatus = 'Failed' - Write-Information "Error processing audit log rules: $($_.Exception.Message)" + $SearchEntity | Add-Member -MemberType NoteProperty -Name RetryCount -Value 1 } - + } else { + $Exception = [string](ConvertTo-Json -Compress -InputObject (Get-CippException -Exception $_)) + $SearchEntity | Add-Member -MemberType NoteProperty -Name Error -Value $Exception + $SearchEntity.CippStatus = 'Failed' + Write-Information "Error processing audit log rules: $($_.Exception.Message)" } - Add-CIPPAzDataTableEntity @LogSearchesTable -Entity $SearchEntity -Force + } - } catch { - Write-Information ('Audit Log search: Error {0} line {1} - {2}' -f $_.InvocationInfo.ScriptName, $_.InvocationInfo.ScriptLineNumber, $_.Exception.Message) - exit 0 + Add-CIPPAzDataTableEntity @LogSearchesTable -Entity $SearchEntity -Force } + } catch { + Write-Information ('Audit Log search: Error {0} line {1} - {2}' -f $_.InvocationInfo.ScriptName, $_.InvocationInfo.ScriptLineNumber, $_.Exception.Message) + exit 0 } + } catch { Write-Information ('Push-AuditLogTenant: Error {0} line {1} - {2}' -f $_.InvocationInfo.ScriptName, $_.InvocationInfo.ScriptLineNumber, $_.Exception.Message) exit 0 diff --git a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Webhooks/Push-AuditLogTenantProcess.ps1 b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Webhooks/Push-AuditLogTenantProcess.ps1 index 086adcaf37dc..f52eef63271f 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Webhooks/Push-AuditLogTenantProcess.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Webhooks/Push-AuditLogTenantProcess.ps1 @@ -1,5 +1,5 @@ function Push-AuditLogTenantProcess { - Param($Item) + param($Item) $TenantFilter = $Item.TenantFilter $RowIds = $Item.RowIds @@ -20,12 +20,13 @@ function Push-AuditLogTenantProcess { if ($Rows.Count -gt 0) { Write-Information "Retrieved $($Rows.Count) rows from cache for processing" Test-CIPPAuditLogRules -TenantFilter $TenantFilter -Rows $Rows - exit 0 + return $true } else { Write-Information 'No rows found in cache for the provided row IDs' - exit 0 + return $false } } catch { Write-Information ('Push-AuditLogTenant: Error {0} line {1} - {2}' -f $_.InvocationInfo.ScriptName, $_.InvocationInfo.ScriptLineNumber, $_.Exception.Message) + return $false } } diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Core/Invoke-ExecEditTemplate.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Core/Invoke-ExecEditTemplate.ps1 index 96bb985840b9..cb88cf706419 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Core/Invoke-ExecEditTemplate.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Core/Invoke-ExecEditTemplate.ps1 @@ -16,7 +16,7 @@ function Invoke-ExecEditTemplate { try { $Table = Get-CippTable -tablename 'templates' - $guid = $request.body.id + $guid = $request.body.id ? $request.body.id : $request.body.GUID $JSON = ConvertTo-Json -Compress -Depth 100 -InputObject ($request.body | Select-Object * -ExcludeProperty GUID) $Type = $request.query.Type diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Core/Invoke-ListDirectoryObjects.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Core/Invoke-ListDirectoryObjects.ps1 index 42d4da8b8085..ad8e708b4213 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Core/Invoke-ListDirectoryObjects.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Core/Invoke-ListDirectoryObjects.ps1 @@ -1,7 +1,7 @@ function Invoke-ListDirectoryObjects { <# .FUNCTIONALITY - Entrypoint + Entrypoint,AnyTenant .ROLE CIPP.Core.Read #> @@ -12,7 +12,7 @@ function Invoke-ListDirectoryObjects { $Headers = $Request.Headers Write-LogMessage -headers $Headers -API $APIName -message 'Accessed this API' -Sev 'Debug' - $TenantFilter = $Request.Body.tenantFilter + $TenantFilter = $Request.Body.partnerLookup ? $env:TenantID : $Request.Body.tenantFilter $AsApp = $Request.Body.asApp $Ids = $Request.Body.ids @@ -28,7 +28,7 @@ function Invoke-ListDirectoryObjects { } | ConvertTo-Json -Depth 10 try { - $Results = New-GraphPOSTRequest -tenantid $TenantFilter -uri $Uri -body $Body -AsApp $AsApp + $Results = New-GraphPOSTRequest -tenantid $TenantFilter -uri $Uri -body $Body -AsApp $AsApp -NoAuthCheck $true $StatusCode = [System.Net.HttpStatusCode]::OK } catch { $StatusCode = [System.Net.HttpStatusCode]::BadRequest diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Scheduler/Invoke-ListScheduledItems.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Scheduler/Invoke-ListScheduledItems.ps1 index b60fe1e64a15..68d4e7f692c2 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Scheduler/Invoke-ListScheduledItems.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Scheduler/Invoke-ListScheduledItems.ps1 @@ -61,12 +61,18 @@ function Invoke-ListScheduledItems { $Tasks = $Tasks | Where-Object -Property Tenant -In $AllowedTenantDomains } $ScheduledTasks = foreach ($Task in $tasks) { + if (!$Task.Tenant -or !$Task.Command) { + continue + } + if ($Task.Parameters) { $Task.Parameters = $Task.Parameters | ConvertFrom-Json -ErrorAction SilentlyContinue } else { $Task | Add-Member -NotePropertyName Parameters -NotePropertyValue @{} } - if ($Task.Recurrence -eq 0 -or [string]::IsNullOrEmpty($Task.Recurrence)) { + if (!$Task.Recurrence) { + $Task | Add-Member -NotePropertyName Recurrence -NotePropertyValue 'Once' -Force + } elseif ($Task.Recurrence -eq 0 -or [string]::IsNullOrEmpty($Task.Recurrence)) { $Task.Recurrence = 'Once' } try { diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecRestoreBackup.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecRestoreBackup.ps1 index 9a12d734adce..25fbf73dc0ed 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecRestoreBackup.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Settings/Invoke-ExecRestoreBackup.ps1 @@ -1,6 +1,6 @@ using namespace System.Net -Function Invoke-ExecRestoreBackup { +function Invoke-ExecRestoreBackup { <# .FUNCTIONALITY Entrypoint @@ -27,7 +27,7 @@ Function Invoke-ExecRestoreBackup { $Table.Entity = $ht2 Add-CIPPAzDataTableEntity @Table -Force } - Write-LogMessage -headers $Request.Headers -API $APINAME -message 'Created backup' -Sev 'Debug' + Write-LogMessage -headers $Request.Headers -API $APINAME -message "Restored backup $($Request.Body.BackupName)" -Sev 'Info' $body = [pscustomobject]@{ 'Results' = 'Successfully restored backup.' } @@ -44,7 +44,7 @@ Function Invoke-ExecRestoreBackup { $Table.Entity = $ht2 Add-AzDataTableEntity @Table -Force } - Write-LogMessage -headers $Request.Headers -API $APINAME -message 'Created backup' -Sev 'Debug' + Write-LogMessage -headers $Request.Headers -API $APINAME -message "Restored backup $($Request.Body.BackupName)" -Sev 'Info' $body = [pscustomobject]@{ 'Results' = 'Successfully restored backup.' diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Administration/Invoke-ExecModifyMBPerms.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Administration/Invoke-ExecModifyMBPerms.ps1 index cddc705a3556..ff03133f3675 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Administration/Invoke-ExecModifyMBPerms.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Email-Exchange/Administration/Invoke-ExecModifyMBPerms.ps1 @@ -11,188 +11,382 @@ Function Invoke-ExecModifyMBPerms { param($Request, $TriggerMetadata) $APIName = $Request.Params.CIPPEndpoint - Write-LogMessage -headers $Request.Headers -API $APINAME-message 'Accessed this API' -Sev 'Debug' + Write-LogMessage -headers $Request.Headers -API $APINAME -message 'Accessed this API' -Sev 'Debug' - $Username = $request.body.userID - $Tenantfilter = $request.body.tenantfilter - $Permissions = $request.body.permissions - - if ($username -eq $null) { exit } - - $userid = (New-GraphGetRequest -uri "https://graph.microsoft.com/beta/users/$($username)" -tenantid $Tenantfilter).id + # Extract mailbox requests - handle all three formats + $MailboxRequests = $null $Results = [System.Collections.ArrayList]::new() - # Convert permissions to array format if it's an object with numeric keys - if ($Permissions -is [PSCustomObject]) { - if ($Permissions.PSObject.Properties.Name -match '^\d+$') { - $Permissions = $Permissions.PSObject.Properties.Value - } - else { - $Permissions = @($Permissions) - } + # Direct array format + if ($request.body -is [array]) { + $MailboxRequests = $request.body + } + # Bulk format with mailboxRequests property + elseif ($request.body.mailboxRequests) { + $MailboxRequests = $request.body.mailboxRequests + } + # Legacy single mailbox format + elseif ($request.body.userID -and $request.body.permissions) { + $MailboxRequests = @([PSCustomObject]@{ + userID = $request.body.userID + tenantFilter = $request.body.tenantFilter + permissions = $request.body.permissions + }) + } + + if (-not $MailboxRequests -or $MailboxRequests.Count -eq 0) { + Write-LogMessage -headers $Request.Headers -API $APINAME -message 'No mailbox requests provided' -Sev 'Error' + $body = [pscustomobject]@{'Results' = @("No mailbox requests provided") } + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::BadRequest + Body = $Body + }) + return } - foreach ($Permission in $Permissions) { - $PermissionLevels = $Permission.PermissionLevel - $Modification = $Permission.Modification - $AutoMap = if ($Permission.PSObject.Properties.Name -contains 'AutoMap') { $Permission.AutoMap } else { $true } + $TenantFilter = $Request.body.tenantFilter + Write-LogMessage -headers $Request.Headers -API $APINAME -message "Processing permission changes for $($MailboxRequests.Count) mailboxes" -Sev 'Info' -tenant $TenantFilter - # Handle multiple permission levels separated by commas - if ($PermissionLevels -like "*,*") { - $PermissionLevelArray = $PermissionLevels -split ',' | ForEach-Object { $_.Trim() } - } - else { - $PermissionLevelArray = @($PermissionLevels.Trim()) + # Build cmdlet array for processing + $CmdletArray = [System.Collections.ArrayList]::new() + $CmdletMetadataArray = [System.Collections.ArrayList]::new() + $GuidToMetadataMap = @{} # Map GUIDs to our metadata + $UserLookupCache = @{} + + foreach ($MailboxRequest in $MailboxRequests) { + $Username = $MailboxRequest.userID + $Permissions = $MailboxRequest.permissions + + if ([string]::IsNullOrEmpty($Username)) { + $null = $Results.Add("Skipped mailbox with missing userID") + continue } - # Handle UserID as array of objects or single value - $TargetUsers = if ($Permission.UserID -is [array]) { - $Permission.UserID | ForEach-Object { $_.value } + # User lookup with caching for bulk operations + if (-not $UserLookupCache.ContainsKey($Username)) { + try { + $UserObject = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/users/$($Username)" -tenantid $TenantFilter + $UserLookupCache[$Username] = $UserObject.userPrincipalName + } + catch { + try { + $UserObject = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/users?`$filter=userPrincipalName eq '$Username'" -tenantid $TenantFilter + if ($UserObject.value -and $UserObject.value.Count -gt 0) { + $UserLookupCache[$Username] = $UserObject.value[0].userPrincipalName + } else { + throw "User not found" + } + } + catch { + Write-LogMessage -headers $Request.Headers -API $APINAME -message "Could not find user $($Username)" -Sev 'Error' -tenant $TenantFilter + $null = $Results.Add("Could not find user $($Username)") + continue + } + } } - else { - @($Permission.UserID) + $UserId = $UserLookupCache[$Username] + + # Convert permissions to array if needed + if ($Permissions -is [PSCustomObject]) { + if ($Permissions.PSObject.Properties.Name -match '^\d+$') { + $Permissions = $Permissions.PSObject.Properties.Value + } else { + $Permissions = @($Permissions) + } } - foreach ($TargetUser in $TargetUsers) { - foreach ($PermissionLevel in $PermissionLevelArray) { - try { + foreach ($Permission in $Permissions) { + $PermissionLevels = $Permission.PermissionLevel + $Modification = $Permission.Modification + $AutoMap = if ($Permission.PSObject.Properties.Name -contains 'AutoMap') { $Permission.AutoMap } else { $true } + + # Handle multiple permission levels + $PermissionLevelArray = if ($PermissionLevels -like "*,*") { + $PermissionLevels -split ',' | ForEach-Object { $_.Trim() } + } else { + @($PermissionLevels.Trim()) + } + + # Extract target users from UserID (handle array of objects or single values) + $TargetUsers = if ($Permission.UserID -is [array]) { + $Permission.UserID | ForEach-Object { + if ($_ -is [PSCustomObject] -and $_.value) { + $_.value + } else { + $_.ToString() + } + } + } else { + if ($Permission.UserID -is [PSCustomObject] -and $Permission.UserID.value) { + @($Permission.UserID.value) + } else { + @($Permission.UserID.ToString()) + } + } + + foreach ($TargetUser in $TargetUsers) { + foreach ($PermissionLevel in $PermissionLevelArray) { + + # Create cmdlet parameters based on permission type and action + $CmdletParams = @{} + $CmdletName = "" + $ExpectedResult = "" + switch ($PermissionLevel) { 'FullAccess' { if ($Modification -eq 'Remove') { - $MailboxPerms = New-ExoRequest -Anchor $username -tenantid $Tenantfilter -cmdlet 'Remove-mailboxpermission' -cmdParams @{ - Identity = $userid + $CmdletName = 'Remove-MailboxPermission' + $CmdletParams = @{ + Identity = $UserId user = $TargetUser accessRights = @('FullAccess') Confirm = $false } - $null = $results.Add("Removed $($TargetUser) from $($username) Shared Mailbox permissions (FullAccess)") - } - else { - $MailboxPerms = New-ExoRequest -Anchor $username -tenantid $Tenantfilter -cmdlet 'Add-MailboxPermission' -cmdParams @{ - Identity = $userid + $ExpectedResult = "Removed $($TargetUser) from $($Username) FullAccess permissions" + } else { + $CmdletName = 'Add-MailboxPermission' + $CmdletParams = @{ + Identity = $UserId user = $TargetUser accessRights = @('FullAccess') automapping = $AutoMap Confirm = $false } - $null = $results.Add("Granted $($TargetUser) access to $($username) Mailbox (FullAccess) with automapping set to $($AutoMap)") + $ExpectedResult = "Granted $($TargetUser) FullAccess to $($Username) with automapping $($AutoMap)" } } 'SendAs' { if ($Modification -eq 'Remove') { - $MailboxPerms = New-ExoRequest -Anchor $username -tenantid $Tenantfilter -cmdlet 'Remove-RecipientPermission' -cmdParams @{ - Identity = $userid + $CmdletName = 'Remove-RecipientPermission' + $CmdletParams = @{ + Identity = $UserId Trustee = $TargetUser accessRights = @('SendAs') Confirm = $false } - $null = $results.Add("Removed $($TargetUser) from $($username) with Send As permissions") - } - else { - $MailboxPerms = New-ExoRequest -Anchor $username -tenantid $Tenantfilter -cmdlet 'Add-RecipientPermission' -cmdParams @{ - Identity = $userid + $ExpectedResult = "Removed $($TargetUser) SendAs permissions from $($Username)" + } else { + $CmdletName = 'Add-RecipientPermission' + $CmdletParams = @{ + Identity = $UserId Trustee = $TargetUser accessRights = @('SendAs') Confirm = $false } - $null = $results.Add("Granted $($TargetUser) access to $($username) with Send As permissions") + $ExpectedResult = "Granted $($TargetUser) SendAs permissions to $($Username)" } } 'SendOnBehalf' { + $CmdletName = 'Set-Mailbox' if ($Modification -eq 'Remove') { - $MailboxPerms = New-ExoRequest -Anchor $username -tenantid $Tenantfilter -cmdlet 'Set-Mailbox' -cmdParams @{ - Identity = $userid + $CmdletParams = @{ + Identity = $UserId GrantSendonBehalfTo = @{ '@odata.type' = '#Exchange.GenericHashTable' remove = $TargetUser } Confirm = $false } - $null = $results.Add("Removed $($TargetUser) from $($username) Send on Behalf Permissions") - } - else { - $MailboxPerms = New-ExoRequest -Anchor $username -tenantid $Tenantfilter -cmdlet 'Set-Mailbox' -cmdParams @{ - Identity = $userid + $ExpectedResult = "Removed $($TargetUser) SendOnBehalf permissions from $($Username)" + } else { + $CmdletParams = @{ + Identity = $UserId GrantSendonBehalfTo = @{ '@odata.type' = '#Exchange.GenericHashTable' add = $TargetUser } Confirm = $false } - $null = $results.Add("Granted $($TargetUser) access to $($username) with Send On Behalf Permissions") + $ExpectedResult = "Granted $($TargetUser) SendOnBehalf permissions to $($Username)" } } 'ReadPermission' { if ($Modification -eq 'Remove') { - $MailboxPerms = New-ExoRequest -Anchor $username -tenantid $Tenantfilter -cmdlet 'Remove-MailboxPermission' -cmdParams @{ - Identity = $userid + $CmdletName = 'Remove-MailboxPermission' + $CmdletParams = @{ + Identity = $UserId user = $TargetUser accessRights = @('ReadPermission') Confirm = $false } - $null = $results.Add("Removed $($TargetUser) from $($username) Read Permissions") + $ExpectedResult = "Removed $($TargetUser) ReadPermission from $($Username)" } } 'ExternalAccount' { if ($Modification -eq 'Remove') { - $MailboxPerms = New-ExoRequest -Anchor $username -tenantid $Tenantfilter -cmdlet 'Remove-MailboxPermission' -cmdParams @{ - Identity = $userid + $CmdletName = 'Remove-MailboxPermission' + $CmdletParams = @{ + Identity = $UserId user = $TargetUser accessRights = @('ExternalAccount') Confirm = $false } - $null = $results.Add("Removed $($TargetUser) from $($username) Read Permissions") + $ExpectedResult = "Removed $($TargetUser) ExternalAccount permissions from $($Username)" } } 'DeleteItem' { if ($Modification -eq 'Remove') { - $MailboxPerms = New-ExoRequest -Anchor $username -tenantid $Tenantfilter -cmdlet 'Remove-MailboxPermission' -cmdParams @{ - Identity = $userid + $CmdletName = 'Remove-MailboxPermission' + $CmdletParams = @{ + Identity = $UserId user = $TargetUser accessRights = @('DeleteItem') Confirm = $false } - $null = $results.Add("Removed $($TargetUser) from $($username) Read Permissions") + $ExpectedResult = "Removed $($TargetUser) DeleteItem permissions from $($Username)" } } 'ChangePermission' { if ($Modification -eq 'Remove') { - $MailboxPerms = New-ExoRequest -Anchor $username -tenantid $Tenantfilter -cmdlet 'Remove-MailboxPermission' -cmdParams @{ - Identity = $userid + $CmdletName = 'Remove-MailboxPermission' + $CmdletParams = @{ + Identity = $UserId user = $TargetUser accessRights = @('ChangePermission') Confirm = $false } - $null = $results.Add("Removed $($TargetUser) from $($username) Read Permissions") + $ExpectedResult = "Removed $($TargetUser) ChangePermission from $($Username)" } } 'ChangeOwner' { if ($Modification -eq 'Remove') { - $MailboxPerms = New-ExoRequest -Anchor $username -tenantid $Tenantfilter -cmdlet 'Remove-MailboxPermission' -cmdParams @{ - Identity = $userid + $CmdletName = 'Remove-MailboxPermission' + $CmdletParams = @{ + Identity = $UserId user = $TargetUser accessRights = @('ChangeOwner') Confirm = $false } - $null = $results.Add("Removed $($TargetUser) from $($username) Read Permissions") + $ExpectedResult = "Removed $($TargetUser) ChangeOwner permissions from $($Username)" } } } - Write-LogMessage -headers $Request.Headers -API $APINAME-message "Executed $($PermissionLevel) permission modification for $($TargetUser) on $($username)" -Sev 'Info' -tenant $TenantFilter - } - catch { - Write-LogMessage -headers $Request.Headers -API $APINAME-message "Could not execute $($PermissionLevel) permission modification for $($TargetUser) on $($username)" -Sev 'Error' -tenant $TenantFilter - $null = $results.Add("Could not execute $($PermissionLevel) permission modification for $($TargetUser) on $($username). Error: $($_.Exception.Message)") + + if ($CmdletName) { + # Generate unique GUID for this operation + $OperationGuid = [Guid]::NewGuid().ToString() + + $CmdletObj = @{ + CmdletInput = @{ + CmdletName = $CmdletName + Parameters = $CmdletParams + } + OperationGuid = $OperationGuid # Add GUID to cmdlet object + } + + $CmdletMetadata = [PSCustomObject]@{ + ExpectedResult = $ExpectedResult + Mailbox = $Username + TargetUser = $TargetUser + Permission = $PermissionLevel + Action = $Modification + OperationGuid = $OperationGuid + } + + $null = $CmdletArray.Add($CmdletObj) + $null = $CmdletMetadataArray.Add($CmdletMetadata) + + # Map GUID to metadata for precise result mapping + $GuidToMetadataMap[$OperationGuid] = $CmdletMetadata + } } } } } - $body = [pscustomobject]@{'Results' = @($results) } - - # Associate values to output bindings by calling 'Push-OutputBinding'. - Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + if ($CmdletArray.Count -eq 0) { + Write-LogMessage -headers $Request.Headers -API $APINAME -message 'No valid cmdlets to process' -Sev 'Warning' -tenant $TenantFilter + $body = [pscustomobject]@{'Results' = @("No valid permission changes to process") } + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::OK Body = $Body }) + return + } + + # Execute requests - use enhanced bulk processing with GUID mapping + if ($CmdletArray.Count -gt 1) { + # Use bulk processing with GUID tracking + try { + Write-LogMessage -headers $Request.Headers -API $APINAME -message "Executing bulk request with $($CmdletArray.Count) cmdlets" -Sev 'Info' -tenant $TenantFilter + $BulkResults = New-ExoBulkRequest -tenantid $TenantFilter -cmdletArray @($CmdletArray) -ReturnWithCommand $true + + # Process bulk results using GUID mapping + if ($BulkResults -is [hashtable] -and $BulkResults.Keys.Count -gt 0) { + foreach ($cmdletName in $BulkResults.Keys) { + foreach ($result in $BulkResults[$cmdletName]) { + $operationGuid = $result.OperationGuid + + if ($operationGuid -and $GuidToMetadataMap.ContainsKey($operationGuid)) { + $metadata = $GuidToMetadataMap[$operationGuid] + + if ($result.error) { + $ErrorMessage = try { (Get-CippException -Exception $result.error).NormalizedError } catch { $result.error } + $null = $Results.Add("Error processing $($metadata.Permission) for $($metadata.TargetUser) on $($metadata.Mailbox): $ErrorMessage") + Write-LogMessage -headers $Request.Headers -API $APINAME -message "Error for operation $operationGuid`: $ErrorMessage" -Sev 'Error' -tenant $TenantFilter + } else { + $null = $Results.Add($metadata.ExpectedResult) + Write-LogMessage -headers $Request.Headers -API $APINAME -message "Success for operation $operationGuid`: $($metadata.ExpectedResult)" -Sev 'Info' -tenant $TenantFilter + } + } else { + Write-LogMessage -headers $Request.Headers -API $APINAME -message "Could not map result to operation. GUID: $operationGuid, Available GUIDs: $($GuidToMetadataMap.Keys -join ', ')" -Sev 'Warning' -tenant $TenantFilter + + # Fallback for unmapped results + if ($result.error) { + $ErrorMessage = try { (Get-CippException -Exception $result.error).NormalizedError } catch { $result.error } + $null = $Results.Add("Error in $cmdletName`: $ErrorMessage") + } else { + $null = $Results.Add("Completed $cmdletName operation") + } + } + } + } + } else { + # If no results returned but no error thrown, assume all succeeded + foreach ($CmdletMetadata in $CmdletMetadataArray) { + if ($CmdletMetadata.ExpectedResult) { + $null = $Results.Add($CmdletMetadata.ExpectedResult) + } + } + } + + Write-LogMessage -headers $Request.Headers -API $APINAME -message "Bulk request completed successfully" -Sev 'Info' -tenant $TenantFilter + } + catch { + Write-LogMessage -headers $Request.Headers -API $APINAME -message "Bulk request failed, using fallback: $($_.Exception.Message)" -Sev 'Error' -tenant $TenantFilter + + # Fallback to individual processing + for ($i = 0; $i -lt $CmdletArray.Count; $i++) { + $CmdletObj = $CmdletArray[$i] + $CmdletMetadata = $CmdletMetadataArray[$i] + try { + $null = New-ExoRequest -Anchor $CmdletMetadata.Mailbox -tenantid $TenantFilter -cmdlet $CmdletObj.CmdletInput.CmdletName -cmdParams $CmdletObj.CmdletInput.Parameters + $null = $Results.Add($CmdletMetadata.ExpectedResult) + } + catch { + $null = $Results.Add("Error processing $($CmdletMetadata.Permission) for $($CmdletMetadata.TargetUser) on $($CmdletMetadata.Mailbox): $($_.Exception.Message)") + } + } + } + } + else { + # Use individual processing for single operation + $CmdletObj = $CmdletArray[0] + $CmdletMetadata = $CmdletMetadataArray[0] + try { + $null = New-ExoRequest -Anchor $CmdletMetadata.Mailbox -tenantid $TenantFilter -cmdlet $CmdletObj.CmdletInput.CmdletName -cmdParams $CmdletObj.CmdletInput.Parameters + $null = $Results.Add($CmdletMetadata.ExpectedResult) + Write-LogMessage -headers $Request.Headers -API $APINAME -message "Executed $($CmdletMetadata.Permission) permission modification" -Sev 'Info' -tenant $TenantFilter + } + catch { + Write-LogMessage -headers $Request.Headers -API $APINAME -message "Permission modification failed: $($_.Exception.Message)" -Sev 'Error' -tenant $TenantFilter + $null = $Results.Add("Error processing $($CmdletMetadata.Permission) for $($CmdletMetadata.TargetUser) on $($CmdletMetadata.Mailbox): $($_.Exception.Message)") + } + } + + $body = [pscustomobject]@{'Results' = @($Results) } + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $Body + }) } diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/MEM/Invoke-ExecDeviceAction.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/MEM/Invoke-ExecDeviceAction.ps1 index 5600b3984fce..5b18d65cfecf 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/MEM/Invoke-ExecDeviceAction.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/MEM/Invoke-ExecDeviceAction.ps1 @@ -1,6 +1,6 @@ using namespace System.Net -Function Invoke-ExecDeviceAction { +function Invoke-ExecDeviceAction { <# .FUNCTIONALITY Entrypoint @@ -22,6 +22,16 @@ Function Invoke-ExecDeviceAction { try { switch ($Action) { 'setDeviceName' { + if ($Request.Body.input -match '%') { + $Device = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/deviceManagement/managedDevices/$DeviceFilter" -tenantid $TenantFilter + $Request.Body.input = Get-CIPPTextReplacement -TenantFilter $TenantFilter -Text $Request.Body.input + $Request.Body.input = $Request.Body.input -replace '%SERIAL%', $Device.serialNumber + # limit to 15 characters + if ($Request.Body.input.Length -gt 15) { + $Request.Body.input = $Request.Body.input.Substring(0, 15) + } + } + $ActionBody = @{ deviceName = $Request.Body.input } | ConvertTo-Json -Compress break } @@ -30,7 +40,7 @@ Function Invoke-ExecDeviceAction { Write-Host "ActionBody: $ActionBody" break } - Default { $ActionBody = $Request.Body | ConvertTo-Json -Compress } + default { $ActionBody = $Request.Body | ConvertTo-Json -Compress } } $cmdParams = @{ diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/MEM/Invoke-RemovePolicy.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/MEM/Invoke-RemovePolicy.ps1 index af0206cc0619..a0941f3854e6 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/MEM/Invoke-RemovePolicy.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/MEM/Invoke-RemovePolicy.ps1 @@ -1,6 +1,6 @@ using namespace System.Net -Function Invoke-RemovePolicy { +function Invoke-RemovePolicy { <# .FUNCTIONALITY Entrypoint diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Groups/Invoke-EditGroup.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Groups/Invoke-EditGroup.ps1 index 18cd79effab4..9097ad19ab7a 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Groups/Invoke-EditGroup.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Groups/Invoke-EditGroup.ps1 @@ -45,12 +45,15 @@ function Invoke-EditGroup { target = $GroupId }) } else { + # Use new securityEnabled value if provided, otherwise keep original + $SecurityEnabled = $null -ne $UserObj.securityEnabled ? $UserObj.securityEnabled : $OrgGroup.securityEnabled + $PatchObj = @{ displayName = $UserObj.displayName description = $UserObj.description mailNickname = $UserObj.mailNickname mailEnabled = $OrgGroup.mailEnabled - securityEnabled = $OrgGroup.securityEnabled + securityEnabled = $SecurityEnabled } Write-Host "body: $($PatchObj | ConvertTo-Json -Depth 10 -Compress)" -ForegroundColor Yellow if ($UserObj.membershipRules) { $PatchObj | Add-Member -MemberType NoteProperty -Name 'membershipRule' -Value $UserObj.membershipRules -Force } @@ -58,6 +61,13 @@ function Invoke-EditGroup { $null = New-GraphPOSTRequest -type PATCH -uri "https://graph.microsoft.com/beta/groups/$($GroupId)" -tenantid $UserObj.tenantFilter -body ($PatchObj | ConvertTo-Json -Depth 10 -Compress) $Results.Add("Success - Edited group properties for $($GroupName) group. It might take some time to reflect the changes.") Write-LogMessage -headers $Headers -API $APIName -tenant $UserObj.tenantFilter -message "Edited group properties for $($GroupName) group" -Sev 'Info' + + # Log securityEnabled changes specifically + if ($null -ne $UserObj.securityEnabled -and $UserObj.securityEnabled -ne $OrgGroup.securityEnabled) { + $securityStatusText = "Security capabilities $($UserObj.securityEnabled ? 'enabled' : 'disabled') for group $($GroupName)" + Write-LogMessage -headers $Headers -API $APIName -tenant $UserObj.tenantFilter -message $securityStatusText -Sev 'Info' + $Results.Add($securityStatusText) + } } catch { $Results.Add("Error - Failed to edit group properties: $($_.Exception.Message)") Write-LogMessage -headers $Headers -API $APIName -tenant $UserObj.tenantFilter -message "Failed to patch group: $($_.Exception.Message)" -Sev 'Error' diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-CIPPOffboardingJob.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-CIPPOffboardingJob.ps1 index 0b1c0254759e..931fcd0af54b 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-CIPPOffboardingJob.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-CIPPOffboardingJob.ps1 @@ -32,7 +32,7 @@ function Invoke-CIPPOffboardingJob { } { $_.ResetPass -eq $true } { try { - Set-CIPPResetPassword -tenantFilter $TenantFilter -DisplayName -UserID $username -Headers $Headers -APIName $APIName + Set-CIPPResetPassword -tenantFilter $TenantFilter -DisplayName $DisplayName -UserID $username -Headers $Headers -APIName $APIName } catch { $_.Exception.Message } diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-EditUser.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-EditUser.ps1 index f5fd50105a6d..6249ac7c1680 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-EditUser.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-EditUser.ps1 @@ -38,17 +38,18 @@ function Invoke-EditUser { 'givenName' = $UserObj.givenName 'surname' = $UserObj.surname 'displayName' = $UserObj.displayName - 'department' = $UserObj.Department - 'mailNickname' = $UserObj.Username ? $UserObj.username :$UserObj.mailNickname + 'department' = $UserObj.department + 'mailNickname' = $UserObj.username ? $UserObj.username : $UserObj.mailNickname 'userPrincipalName' = $UserPrincipalName 'usageLocation' = $UserObj.usageLocation.value ? $UserObj.usageLocation.value : $UserObj.usageLocation - 'city' = $UserObj.City - 'country' = $UserObj.Country 'jobTitle' = $UserObj.jobTitle - 'mobilePhone' = $UserObj.MobilePhone + 'mobilePhone' = $UserObj.mobilePhone 'streetAddress' = $UserObj.streetAddress - 'postalCode' = $UserObj.PostalCode - 'companyName' = $UserObj.CompanyName + 'city' = $UserObj.city + 'state' = $UserObj.state + 'postalCode' = $UserObj.postalCode + 'country' = $UserObj.country + 'companyName' = $UserObj.companyName 'businessPhones' = $UserObj.businessPhones ? @($UserObj.businessPhones) : @() 'otherMails' = $UserObj.otherMails ? @($UserObj.otherMails) : @() 'passwordProfile' = @{ @@ -224,19 +225,13 @@ function Invoke-EditUser { } if ($Request.body.setManager.value) { - $ManagerBody = [PSCustomObject]@{'@odata.id' = "https://graph.microsoft.com/beta/users/$($Request.body.setManager.value)" } - $ManagerBodyJSON = ConvertTo-Json -Compress -Depth 10 -InputObject $ManagerBody - $null = New-GraphPostRequest -uri "https://graph.microsoft.com/beta/users/$($UserObj.id)/manager/`$ref" -tenantid $UserObj.tenantFilter -type PUT -body $ManagerBodyJSON -Verbose - Write-LogMessage -headers $Headers -API $APIName -tenant $UserObj.tenantFilter -message "Set $($UserObj.DisplayName)'s manager to $($Request.body.setManager.label)" -Sev Info - $Results.Add("Success. Set $($UserObj.DisplayName)'s manager to $($Request.body.setManager.label)") + $ManagerResult = Set-CIPPManager -User $UserPrincipalName -Manager $Request.body.setManager.value -TenantFilter $UserObj.tenantFilter -Headers $Headers + $Results.Add($ManagerResult) } if ($Request.body.setSponsor.value) { - $SponsorBody = [PSCustomObject]@{'@odata.id' = "https://graph.microsoft.com/beta/users/$($Request.body.setSponsor.value)" } - $SponsorBodyJSON = ConvertTo-Json -Compress -Depth 10 -InputObject $SponsorBody - $null = New-GraphPostRequest -uri "https://graph.microsoft.com/beta/users/$($UserObj.id)/sponsors/`$ref" -tenantid $UserObj.tenantFilter -type POST -body $SponsorBodyJSON -Verbose - Write-LogMessage -headers $Headers -API $APIName -tenant $UserObj.tenantFilter -message "Set $($UserObj.DisplayName)'s sponsor to $($Request.body.setSponsor.label)" -Sev Info - $Results.Add("Success. Set $($UserObj.DisplayName)'s sponsor to $($Request.body.setSponsor.label)") + $SponsorResult = Set-CIPPSponsor -User $UserPrincipalName -Sponsor $Request.body.setSponsor.value -TenantFilter $UserObj.tenantFilter -Headers $Headers + $Results.Add($SponsorResult) } # Associate values to output bindings by calling 'Push-OutputBinding'. diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ListUserMailboxDetails.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ListUserMailboxDetails.ps1 index 1f5ed5a1a1bf..34f7a1d7c5b1 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ListUserMailboxDetails.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-ListUserMailboxDetails.ps1 @@ -18,6 +18,10 @@ function Invoke-ListUserMailboxDetails { # Interact with query parameters or the body of the request. $TenantFilter = $Request.Query.tenantFilter $UserID = $Request.Query.UserID + $UserMail = $Request.Query.userMail + Write-Host "TenantFilter: $TenantFilter" + Write-Host "UserID: $UserID" + Write-Host "UserMail: $UserMail" try { $Requests = @( @@ -53,7 +57,7 @@ function Invoke-ListUserMailboxDetails { @{ CmdletInput = @{ CmdletName = 'Get-BlockedSenderAddress' - Parameters = @{ Identity = $UserID } + Parameters = @{ SenderAddress = $UserMail } } }, @{ @@ -63,7 +67,6 @@ function Invoke-ListUserMailboxDetails { } } ) - Write-Host $UserID $usernames = New-GraphGetRequest -tenantid $TenantFilter -uri 'https://graph.microsoft.com/beta/users?$select=id,userPrincipalName,displayName,mailNickname&$top=999' $Results = New-ExoBulkRequest -TenantId $TenantFilter -CmdletArray $Requests -returnWithCommand $true -Anchor $username Write-Host "First line of usernames is $($usernames[0] | ConvertTo-Json)" @@ -105,9 +108,9 @@ function Invoke-ListUserMailboxDetails { # Determine if the user is blocked for spam if ($BlockedSender -and $BlockedSender.Count -gt 0) { - $BlockedForSpam = $false - } else { $BlockedForSpam = $true + } else { + $BlockedForSpam = $false } } catch { Write-Error "Failed Fetching Data $($_.Exception.message): $($_.InvocationInfo.ScriptLineNumber)" diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-PatchUser.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-PatchUser.ps1 new file mode 100644 index 000000000000..cb8315167baa --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Users/Invoke-PatchUser.ps1 @@ -0,0 +1,103 @@ +function Invoke-PatchUser { + <# + .FUNCTIONALITY + Entrypoint + .ROLE + Identity.User.ReadWrite + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $Request.Params.CIPPEndpoint + $Headers = $Request.Headers + Write-LogMessage -headers $Headers -API $APIName -message 'Accessed this API' -Sev 'Debug' + + $HttpResponse = [HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = @{'Results' = @("Default response, you should never see this.") } + } + + try { + # Handle array of user objects or single user object + $Users = if ($Request.Body -is [array]) { + $Request.Body + } else { + @($Request.Body) + } + + # Validate that all users have required properties + $InvalidUsers = $Users | Where-Object { + [string]::IsNullOrWhiteSpace($_.id) -or [string]::IsNullOrWhiteSpace($_.tenantFilter) + } + if ($InvalidUsers.Count -gt 0) { + $HttpResponse.StatusCode = [HttpStatusCode]::BadRequest + $HttpResponse.Body = @{'Results' = @('Failed to patch user(s). Some users are missing id or tenantFilter') } + } else { + # Group users by tenant filter + $UsersByTenant = $Users | Group-Object -Property tenantFilter + + $TotalSuccessCount = 0 + $AllErrorMessages = @() + + # Process each tenant separately + foreach ($TenantGroup in $UsersByTenant) { + $tenantFilter = $TenantGroup.Name + $TenantUsers = $TenantGroup.Group + + # Build bulk requests for this tenant + $int = 0 + $BulkRequests = foreach ($User in $TenantUsers) { + # Remove the id and tenantFilter properties from the body since they're not user properties + $PatchBody = $User | Select-Object -Property * -ExcludeProperty id, tenantFilter + + @{ + id = $int++ + method = 'PATCH' + url = "users/$($User.id)" + body = $PatchBody + 'headers' = @{ + 'Content-Type' = 'application/json' + } + } + } + + # Execute bulk request for this tenant + $BulkResults = New-GraphBulkRequest -tenantid $tenantFilter -Requests @($BulkRequests) + + # Process results for this tenant + for ($i = 0; $i -lt $BulkResults.Count; $i++) { + $result = $BulkResults[$i] + $user = $TenantUsers[$i] + + if ($result.status -eq 200 -or $result.status -eq 204) { + $TotalSuccessCount++ + Write-LogMessage -headers $Headers -API $APIName -tenant $tenantFilter -message "Successfully patched user $($user.id)" -Sev 'Info' + } else { + $errorMsg = if ($result.body.error.message) { + $result.body.error.message + } else { + "Unknown error (Status: $($result.status))" + } + $AllErrorMessages += "Failed to patch user $($user.id) in tenant $($tenantFilter): $errorMsg" + Write-LogMessage -headers $Headers -API $APIName -tenant $tenantFilter -message "Failed to patch user $($user.id). Error: $errorMsg" -Sev 'Error' + } + } + } + + # Build final response + if ($AllErrorMessages.Count -eq 0) { + $TenantCount = ($Users | Select-Object -Property tenantFilter -Unique).Count + $HttpResponse.Body = @{'Results' = @("Successfully patched $TotalSuccessCount user$(if($TotalSuccessCount -ne 1){'s'}) across $TenantCount tenant$(if($TenantCount -ne 1){'s'})") } + } else { + $HttpResponse.StatusCode = [HttpStatusCode]::BadRequest + $HttpResponse.Body = @{'Results' = $AllErrorMessages + @("Successfully patched $TotalSuccessCount of $($Users.Count) users") } + } + } + + } catch { + $HttpResponse.StatusCode = [HttpStatusCode]::InternalServerError + $HttpResponse.Body = @{'Results' = @("Failed to patch user(s). Error: $($_.Exception.Message)") } + } + + Push-OutputBinding -Name Response -Value $HttpResponse +} \ No newline at end of file diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Administration/Alerts/Invoke-ExecAuditLogSearch.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Administration/Alerts/Invoke-ExecAuditLogSearch.ps1 index dc54f2259f53..d0fb549f3ca5 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Administration/Alerts/Invoke-ExecAuditLogSearch.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Administration/Alerts/Invoke-ExecAuditLogSearch.ps1 @@ -10,50 +10,123 @@ function Invoke-ExecAuditLogSearch { $APIName = $Request.Params.CIPPEndpoint $Headers = $Request.Headers + $Action = $Request.Query.Action ?? $Request.Body.Action + Write-LogMessage -headers $Headers -API $APIName -message 'Accessed this API' -Sev 'Debug' - $Query = $Request.Body - if (!$Query.TenantFilter) { - Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::BadRequest - Body = 'TenantFilter is required' - }) - return - } - if (!$Query.StartTime -or !$Query.EndTime) { - Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::BadRequest - Body = 'StartTime and EndTime are required' - }) - return - } + switch ($Action) { + 'ProcessLogs' { + $SearchId = $Request.Query.SearchId ?? $Request.Body.SearchId + $TenantFilter = $Request.Query.tenantFilter ?? $Request.Body.tenantFilter + if (!$SearchId) { + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::BadRequest + Body = 'SearchId is required' + }) + return + } + + $Search = New-GraphGetRequest -Uri "https://graph.microsoft.com/beta/security/auditLog/queries/$SearchId" -AsApp $true -TenantId $TenantFilter + Write-Information ($Search | ConvertTo-Json -Depth 10) - $Command = Get-Command New-CippAuditLogSearch - $AvailableParameters = $Command.Parameters.Keys - $BadProps = foreach ($Prop in $Query.PSObject.Properties.Name) { - if ($AvailableParameters -notcontains $Prop) { - $Prop + $Entity = [PSCustomObject]@{ + PartitionKey = [string]'Search' + RowKey = [string]$SearchId + Tenant = [string]$TenantFilter + DisplayName = [string]$Search.displayName + StartTime = [datetime]$Search.filterStartDateTime + EndTime = [datetime]$Search.filterEndDateTime + Query = [string]($Search | ConvertTo-Json -Compress) + CippStatus = [string]'Pending' + } + $Table = Get-CIPPTable -TableName 'AuditLogSearches' + Add-CIPPAzDataTableEntity @Table -Entity $Entity -Force | Out-Null + + Write-LogMessage -headers $Headers -API $APIName -message "Queued search for processing: $($Search.displayName)" -Sev 'Info' -tenant $TenantFilter + + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = @{ + resultText = "Search '$($Search.displayName)' queued for processing." + state = 'success' + } | ConvertTo-Json -Depth 10 -Compress + }) } - } - if ($BadProps) { - Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::BadRequest - Body = "Invalid parameters: $($BadProps -join ', ')" - }) - return - } + default { + $Query = $Request.Body + if (!$Query.TenantFilter) { + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::BadRequest + Body = 'TenantFilter is required' + }) + return + } + if (!$Query.StartTime -or !$Query.EndTime) { + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::BadRequest + Body = 'StartTime and EndTime are required' + }) + return + } + + # Convert StartTime and EndTime to DateTime from unixtime + if ($Query.StartTime -match '^\d+$') { + $Query.StartTime = [DateTime]::UnixEpoch.AddSeconds([long]$Query.StartTime) + } else { + $Query.StartTime = [DateTime]$Query.StartTime + } + + if ($Query.EndTime -match '^\d+$') { + $Query.EndTime = [DateTime]::UnixEpoch.AddSeconds([long]$Query.EndTime) + } else { + $Query.EndTime = [DateTime]$Query.EndTime + } + + $Command = Get-Command New-CippAuditLogSearch + $AvailableParameters = $Command.Parameters.Keys + $BadProps = foreach ($Prop in $Query.PSObject.Properties.Name) { + if ($AvailableParameters -notcontains $Prop) { + $Prop + } + } + if ($BadProps) { + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::BadRequest + Body = "Invalid parameters: $($BadProps -join ', ')" + }) + return + } - try { - $Query = $Query | ConvertTo-Json -Depth 10 | ConvertFrom-Json -AsHashtable - $Results = New-CippAuditLogSearch @Query - Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::OK - Body = $Results - }) - } catch { - Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ - StatusCode = [HttpStatusCode]::BadRequest - Body = $_.Exception.Message - }) + try { + Write-Information "Executing audit log search with parameters: $($Query | ConvertTo-Json -Depth 10)" + + $Query = $Query | ConvertTo-Json -Depth 10 | ConvertFrom-Json -AsHashtable + $NewSearch = New-CippAuditLogSearch @Query + + if ($NewSearch) { + Write-LogMessage -headers $Headers -API $APIName -message "Created audit log search: $($NewSearch.displayName)" -Sev 'Info' -tenant $TenantFilter + $Results = @{ + resultText = "Created audit log search: $($NewSearch.displayName)" + state = 'success' + details = $NewSearch + } + } else { + Write-LogMessage -headers $Headers -API $APIName -message 'Failed to create audit log search' -Sev 'Error' -tenant $TenantFilter + $Results = @{ + resultText = 'Failed to initiate search' + state = 'error' + } + } + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $Results + }) + } catch { + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::BadRequest + Body = $_.Exception.Message + }) + } + } } } diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Administration/Alerts/Invoke-ListAuditLogSearches.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Administration/Alerts/Invoke-ListAuditLogSearches.ps1 index 4468443aece3..988969f55970 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Administration/Alerts/Invoke-ListAuditLogSearches.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Administration/Alerts/Invoke-ListAuditLogSearches.ps1 @@ -18,7 +18,6 @@ function Invoke-ListAuditLogSearches { $Days = $Request.Query.Days $Type = $Request.Query.Type - if ($TenantFilter) { switch ($Type) { 'Searches' { diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Administration/Application Approval/Invoke-ExecAppApprovalTemplate.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Administration/Application Approval/Invoke-ExecAppApprovalTemplate.ps1 index 9368600072bb..9adb1dc1c6b1 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Administration/Application Approval/Invoke-ExecAppApprovalTemplate.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Administration/Application Approval/Invoke-ExecAppApprovalTemplate.ps1 @@ -102,7 +102,6 @@ function Invoke-ExecAppApprovalTemplate { if ($Request.Query.TemplateId) { $templateId = $Request.Query.TemplateId $filter = "PartitionKey eq 'AppApprovalTemplate' and RowKey eq '$templateId'" - Write-LogMessage -headers $Headers -API $APIName -message "Retrieved specific template: $templateId" -Sev 'Info' } $Templates = Get-CIPPAzDataTableEntity @Table -Filter $filter diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/GDAP/Invoke-ExecGDAPRoleTemplate.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/GDAP/Invoke-ExecGDAPRoleTemplate.ps1 index ec02efcf937d..58f0b556d73c 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/GDAP/Invoke-ExecGDAPRoleTemplate.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/GDAP/Invoke-ExecGDAPRoleTemplate.ps1 @@ -20,8 +20,10 @@ Function Invoke-ExecGDAPRoleTemplate { if ($Request.Query.TemplateId) { $Template = $Templates | Where-Object -Property RowKey -EQ $Request.Query.TemplateId if (!$Template) { + Write-LogMessage -headers $Headers -API $APIName -message "GDAP role template '$($Request.Query.TemplateId)' not found" -Sev 'Warning' $Body = @{} } else { + Write-LogMessage -headers $Headers -API $APIName -message "Retrieved GDAP role template '$($Request.Query.TemplateId)'" -Sev 'Info' $Body = @{ TemplateId = $Template.RowKey RoleMappings = @($Template.RoleMappings | ConvertFrom-Json) @@ -38,6 +40,7 @@ Function Invoke-ExecGDAPRoleTemplate { } Write-Information ($RoleMappings | ConvertTo-Json) Add-CIPPGDAPRoleTemplate -TemplateId $RowKey -RoleMappings $RoleMappings + Write-LogMessage -headers $Headers -API $APIName -message "Added role mappings to GDAP template '$RowKey'" -Sev 'Info' $Body = @{ Results = "Added role mappings to template $RowKey" } @@ -48,10 +51,12 @@ Function Invoke-ExecGDAPRoleTemplate { if ($Template) { $RoleMappings = $Request.Body.RoleMappings Add-CIPPGDAPRoleTemplate -TemplateId $RowKey -RoleMappings $RoleMappings -Overwrite + Write-LogMessage -headers $Headers -API $APIName -message "Updated role mappings for GDAP template '$RowKey'" -Sev 'Info' $Body = @{ Results = "Updated role mappings for template $RowKey" } } else { + Write-LogMessage -headers $Headers -API $APIName -message "GDAP role template '$RowKey' not found for editing" -Sev 'Warning' $Body = @{ Results = "Template $RowKey not found" } @@ -62,16 +67,19 @@ Function Invoke-ExecGDAPRoleTemplate { $Template = $Templates | Where-Object -Property RowKey -EQ $RowKey if ($Template) { Remove-AzDataTableEntity -Force @Table -Entity $Template + Write-LogMessage -headers $Headers -API $APIName -message "Deleted GDAP role template '$RowKey'" -Sev 'Info' $Body = @{ Results = "Deleted template $RowKey" } } else { + Write-LogMessage -headers $Headers -API $APIName -message "GDAP role template '$RowKey' not found for deletion" -Sev 'Warning' $Body = @{ Results = "Template $RowKey not found" } } } default { + Write-LogMessage -headers $Headers -API $APIName -message "Retrieved $($Templates.Count) GDAP role templates" -Sev 'Info' $Results = foreach ($Template in $Templates) { [PSCustomObject]@{ TemplateId = $Template.RowKey diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-CIPPStandardsRun.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-CIPPStandardsRun.ps1 index a42da2a5d425..04a61795e46b 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-CIPPStandardsRun.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-CIPPStandardsRun.ps1 @@ -15,38 +15,71 @@ function Invoke-CIPPStandardsRun { [Parameter(Mandatory = $false)] $TemplateID, [Parameter(Mandatory = $false)] - $runManually = $false - + $runManually = $false, + [Parameter(Mandatory = $false)] + [switch]$Drift ) - Write-Host "Starting process for standards - $($tenantFilter). TemplateID: $($TemplateID) RunManually: $($runManually) Force: $($Force.IsPresent)" + Write-Information "Starting process for standards - $($tenantFilter). TemplateID: $($TemplateID) RunManually: $($runManually) Force: $($Force.IsPresent) Drift: $($Drift.IsPresent)" - $AllTasks = Get-CIPPStandards + if ($Drift.IsPresent) { + Write-Information 'Drift Standards Run' + $AllTasks = Get-CIPPTenantAlignment | Where-Object -Property standardtype -EQ 'drift' | Select-Object -Property TenantFilter | Sort-Object -Unique -Property TenantFilter - if ($Force.IsPresent) { - Write-Host 'Clearing Rerun Cache' - Test-CIPPRerun -ClearAll -TenantFilter $TenantFilter -Type 'Standard' - } + #For each item in our object, run the queue. + $Queue = New-CippQueueEntry -Name 'Drift Standards' -TotalTasks ($AllTasks | Measure-Object).Count - #For each item in our object, run the queue. - $Queue = New-CippQueueEntry -Name "Applying Standards ($TenantFilter)" -TotalTasks ($AllTasks | Measure-Object).Count - - $InputObject = [PSCustomObject]@{ - OrchestratorName = 'StandardsOrchestrator' - QueueFunction = @{ - FunctionName = 'GetStandards' - QueueId = $Queue.RowKey - StandardParams = @{ - TenantFilter = $TenantFilter - runManually = $runManually + $Batch = foreach ($Task in $AllTasks) { + [PSCustomObject]@{ + FunctionName = 'CIPPDriftManagement' + Tenant = $Task.TenantFilter } } - SkipLog = $true - } - if ($TemplateID) { - $InputObject.QueueFunction.StandardParams['TemplateId'] = $TemplateID + + $InputObject = [PSCustomObject]@{ + OrchestratorName = 'DriftStandardsOrchestrator' + Batch = @($Batch) + SkipLog = $true + } + + $InstanceId = Start-NewOrchestration -FunctionName 'CIPPOrchestrator' -InputObject ($InputObject | ConvertTo-Json -Depth 5 -Compress) + Write-Information "Started orchestration with ID = '$InstanceId' for drift standards run" + #$Orchestrator = New-OrchestrationCheckStatusResponse -Request $Request -InstanceId $InstanceId + return + } else { + Write-Information 'Classic Standards Run' + $AllTasks = Get-CIPPStandards + + if ($Force.IsPresent) { + Write-Information 'Clearing Rerun Cache' + Test-CIPPRerun -ClearAll -TenantFilter $TenantFilter -Type 'Standard' + } + + if ($AllTasks.Count -eq 0) { + Write-Information "No standards found for tenant $($TenantFilter)." + return + } + + #For each item in our object, run the queue. + $Queue = New-CippQueueEntry -Name "Applying Standards ($TenantFilter)" -TotalTasks ($AllTasks | Measure-Object).Count + + $InputObject = [PSCustomObject]@{ + OrchestratorName = 'StandardsOrchestrator' + QueueFunction = @{ + FunctionName = 'GetStandards' + QueueId = $Queue.RowKey + StandardParams = @{ + TenantFilter = $TenantFilter + runManually = $runManually + } + } + SkipLog = $true + } + if ($TemplateID) { + $InputObject.QueueFunction.StandardParams['TemplateId'] = $TemplateID + } + Write-Information "InputObject: $($InputObject | ConvertTo-Json -Depth 5 -Compress)" + $InstanceId = Start-NewOrchestration -FunctionName 'CIPPOrchestrator' -InputObject ($InputObject | ConvertTo-Json -Depth 5 -Compress) + Write-Information "Started orchestration with ID = '$InstanceId'" + #$Orchestrator = New-OrchestrationCheckStatusResponse -Request $Request -InstanceId $InstanceId } - Write-Host "InputObject: $($InputObject | ConvertTo-Json -Depth 5 -Compress)" - $InstanceId = Start-NewOrchestration -FunctionName 'CIPPOrchestrator' -InputObject ($InputObject | ConvertTo-Json -Depth 5 -Compress) - Write-Host "Started orchestration with ID = '$InstanceId'" - #$Orchestrator = New-OrchestrationCheckStatusResponse -Request $Request -InstanceId $InstanceId } diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ExecStandardsRun.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ExecStandardsRun.ps1 index f2440e006895..865686e419ca 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ExecStandardsRun.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ExecStandardsRun.ps1 @@ -1,6 +1,6 @@ using namespace System.Net -Function Invoke-ExecStandardsRun { +function Invoke-ExecStandardsRun { <# .FUNCTIONALITY Entrypoint @@ -38,7 +38,7 @@ Function Invoke-ExecStandardsRun { $ProcessorFunction = [PSCustomObject]@{ PartitionKey = 'Function' - RowKey = "Invoke-CIPPStandardsRun-$TenantFilter" + RowKey = "Invoke-CIPPStandardsRun-$TenantFilter-$TemplateId" FunctionName = 'Invoke-CIPPStandardsRun' Parameters = [string](ConvertTo-Json -Compress -InputObject @{ TenantFilter = $TenantFilter diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ExecUpdateDriftDeviation.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ExecUpdateDriftDeviation.ps1 new file mode 100644 index 000000000000..82b45b6bb37c --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ExecUpdateDriftDeviation.ps1 @@ -0,0 +1,115 @@ +using namespace System.Net + +function Invoke-ExecUpdateDriftDeviation { + <# + .FUNCTIONALITY + Entrypoint + .ROLE + Tenant.Standards.ReadWrite + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $TriggerMetadata.FunctionName + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message 'Accessed this API' -Sev 'Debug' + + try { + $TenantFilter = $Request.Body.TenantFilter + + if ($Request.Body.RemoveDriftCustomization) { + $Table = Get-CippTable -tablename 'tenantDrift' + $Filter = "PartitionKey eq '$TenantFilter'" + $ExistingDeviations = Get-CIPPAzDataTableEntity @Table -Filter $Filter + foreach ($Deviation in $ExistingDeviations) { + Remove-AzDataTableEntity @Table -Entity $Deviation + } + $Results = @([PSCustomObject]@{ + success = $true + result = "All drift customizations removed for tenant $TenantFilter" + }) + Write-LogMessage -tenant $TenantFilter -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Removed all drift customizations for tenant $TenantFilter" -Sev 'Info' + } else { + $Deviations = $Request.Body.deviations + $Reason = $Request.Body.reason + $Results = foreach ($Deviation in $Deviations) { + try { + $user = $request.headers.'x-ms-client-principal' + $username = ([System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($user)) | ConvertFrom-Json).userDetails + $Result = Set-CIPPDriftDeviation -TenantFilter $TenantFilter -StandardName $Deviation.standardName -Status $Deviation.status -Reason $Reason -user $username + [PSCustomObject]@{ + success = $true + result = $Result + } + Write-LogMessage -tenant $TenantFilter -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Updated drift deviation status for $($Deviation.standardName) to $($Deviation.status) with reason: $Reason" -Sev 'Info' + if ($Deviation.status -eq 'DeniedRemediate') { + $Setting = $Deviation.standardName -replace 'standards.', '' + $StandardTemplate = Get-CIPPTenantAlignment -TenantFilter $TenantFilter | Where-Object -Property standardType -EQ 'drift' + $StandardTemplate = $StandardTemplate.standardSettings.$Setting + + $StandardTemplate.standards.$Setting | Add-Member -MemberType NoteProperty -Name 'remediate' -Value $true -Force + $StandardTemplate.standards.$Setting | Add-Member -MemberType NoteProperty -Name 'report' -Value $true -Force + + $TaskBody = @{ + TenantFilter = $TenantFilter + Name = "One Off Drift Remediation: $Setting - $TenantFilter" + Command = @{ + value = "Invoke-CIPPStandard$Setting" + label = "Invoke-CIPPStandard$Setting" + } + + Parameters = [pscustomobject]@{ + Tenant = $TenantFilter + Settings = $StandardTemplate.standards.$Setting + } + ScheduledTime = '0' + PostExecution = @{ + Webhook = $false + Email = $false + PSA = $false + } + } + Add-CIPPScheduledTask -Task $TaskBody -hidden $false + Write-LogMessage -tenant $TenantFilter -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Scheduled drift remediation task for $Setting" -Sev 'Info' + } + if ($Deviation.status -eq 'deniedDelete') { + $Policy = $Deviation.receivedValue | ConvertFrom-Json -ErrorAction SilentlyContinue + Write-Host "Policy is $($Policy)" + $URLName = Get-CIPPURLName -Template $Policy + if ($Policy -and $URLName) { + Write-Host "Going to delete Policy with ID $($policy.ID) Deviation Name is $($Deviation.standardName)" + $null = New-GraphPostRequest -uri "https://graph.microsoft.com/beta/$($URLName)/$($policy.id)" -type DELETE -tenant $TenantFilter + "Deleted Policy $($ID)" + Write-LogMessage -tenant $TenantFilter -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Deleted Policy with ID $($ID)" -Sev 'Info' + } else { + "could not find policy with ID $($ID)" + Write-LogMessage -tenant $TenantFilter -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Could not find Policy with ID $($ID) to delete for remediation" -Sev 'Warning' + } + + + } + } catch { + [PSCustomObject]@{ + standardName = $Deviation.standardName + success = $false + error = $_.Exception.Message + } + Write-LogMessage -tenant $TenantFilter -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Failed to update drift deviation for $($Deviation.standardName): $($_.Exception.Message)" -Sev 'Error' + } + } + } + + $Body = @{ Results = @($Results) } + + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = $Body + }) + + } catch { + Write-LogMessage -user $request.headers.'x-ms-client-principal' -API $APINAME -message "Failed to update drift deviation: $($_.Exception.Message)" -Sev 'Error' + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::BadRequest + Body = @{error = $_.Exception.Message } + }) + } +} diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ListTenantAlignment.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ListTenantAlignment.ps1 index 4f4a48dd6766..93859625f996 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ListTenantAlignment.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ListTenantAlignment.ps1 @@ -22,6 +22,7 @@ function Invoke-ListTenantAlignment { [PSCustomObject]@{ tenantFilter = $_.TenantFilter standardName = $_.StandardName + standardType = $_.StandardType ? $_.StandardType : 'Classic Standard' standardId = $_.StandardId alignmentScore = $_.AlignmentScore LicenseMissingPercentage = $_.LicenseMissingPercentage diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ListTenantDrift.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ListTenantDrift.ps1 new file mode 100644 index 000000000000..8daf23f5861b --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ListTenantDrift.ps1 @@ -0,0 +1,36 @@ +using namespace System.Net + +function Invoke-ListTenantDrift { + <# + .FUNCTIONALITY + Entrypoint + .ROLE + Tenant.Standards.Read + #> + [CmdletBinding()] + param($Request, $TriggerMetadata) + + $APIName = $Request.Params.CIPPEndpoint + + try { + # Use the new Get-CIPPTenantAlignment function to get alignment data + if ($Request.Query.TenantFilter) { + $TenantFilter = $Request.Query.TenantFilter + $Results = Get-CIPPDrift -TenantFilter $TenantFilter + } else { + $Tenants = Get-Tenants + $Results = $Tenants | ForEach-Object { Get-CIPPDrift -AllTenants -TenantFilter $_.defaultDomainName } + } + + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::OK + Body = @($Results) + }) + } catch { + Write-LogMessage -API $APIName -message "Failed to get tenant alignment data: $($_.Exception.Message)" -sev Error + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::InternalServerError + Body = @{ error = "Failed to get tenant alignment data: $($_.Exception.Message)" } + }) + } +} diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-listStandardTemplates.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-listStandardTemplates.ps1 index 258ce05cc801..b22164cd87a4 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-listStandardTemplates.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-listStandardTemplates.ps1 @@ -15,7 +15,6 @@ function Invoke-listStandardTemplates { Write-LogMessage -Headers $Headers -API $APIName -message 'Accessed this API' -Sev 'Debug' # Interact with query parameters or the body of the request. $ID = $Request.Query.id - $Table = Get-CippTable -tablename 'templates' $Filter = "PartitionKey eq 'StandardsTemplateV2'" $Templates = (Get-CIPPAzDataTableEntity @Table -Filter $Filter) | ForEach-Object { diff --git a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Tools/Invoke-ExecGraphExplorerPreset.ps1 b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Tools/Invoke-ExecGraphExplorerPreset.ps1 index 9828d33ba5ef..349dc67f91d2 100644 --- a/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Tools/Invoke-ExecGraphExplorerPreset.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Tools/Invoke-ExecGraphExplorerPreset.ps1 @@ -41,31 +41,31 @@ function Invoke-ExecGraphExplorerPreset { $params.'$select' = ($params.'$select').value -join ',' } - if (!$Request.Body.preset.name) { + if (!$Request.Body.preset.name -and $Action -ne 'Delete') { $Message = 'Error: Preset name is required' $StatusCode = [HttpStatusCode]::BadRequest Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ StatusCode = $StatusCode Body = @{ - Results = @{ + Results = @(@{ resultText = $Message state = 'error' - } + }) } }) return } - if (!$Request.Body.preset.endpoint) { + if (!$Request.Body.preset.endpoint -and $Action -ne 'Delete') { $Message = 'Error: Preset endpoint is required' $StatusCode = [HttpStatusCode]::BadRequest Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ StatusCode = $StatusCode Body = @{ - Results = @{ + Results = @(@{ resultText = $Message state = 'error' - } + }) } }) return @@ -114,10 +114,10 @@ function Invoke-ExecGraphExplorerPreset { Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ StatusCode = $StatusCode Body = @{ - Results = @{ + Results = @(@{ resultText = $Message state = if ($Success) { 'success' } else { 'error' } - } + }) } }) } diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecBreachSearch.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecBreachSearch.ps1 index f012ad7ec12e..2fb1e75e4337 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecBreachSearch.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ExecBreachSearch.ps1 @@ -1,6 +1,6 @@ using namespace System.Net -Function Invoke-ExecBreachSearch { +function Invoke-ExecBreachSearch { <# .FUNCTIONALITY Entrypoint @@ -15,13 +15,13 @@ Function Invoke-ExecBreachSearch { Write-LogMessage -headers $Headers -API $APIName -message 'Accessed this API' -Sev 'Debug' # Interact with query parameters or the body of the request. - $TenantFilter = $Request.query.tenantFilter + $TenantFilter = $Request.body.tenantFilter #Move to background job New-BreachTenantSearch -TenantFilter $TenantFilter Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ StatusCode = [HttpStatusCode]::OK - Body = @{ Results = "Executing Search for $TenantFilter" } + Body = @{ Results = "Executing Search for $TenantFilter. This may take up to 24 hours to complete." } }) } diff --git a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListLogs.ps1 b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListLogs.ps1 index 6e360b25a6a0..c5b900c50a10 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Invoke-ListLogs.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Invoke-ListLogs.ps1 @@ -23,11 +23,48 @@ function Invoke-ListLogs { label = $_.PartitionKey } } + } elseif ($Request.Query.logentryid) { + # Return single log entry by RowKey + $Filter = "RowKey eq '{0}'" -f $Request.Query.logentryid + $AllowedTenants = Test-CIPPAccess -Request $Request -TenantList + Write-Host "Getting single log entry for RowKey: $($Request.Query.logentryid)" + + $Row = Get-AzDataTableEntity @Table -Filter $Filter + + if ($Row) { + if ($AllowedTenants -notcontains 'AllTenants') { + $TenantList = Get-Tenants -IncludeErrors | Where-Object { $_.customerId -in $AllowedTenants } + } + + if ($AllowedTenants -contains 'AllTenants' -or ($AllowedTenants -notcontains 'AllTenants' -and ($TenantList.defaultDomainName -contains $Row.Tenant -or $Row.Tenant -eq 'CIPP' -or $TenantList.customerId -contains $Row.TenantId)) ) { + $LogData = if ($Row.LogData -and (Test-Json -Json $Row.LogData -ErrorAction SilentlyContinue)) { + $Row.LogData | ConvertFrom-Json + } else { $Row.LogData } + [PSCustomObject]@{ + DateTime = $Row.Timestamp + Tenant = $Row.Tenant + API = $Row.API + Message = $Row.Message + User = $Row.Username + Severity = $Row.Severity + LogData = $LogData + TenantID = if ($Row.TenantID -ne $null) { + $Row.TenantID + } else { + 'None' + } + AppId = $Row.AppId + IP = $Row.IP + RowKey = $Row.RowKey + } + } + } } else { if ($request.Query.Filter -eq 'True') { $LogLevel = if ($Request.Query.Severity) { ($Request.query.Severity).split(',') } else { 'Info', 'Warn', 'Error', 'Critical', 'Alert' } $PartitionKey = $Request.Query.DateFilter $username = $Request.Query.User ?? '*' + $TenantFilter = $Request.Query.Tenant $StartDate = $Request.Query.StartDate ?? $Request.Query.DateFilter $EndDate = $Request.Query.EndDate ?? $Request.Query.DateFilter @@ -48,12 +85,17 @@ function Invoke-ListLogs { $LogLevel = 'Info', 'Warn', 'Error', 'Critical', 'Alert' $PartitionKey = Get-Date -UFormat '%Y%m%d' $username = '*' + $TenantFilter = $null $Filter = "PartitionKey eq '{0}'" -f $PartitionKey } $AllowedTenants = Test-CIPPAccess -Request $Request -TenantList Write-Host "Getting logs for filter: $Filter, LogLevel: $LogLevel, Username: $username" - $Rows = Get-AzDataTableEntity @Table -Filter $Filter | Where-Object { $_.Severity -in $LogLevel -and $_.Username -like $username } + $Rows = Get-AzDataTableEntity @Table -Filter $Filter | Where-Object { + $_.Severity -in $LogLevel -and + $_.Username -like $username -and + ($TenantFilter -eq $null -or $TenantFilter -eq 'AllTenants' -or $_.Tenant -like "*$TenantFilter*" -or $_.TenantID -eq $TenantFilter) + } if ($AllowedTenants -notcontains 'AllTenants') { $TenantList = Get-Tenants -IncludeErrors | Where-Object { $_.customerId -in $AllowedTenants } @@ -80,6 +122,7 @@ function Invoke-ListLogs { } AppId = $Row.AppId IP = $Row.IP + RowKey = $Row.RowKey } } } diff --git a/Modules/CIPPCore/Public/Entrypoints/Orchestrator Functions/Start-AuditLogOrchestrator.ps1 b/Modules/CIPPCore/Public/Entrypoints/Orchestrator Functions/Start-AuditLogOrchestrator.ps1 index e7010fd8fa0a..944592099a5e 100644 --- a/Modules/CIPPCore/Public/Entrypoints/Orchestrator Functions/Start-AuditLogOrchestrator.ps1 +++ b/Modules/CIPPCore/Public/Entrypoints/Orchestrator Functions/Start-AuditLogOrchestrator.ps1 @@ -16,7 +16,7 @@ function Start-AuditLogOrchestrator { if (($AuditLogSearches | Measure-Object).Count -eq 0) { Write-Information 'No audit log searches available' - } elseif (($WebhookRules | Measure-Object).Count -eq 0) { + } elseif (($AuditLogSearches | Measure-Object).Count -eq 0 -and ($WebhookRules | Measure-Object).Count -eq 0) { Write-Information 'No webhook rules defined' } else { Write-Information "Audit Logs: Downloading $($AuditLogSearches.Count) searches" diff --git a/Modules/CIPPCore/Public/Entrypoints/Orchestrator Functions/Start-DriftStandardsOrchestrator.ps1 b/Modules/CIPPCore/Public/Entrypoints/Orchestrator Functions/Start-DriftStandardsOrchestrator.ps1 new file mode 100644 index 000000000000..7aad617a0c71 --- /dev/null +++ b/Modules/CIPPCore/Public/Entrypoints/Orchestrator Functions/Start-DriftStandardsOrchestrator.ps1 @@ -0,0 +1,13 @@ +function Start-DriftStandardsOrchestrator { + <# + .SYNOPSIS + Start the Drift Standards Orchestrator + #> + [CmdletBinding(SupportsShouldProcess = $true)] + param() + + if ($PSCmdlet.ShouldProcess('Start-DriftStandardsOrchestrator', 'Starting Drift Standards Orchestrator')) { + Write-LogMessage -API 'Standards' -message 'Starting Drift Standards Schedule' -sev Info + Invoke-CIPPStandardsRun -Drift + } +} diff --git a/Modules/CIPPCore/Public/Functions/Get-CIPPTenantAlignment.ps1 b/Modules/CIPPCore/Public/Functions/Get-CIPPTenantAlignment.ps1 index 5671b2a79632..5fdabf756b71 100644 --- a/Modules/CIPPCore/Public/Functions/Get-CIPPTenantAlignment.ps1 +++ b/Modules/CIPPCore/Public/Functions/Get-CIPPTenantAlignment.ps1 @@ -52,7 +52,7 @@ function Get-CIPPTenantAlignment { # Get standards comparison data $StandardsTable = Get-CIPPTable -TableName 'CippStandardsReports' - $AllStandards = Get-CIPPAzDataTableEntity @StandardsTable + $AllStandards = Get-CIPPAzDataTableEntity @StandardsTable -Filter "PartitionKey ne 'StandardReport'" # Filter by tenant if specified $Standards = if ($TenantFilter) { @@ -72,7 +72,7 @@ function Get-CIPPTenantAlignment { if ($FieldValue -is [System.Boolean]) { $FieldValue = [bool]$FieldValue } elseif ($FieldValue -like '*{*') { - $FieldValue = ConvertFrom-Json -InputObject $FieldValue -ErrorAction SilentlyContinue + $FieldValue = ConvertFrom-Json -Depth 100 -InputObject $FieldValue -ErrorAction SilentlyContinue } else { $FieldValue = [string]$FieldValue } @@ -101,7 +101,13 @@ function Get-CIPPTenantAlignment { if ($Template.tenantFilter -and $Template.tenantFilter.Count -gt 0) { # Extract tenant values from the tenantFilter array - $TenantValues = $Template.tenantFilter | ForEach-Object { $_.value } + $TenantValues = $Template.tenantFilter | ForEach-Object { + if ($_.type -eq 'group') { + (Get-TenantGroups -GroupId $_.value).members.defaultDomainName + } else { + $_.value + } + } if ($TenantValues -contains 'AllTenants') { $AppliestoAllTenants = $true @@ -144,6 +150,21 @@ function Get-CIPPTenantAlignment { } } } + } + # Handle Conditional Access templates specially + elseif ($StandardKey -eq 'ConditionalAccessTemplate' -and $StandardConfig -is [array]) { + foreach ($CATemplate in $StandardConfig) { + if ($CATemplate.TemplateList.value) { + $CAStandardId = "standards.ConditionalAccessTemplate.$($CATemplate.TemplateList.value)" + $CAActions = if ($CATemplate.action) { $CATemplate.action } else { @() } + $CAReportingEnabled = ($CAActions | Where-Object { $_.value -and ($_.value.ToLower() -eq 'report' -or $_.value.ToLower() -eq 'remediate') }).Count -gt 0 + + [PSCustomObject]@{ + StandardId = $CAStandardId + ReportingEnabled = $CAReportingEnabled + } + } + } } else { [PSCustomObject]@{ StandardId = $StandardId @@ -236,6 +257,10 @@ function Get-CIPPTenantAlignment { TenantFilter = $TenantName StandardName = $Template.templateName StandardId = $Template.GUID + standardType = $Template.type + standardSettings = $Template.Standards + driftAlertEmail = $Template.driftAlertEmail + driftAlertWebhook = $Template.driftAlertWebhook AlignmentScore = $AlignmentPercentage LicenseMissingPercentage = $LicenseMissingPercentage CombinedScore = $AlignmentPercentage + $LicenseMissingPercentage @@ -255,6 +280,7 @@ function Get-CIPPTenantAlignment { return $Results } catch { Write-Error "Error getting tenant alignment data: $($_.Exception.Message)" + Write-Information $_.InvocationInfo.PositionMessage throw } } diff --git a/Modules/CIPPCore/Public/Functions/Get-CIPPURLName.ps1 b/Modules/CIPPCore/Public/Functions/Get-CIPPURLName.ps1 new file mode 100644 index 000000000000..c9136b55435e --- /dev/null +++ b/Modules/CIPPCore/Public/Functions/Get-CIPPURLName.ps1 @@ -0,0 +1,155 @@ +function Get-CIPPURLName { + <# + .SYNOPSIS + Gets the correct Microsoft Graph URL based on the OData type of a template + .DESCRIPTION + This function examines the @odata.type property of a JSON template object and returns + the appropriate full Microsoft Graph API URL for that resource type. + .PARAMETER Template + The template object containing the @odata.type property to analyze + .FUNCTIONALITY + Internal + .EXAMPLE + Get-CIPPURLName -Template $MyTemplate + .EXAMPLE + $Template | Get-CIPPURLName + #> + [CmdletBinding()] + param( + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] + [PSCustomObject]$Template + ) + + # Extract the OData type from the template + $ODataType = $Template.'@odata.type' + if ($Template.urlName) { return $Template.urlName } + + if (-not $ODataType) { + Write-Warning 'No @odata.type property found in template' + return $null + } + + # Determine the full Microsoft Graph URL based on the OData type + $URLName = switch -wildcard ($ODataType) { + # Device Compliance Policies + '*CompliancePolicy' { + 'deviceManagement/deviceCompliancePolicies' + } + '*deviceCompliancePolicy' { + 'deviceManagement/deviceCompliancePolicies' + } + + # Managed App Policies (App Protection) + '*ManagedAppProtection' { + 'deviceAppManagement/managedAppPolicies' + } + '*managedAppPolicies' { + 'deviceAppManagement/managedAppPolicies' + } + '*managedAppPolicy' { + 'deviceAppManagement/managedAppPolicies' + } + '*appProtectionPolicy' { + 'deviceAppManagement/managedAppPolicies' + } + + # Configuration Policies (Settings Catalog) + '*configurationPolicies' { + 'deviceManagement/configurationPolicies' + } + '*deviceManagementConfigurationPolicy' { + 'deviceManagement/configurationPolicies' + } + + # Windows Driver Update Profiles + '*windowsDriverUpdateProfiles' { + 'deviceManagement/windowsDriverUpdateProfiles' + } + '*windowsDriverUpdateProfile' { + 'deviceManagement/windowsDriverUpdateProfiles' + } + + # Device Configurations + '*deviceConfigurations' { + 'deviceManagement/deviceConfigurations' + } + '*deviceConfiguration' { + 'deviceManagement/deviceConfigurations' + } + + # Group Policy Configurations (Administrative Templates) + '*groupPolicyConfigurations' { + 'deviceManagement/groupPolicyConfigurations' + } + '*groupPolicyConfiguration' { + 'deviceManagement/groupPolicyConfigurations' + } + + # Conditional Access Policies + '*conditionalAccessPolicy' { + 'identity/conditionalAccess/policies' + } + + # Device Enrollment Configurations + '*deviceEnrollmentConfiguration' { + 'deviceManagement/deviceEnrollmentConfigurations' + } + '*enrollmentConfiguration' { + 'deviceManagement/deviceEnrollmentConfigurations' + } + + # Mobile App Configurations + '*mobileAppConfiguration' { + 'deviceAppManagement/mobileAppConfigurations' + } + '*appConfiguration' { + 'deviceAppManagement/mobileAppConfigurations' + } + + # Windows Feature Update Profiles + '*windowsFeatureUpdateProfile' { + 'deviceManagement/windowsFeatureUpdateProfiles' + } + + # Device Health Scripts (Remediation Scripts) + '*deviceHealthScript' { + 'deviceManagement/deviceHealthScripts' + } + + # Device Management Scripts (PowerShell Scripts) + '*deviceManagementScript' { + 'deviceManagement/deviceManagementScripts' + } + + # Mobile Applications + '*mobileApp' { + 'deviceAppManagement/mobileApps' + } + '*winGetApp' { + 'deviceAppManagement/mobileApps' + } + '*officeSuiteApp' { + 'deviceAppManagement/mobileApps' + } + + # Named Locations + '*namedLocation' { + 'identity/conditionalAccess/namedLocations' + } + '*ipNamedLocation' { + 'identity/conditionalAccess/namedLocations' + } + '*countryNamedLocation' { + 'identity/conditionalAccess/namedLocations' + } + + # Default fallback + default { + Write-Warning "Unknown OData type: $ODataType" + $null + } + } + + return $URLName +} + diff --git a/Modules/CIPPCore/Public/Get-CIPPDrift.ps1 b/Modules/CIPPCore/Public/Get-CIPPDrift.ps1 new file mode 100644 index 000000000000..f08cd5e573ff --- /dev/null +++ b/Modules/CIPPCore/Public/Get-CIPPDrift.ps1 @@ -0,0 +1,330 @@ +function Get-CIPPDrift { + <# + .SYNOPSIS + Gets comprehensive drift information for a tenant including standards compliance and policy deviations + .DESCRIPTION + This function collects drift information by executing Get-CIPPTenantAlignment and comparing + tenant policies against standards templates. It identifies both standards deviations and + extra policies not defined in templates. + .PARAMETER TenantFilter + The tenant to get drift data for + .PARAMETER TemplateId + Optional specific template GUID to check drift for. If not specified, processes all templates. + .FUNCTIONALITY + Internal + .EXAMPLE + Get-CIPPDrift -TenantFilter "contoso.onmicrosoft.com" + .EXAMPLE + Get-CIPPDrift -TenantFilter "contoso.onmicrosoft.com" -TemplateId "12345-67890-abcdef" + #> + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + [string]$TenantFilter, + + [Parameter(Mandatory = $false)] + [string]$TemplateId, + + [Parameter(Mandatory = $false)] + [switch]$AllTenants + ) + + try { + $AlignmentData = Get-CIPPTenantAlignment -TenantFilter $TenantFilter -TemplateId $TemplateId | Where-Object -Property standardType -EQ 'drift' + if (-not $AlignmentData) { + Write-Warning "No alignment data found for tenant $TenantFilter" + return @() + } + + # Get existing drift states from the tenantDrift table + $DriftTable = Get-CippTable -tablename 'tenantDrift' + $DriftFilter = "PartitionKey eq '$TenantFilter'" + $ExistingDriftStates = @{} + try { + $DriftEntities = Get-CIPPAzDataTableEntity @DriftTable -Filter $DriftFilter + foreach ($Entity in $DriftEntities) { + $ExistingDriftStates[$Entity.StandardName] = $Entity + } + } catch { + Write-Warning "Failed to get existing drift states: $($_.Exception.Message)" + } + + $Results = [System.Collections.Generic.List[object]]::new() + foreach ($Alignment in $AlignmentData) { + # Initialize deviation collections + $StandardsDeviations = [System.Collections.Generic.List[object]]::new() + $PolicyDeviations = [System.Collections.Generic.List[object]]::new() + + # Process standards compliance deviations + if ($Alignment.ComparisonDetails) { + foreach ($ComparisonItem in $Alignment.ComparisonDetails) { + if ($ComparisonItem.Compliant -ne $true) { + $Status = if ($ExistingDriftStates.ContainsKey($ComparisonItem.StandardName)) { + $ExistingDriftStates[$ComparisonItem.StandardName].Status + } else { + 'New' + } + $reason = if ($ExistingDriftStates.ContainsKey($ComparisonItem.StandardName)) { $ExistingDriftStates[$ComparisonItem.StandardName].Reason } + $User = if ($ExistingDriftStates.ContainsKey($ComparisonItem.StandardName)) { $ExistingDriftStates[$ComparisonItem.StandardName].User } + $StandardsDeviations.Add([PSCustomObject]@{ + standardName = $ComparisonItem.StandardName + expectedValue = 'Compliant' + receivedValue = $ComparisonItem.StandardValue + state = 'current' + Status = $Status + Reason = $reason + lastChangedByUser = $User + }) + } + } + } + + # Perform full policy collection + + # Always get live data when not in AllTenants mode + $IntuneRequests = @( + @{ + id = 'deviceAppManagement/managedAppPolicies' + url = 'deviceAppManagement/managedAppPolicies' + method = 'GET' + } + @{ + id = 'deviceManagement/deviceCompliancePolicies' + url = 'deviceManagement/deviceCompliancePolicies' + method = 'GET' + } + @{ + id = 'deviceManagement/groupPolicyConfigurations' + url = 'deviceManagement/groupPolicyConfigurations' + method = 'GET' + } + @{ + id = 'deviceManagement/deviceConfigurations' + url = 'deviceManagement/deviceConfigurations' + method = 'GET' + } + @{ + id = 'deviceManagement/configurationPolicies' + url = 'deviceManagement/configurationPolicies' + method = 'GET' + } + @{ + id = 'deviceManagement/windowsDriverUpdateProfiles' + url = 'deviceManagement/windowsDriverUpdateProfiles' + method = 'GET' + } + @{ + id = 'deviceManagement/windowsFeatureUpdateProfiles' + url = 'deviceManagement/windowsFeatureUpdateProfiles' + method = 'GET' + } + @{ + id = 'deviceManagement/windowsQualityUpdatePolicies' + url = 'deviceManagement/windowsQualityUpdatePolicies' + method = 'GET' + } + @{ + id = 'deviceManagement/windowsQualityUpdateProfiles' + url = 'deviceManagement/windowsQualityUpdateProfiles' + method = 'GET' + } + ) + + $TenantIntunePolicies = [System.Collections.Generic.List[object]]::new() + + try { + $IntuneGraphRequest = New-GraphBulkRequest -Requests $IntuneRequests -tenantid $TenantFilter -asapp $true + + foreach ($Request in $IntuneGraphRequest) { + if ($Request.body.value) { + foreach ($Policy in $Request.body.value) { + $TenantIntunePolicies.Add([PSCustomObject]@{ + Type = $Request.id + Policy = $Policy + }) + } + } + } + } catch { + Write-Warning "Failed to get Intune policies: $($_.Exception.Message)" + } + + # Get Conditional Access policies + try { + $CARequests = @( + @{ + id = 'policies' + url = 'identity/conditionalAccess/policies' + method = 'GET' + } + ) + $CAGraphRequest = New-GraphBulkRequest -Requests $CARequests -tenantid $TenantFilter -asapp $true + $TenantCAPolicies = ($CAGraphRequest | Where-Object { $_.id -eq 'policies' }).body.value + } catch { + Write-Warning "Failed to get Conditional Access policies: $($_.Exception.Message)" + $TenantCAPolicies = @() + } + + if ($Alignment.standardSettings) { + if ($Alignment.standardSettings.IntuneTemplate) { + $IntuneTemplateIds = $Alignment.standardSettings.IntuneTemplate.TemplateList | ForEach-Object { $_.value } + } + if ($Alignment.standardSettings.ConditionalAccessTemplate) { + $CATemplateIds = $Alignment.standardSettings.ConditionalAccessTemplate.TemplateList | ForEach-Object { $_.value } + } + } + + # Get actual CA templates from templates table + if ($CATemplateIds.Count -gt 0) { + try { + $CATable = Get-CippTable -tablename 'templates' + $CAFilter = "PartitionKey eq 'CATemplate'" + $AllCATemplates = (Get-CIPPAzDataTableEntity @CATable -Filter $CAFilter) | ForEach-Object { + $data = $_.JSON | ConvertFrom-Json -Depth 100 + $data | Add-Member -NotePropertyName 'GUID' -NotePropertyValue $_.GUID -Force + $data + } | Sort-Object -Property displayName + + $TemplateCATemplates = $AllCATemplates | Where-Object { $_.GUID -in $CATemplateIds } + } catch { + Write-Warning "Failed to get CA templates: $($_.Exception.Message)" + } + } + + # Get actual Intune templates from templates table + if ($IntuneTemplateIds.Count -gt 0) { + try { + $IntuneTable = Get-CippTable -tablename 'templates' + $IntuneFilter = "PartitionKey eq 'IntuneTemplate'" + $RawIntuneTemplates = (Get-CIPPAzDataTableEntity @IntuneTable -Filter $IntuneFilter) + $AllIntuneTemplates = $RawIntuneTemplates | ForEach-Object { + try { + $JSONData = $_.JSON | ConvertFrom-Json -Depth 100 -ErrorAction SilentlyContinue + $data = $JSONData.RAWJson | ConvertFrom-Json -Depth 100 -ErrorAction SilentlyContinue + $data | Add-Member -NotePropertyName 'displayName' -NotePropertyValue $JSONData.Displayname -Force + $data | Add-Member -NotePropertyName 'description' -NotePropertyValue $JSONData.Description -Force + $data | Add-Member -NotePropertyName 'Type' -NotePropertyValue $JSONData.Type -Force + $data | Add-Member -NotePropertyName 'GUID' -NotePropertyValue $_.RowKey -Force + $data + } catch { + # Skip invalid templates + } + } | Sort-Object -Property displayName + + $TemplateIntuneTemplates = $AllIntuneTemplates | Where-Object { $_.GUID -in $IntuneTemplateIds } + } catch { + Write-Warning "Failed to get Intune templates: $($_.Exception.Message)" + } + } + + # Check for extra Intune policies not in template + foreach ($TenantPolicy in $TenantIntunePolicies) { + $PolicyFound = $false + $tenantPolicy.policy | Add-Member -MemberType NoteProperty -Name 'URLName' -Value $TenantPolicy.Type -Force + $TenantPolicyName = if ($TenantPolicy.Policy.displayName) { $TenantPolicy.Policy.displayName } else { $TenantPolicy.Policy.name } + + foreach ($TemplatePolicy in $TemplateIntuneTemplates) { + $TemplatePolicyName = if ($TemplatePolicy.displayName) { $TemplatePolicy.displayName } else { $TemplatePolicy.name } + + if ($TemplatePolicy.displayName -eq $TenantPolicy.Policy.displayName -or + $TemplatePolicy.name -eq $TenantPolicy.Policy.name -or + $TemplatePolicy.displayName -eq $TenantPolicy.Policy.name -or + $TemplatePolicy.name -eq $TenantPolicy.Policy.displayName) { + $PolicyFound = $true + break + } + } + + if (-not $PolicyFound) { + $PolicyKey = "IntuneTemplates.$($TenantPolicy.Policy.id)" + $Status = if ($ExistingDriftStates.ContainsKey($PolicyKey)) { + $ExistingDriftStates[$PolicyKey].Status + } else { + 'New' + } + $PolicyDeviation = [PSCustomObject]@{ + standardName = $PolicyKey + standardDisplayName = "Intune - $TenantPolicyName" + expectedValue = 'This policy only exists in the tenant, not in the template.' + receivedValue = ($TenantPolicy.Policy | ConvertTo-Json -Depth 10 -Compress) + state = 'current' + Status = $Status + } + $PolicyDeviations.Add($PolicyDeviation) + } + } + + # Check for extra Conditional Access policies not in template + foreach ($TenantCAPolicy in $TenantCAPolicies) { + $PolicyFound = $false + + foreach ($TemplateCAPolicy in $TemplateCATemplates) { + if ($TemplateCAPolicy.displayName -eq $TenantCAPolicy.displayName) { + $PolicyFound = $true + break + } + } + + if (-not $PolicyFound) { + $PolicyKey = "ConditionalAccessTemplates.$($TenantCAPolicy.id)" + $Status = if ($ExistingDriftStates.ContainsKey($PolicyKey)) { + $ExistingDriftStates[$PolicyKey].Status + } else { + 'New' + } + $PolicyDeviation = [PSCustomObject]@{ + standardName = $PolicyKey + standardDisplayName = "Conditional Access - $($TenantCAPolicy.displayName)" + expectedValue = 'This policy only exists in the tenant, not in the template.' + receivedValue = ($TenantCAPolicy | ConvertTo-Json -Depth 10 -Compress) + state = 'current' + Status = $Status + } + $PolicyDeviations.Add($PolicyDeviation) + } + } + + + # Combine all deviations and filter by status + $AllDeviations = [System.Collections.Generic.List[object]]::new() + $AllDeviations.AddRange($StandardsDeviations) + $AllDeviations.AddRange($PolicyDeviations) + + # Filter deviations by status for counting + $NewDeviations = $AllDeviations | Where-Object { $_.Status -eq 'New' } + $AcceptedDeviations = $AllDeviations | Where-Object { $_.Status -eq 'Accepted' } + $DeniedDeviations = $AllDeviations | Where-Object { $_.Status -like 'Denied*' } + $CustomerSpecificDeviations = $AllDeviations | Where-Object { $_.Status -eq 'CustomerSpecific' } + + # Current deviations are New + Denied (not accepted or customer specific) + $CurrentDeviations = $AllDeviations | Where-Object { $_.Status -in @('New', 'Denied') } + + $Result = [PSCustomObject]@{ + tenantFilter = $TenantFilter + standardName = $Alignment.StandardName + standardId = $Alignment.StandardId + alignmentScore = $Alignment.AlignmentScore + acceptedDeviationsCount = $AcceptedDeviations.Count + currentDeviationsCount = $CurrentDeviations.Count + deniedDeviationsCount = $DeniedDeviations.Count + customerSpecificDeviationsCount = $CustomerSpecificDeviations.Count + newDeviationsCount = $NewDeviations.Count + alignedCount = $Alignment.CompliantStandards + currentDeviations = @($CurrentDeviations) + acceptedDeviations = @($AcceptedDeviations) + customerSpecificDeviations = @($CustomerSpecificDeviations) + deniedDeviations = @($DeniedDeviations) + allDeviations = @($AllDeviations) + latestDataCollection = $Alignment.LatestDataCollection + } + + $Results.Add($Result) + } + + return @($Results) + + } catch { + Write-Error "Error getting drift data: $($_.Exception.Message)" + throw + } +} diff --git a/Modules/CIPPCore/Public/Get-CIPPTenantCapabilities.ps1 b/Modules/CIPPCore/Public/Get-CIPPTenantCapabilities.ps1 index 124624857bf1..196568107230 100644 --- a/Modules/CIPPCore/Public/Get-CIPPTenantCapabilities.ps1 +++ b/Modules/CIPPCore/Public/Get-CIPPTenantCapabilities.ps1 @@ -21,10 +21,10 @@ function Get-CIPPTenantCapabilities { } Add-CIPPAzDataTableEntity @ConfigTable -Entity $Entity -Force } - $Plans = $Org.servicePlans | Where-Object { $_.provisioningStatus -eq 'Success' } | Sort-Object -Property serviceplanName -Unique | Select-Object servicePlanName, provisioningStatus + $Plans = $Org.servicePlans | Where-Object { $_.provisioningStatus -ne 'disabled' } | Sort-Object -Property serviceplanName -Unique | Select-Object servicePlanName, provisioningStatus $Results = @{} foreach ($Plan in $Plans) { - $Results."$($Plan.servicePlanName)" = $Plan.provisioningStatus -eq 'Success' + $Results."$($Plan.servicePlanName)" = $Plan.provisioningStatus -ne 'disabled' } [PSCustomObject]$Results } diff --git a/Modules/CIPPCore/Public/Get-CIPPTextReplacement.ps1 b/Modules/CIPPCore/Public/Get-CIPPTextReplacement.ps1 index 28ad7d8a7343..68e801215b8a 100644 --- a/Modules/CIPPCore/Public/Get-CIPPTextReplacement.ps1 +++ b/Modules/CIPPCore/Public/Get-CIPPTextReplacement.ps1 @@ -26,6 +26,7 @@ function Get-CIPPTextReplacement { '%temp%', '%tenantid%', '%tenantfilter%', + '%initialdomain%', '%tenantname%', '%partnertenantid%', '%samappid%', @@ -37,7 +38,7 @@ function Get-CIPPTextReplacement { '%programfiles(x86)%', '%programdata%' ) - + $Tenant = Get-Tenants -TenantFilter $TenantFilter $CustomerId = $Tenant.customerId @@ -70,6 +71,7 @@ function Get-CIPPTextReplacement { #default replacements for all tenants: %tenantid% becomes $tenant.customerId, %tenantfilter% becomes $tenant.defaultDomainName, %tenantname% becomes $tenant.displayName $Text = $Text -replace '%tenantid%', $Tenant.customerId $Text = $Text -replace '%tenantfilter%', $Tenant.defaultDomainName + $Text = $Text -replace '%initialdomain%', $Tenant.initialDomainName $Text = $Text -replace '%tenantname%', $Tenant.displayName # Partner specific replacements diff --git a/Modules/CIPPCore/Public/GraphHelper/New-ExoBulkRequest.ps1 b/Modules/CIPPCore/Public/GraphHelper/New-ExoBulkRequest.ps1 index 38275011282b..553331e6a328 100644 --- a/Modules/CIPPCore/Public/GraphHelper/New-ExoBulkRequest.ps1 +++ b/Modules/CIPPCore/Public/GraphHelper/New-ExoBulkRequest.ps1 @@ -43,6 +43,7 @@ function New-ExoBulkRequest { # Initialize the ID to Cmdlet Name mapping $IdToCmdletName = @{} + $IdToOperationGuid = @{} # Track operation GUIDs when provided # Split the cmdletArray into batches of 10 $batches = [System.Collections.Generic.List[object]]::new() @@ -69,19 +70,32 @@ function New-ExoBulkRequest { $Headers['Accept'] = 'application/json; odata.metadata=minimal' $Headers['Accept-Encoding'] = 'gzip' - # Generate a unique ID for each request - $RequestId = [Guid]::NewGuid().ToString() + # Use provided OperationGuid if available, otherwise generate one + $RequestId = if ($cmd.OperationGuid) { + $cmd.OperationGuid + } else { + [Guid]::NewGuid().ToString() + } + + # Create clean cmdlet object for API (without OperationGuid) + $CleanCmd = @{ + CmdletInput = $cmd.CmdletInput + } + $BatchRequest = @{ url = $URL method = 'POST' - body = $cmd + body = $CleanCmd headers = $Headers.Clone() id = $RequestId } $BatchBodyObj['requests'] = $BatchBodyObj['requests'] + $BatchRequest - # Map the Request ID to the Cmdlet Name + # Map the Request ID to the Cmdlet Name and Operation GUID (if provided) $IdToCmdletName[$RequestId] = $cmd.CmdletInput.CmdletName + if ($cmd.OperationGuid) { + $IdToOperationGuid[$RequestId] = $cmd.OperationGuid + } } $BatchBodyJson = ConvertTo-Json -InputObject $BatchBodyObj -Depth 10 $BatchBodyJson = Get-CIPPTextReplacement -TenantFilter $tenantid -Text $BatchBodyJson @@ -104,6 +118,7 @@ function New-ExoBulkRequest { foreach ($item in $ReturnedData) { $itemId = $item.id $CmdletName = $IdToCmdletName[$itemId] + $OperationGuid = $IdToOperationGuid[$itemId] # Will be $null if not provided $body = $item.body.PSObject.Copy() if ($body.'@adminapi.warnings') { @@ -115,20 +130,50 @@ function New-ExoBulkRequest { } else { $msg = [pscustomobject]@{ error = $body.error.message; target = $body.error.details.target } } + + # Add OperationGuid to error if it was provided + if ($OperationGuid) { + $msg | Add-Member -MemberType NoteProperty -Name 'OperationGuid' -Value $OperationGuid -Force + } + $body | Add-Member -MemberType NoteProperty -Name 'value' -Value $msg -Force + } else { + # Handle successful operations - add OperationGuid if provided + if ($body.value) { + # Add GUID to existing results if provided + if ($OperationGuid) { + if ($body.value -is [array]) { + foreach ($val in $body.value) { + $val | Add-Member -MemberType NoteProperty -Name 'OperationGuid' -Value $OperationGuid -Force + } + } else { + $body.value | Add-Member -MemberType NoteProperty -Name 'OperationGuid' -Value $OperationGuid -Force + } + } + } else { + # Create success indicators when GUID was provided (caller wants tracking) + if ($OperationGuid) { + $body | Add-Member -MemberType NoteProperty -Name 'value' -Value ([pscustomobject]@{ + Success = $true + OperationGuid = $OperationGuid + }) -Force + } + } } + $resultValues = $body.value foreach ($resultValue in $resultValues) { if (-not $FinalData.ContainsKey($CmdletName)) { $FinalData[$CmdletName] = [System.Collections.Generic.List[object]]::new() - $FinalData.$CmdletName.Add($resultValue) + $FinalData[$CmdletName].Add($resultValue) } else { - $FinalData.$CmdletName.Add($resultValue) + $FinalData[$CmdletName].Add($resultValue) } } } } else { $FinalData = foreach ($item in $ReturnedData) { + $OperationGuid = $IdToOperationGuid[$item.id] # Will be $null if not provided $body = $item.body.PSObject.Copy() if ($body.'@adminapi.warnings') { @@ -140,7 +185,35 @@ function New-ExoBulkRequest { } else { $msg = [pscustomobject]@{ error = $body.error.message; target = $body.error.details.target } } + + # Add OperationGuid to error if it was provided + if ($OperationGuid) { + $msg | Add-Member -MemberType NoteProperty -Name 'OperationGuid' -Value $OperationGuid -Force + } + $body | Add-Member -MemberType NoteProperty -Name 'value' -Value $msg -Force + } else { + # Handle successful operations + if ($body.value) { + # Add GUID to existing results if provided + if ($OperationGuid) { + if ($body.value -is [array]) { + foreach ($val in $body.value) { + $val | Add-Member -MemberType NoteProperty -Name 'OperationGuid' -Value $OperationGuid -Force + } + } else { + $body.value | Add-Member -MemberType NoteProperty -Name 'OperationGuid' -Value $OperationGuid -Force + } + } + } else { + # Create success indicators when GUID was provided (caller wants tracking) + if ($OperationGuid) { + $body | Add-Member -MemberType NoteProperty -Name 'value' -Value ([pscustomobject]@{ + Success = $true + OperationGuid = $OperationGuid + }) -Force + } + } } $body.value } diff --git a/Modules/CIPPCore/Public/GraphHelper/New-ExoRequest.ps1 b/Modules/CIPPCore/Public/GraphHelper/New-ExoRequest.ps1 index 3f42b22ae2e1..2abf72d3f2af 100644 --- a/Modules/CIPPCore/Public/GraphHelper/New-ExoRequest.ps1 +++ b/Modules/CIPPCore/Public/GraphHelper/New-ExoRequest.ps1 @@ -4,7 +4,7 @@ function New-ExoRequest { Internal #> [CmdletBinding(DefaultParameterSetName = 'ExoRequest')] - Param( + param( [Parameter(Mandatory = $true, ParameterSetName = 'ExoRequest')] [string]$cmdlet, @@ -43,7 +43,7 @@ function New-ExoRequest { $token = Get-GraphToken -Tenantid $tenantid -scope "$Resource/.default" -AsApp:$AsApp.IsPresent if ($cmdParams) { - #if cmdparams is a pscustomobject, convert to hashtable, otherwise leave as is + #if cmdParams is a pscustomobject, convert to hashtable, otherwise leave as is $Params = $cmdParams } else { $Params = @{} @@ -145,7 +145,7 @@ function New-ExoRequest { } until ($null -eq $URL) Write-Verbose ($ResponseHeaders | ConvertTo-Json) - if ($ReturnedData.'@adminapi.warnings' -and $ReturnedData.value -eq $null) { + if ($ReturnedData.'@adminapi.warnings' -and $null -eq $ReturnedData.value) { $ReturnedData.value = $ReturnedData.'@adminapi.warnings' } } catch { diff --git a/Modules/CIPPCore/Public/GraphHelper/New-GraphBulkRequest.ps1 b/Modules/CIPPCore/Public/GraphHelper/New-GraphBulkRequest.ps1 index 12b72accf29e..b6c925415458 100644 --- a/Modules/CIPPCore/Public/GraphHelper/New-GraphBulkRequest.ps1 +++ b/Modules/CIPPCore/Public/GraphHelper/New-GraphBulkRequest.ps1 @@ -58,8 +58,20 @@ function New-GraphBulkRequest { } } catch { - $Message = ($_.ErrorDetails.Message | ConvertFrom-Json -ErrorAction SilentlyContinue).error.message - if ($null -eq $Message) { $Message = $($_.Exception.Message) } + # Try to parse ErrorDetails.Message as JSON + if ($_.ErrorDetails.Message) { + try { + $ErrorJson = $_.ErrorDetails.Message | ConvertFrom-Json -ErrorAction Stop + $Message = $ErrorJson.error.message + } catch { + $Message = $_.ErrorDetails.Message + } + } + + if ([string]::IsNullOrEmpty($Message)) { + $Message = $_.Exception.Message + } + if ($Message -ne 'Request not applicable to target tenant.') { $Tenant.LastGraphError = $Message ?? '' $Tenant.GraphErrorCount++ diff --git a/Modules/CIPPCore/Public/GraphHelper/New-GraphGetRequest.ps1 b/Modules/CIPPCore/Public/GraphHelper/New-GraphGetRequest.ps1 index 12bab410eeee..e7a3ad3bdf9b 100644 --- a/Modules/CIPPCore/Public/GraphHelper/New-GraphGetRequest.ps1 +++ b/Modules/CIPPCore/Public/GraphHelper/New-GraphGetRequest.ps1 @@ -16,7 +16,8 @@ function New-GraphGetRequest { [switch]$ComplexFilter, [switch]$CountOnly, [switch]$IncludeResponseHeaders, - [hashtable]$extraHeaders + [hashtable]$extraHeaders, + [switch]$ReturnRawResponse ) if ($NoAuthCheck -eq $false) { @@ -65,8 +66,24 @@ function New-GraphGetRequest { if ($IncludeResponseHeaders) { $GraphRequest.ResponseHeadersVariable = 'ResponseHeaders' } - $Data = (Invoke-RestMethod @GraphRequest) - if ($CountOnly) { + + if ($ReturnRawResponse) { + $GraphRequest.SkipHttpErrorCheck = $true + $Data = Invoke-WebRequest @GraphRequest + } else { + $Data = (Invoke-RestMethod @GraphRequest) + } + + if ($ReturnRawResponse) { + if (Test-Json -Json $Data.Content) { + $Content = $Data.Content | ConvertFrom-Json + } else { + $Content = $Data.Content + } + + $Data | Select-Object -Property StatusCode, StatusDescription, @{Name = 'Content'; Expression = { $Content }} + $nextURL = $null + } elseif ($CountOnly) { $Data.'@odata.count' $NextURL = $null } else { diff --git a/Modules/CIPPCore/Public/New-CIPPAlertTemplate.ps1 b/Modules/CIPPCore/Public/New-CIPPAlertTemplate.ps1 index bf93a9098812..54185b16994f 100644 --- a/Modules/CIPPCore/Public/New-CIPPAlertTemplate.ps1 +++ b/Modules/CIPPCore/Public/New-CIPPAlertTemplate.ps1 @@ -27,6 +27,14 @@ function New-CIPPAlertTemplate { if ($Data -is [array] -and $Data[0] -is [string]) { $Data = $Data | ForEach-Object { @{ message = $_ } } } + if ($InputObject -eq 'driftStandard') { + $Title = "CIPP Alert - Standard Drift Detected for $($Tenant)" + $DataHTML = ($Data | ConvertTo-Html | Out-String).Replace('', '
') + $IntroText = "

You've setup your instance to receive alerts when a tenant is drifting away from your standard. This seems to have happened! We've found the following deviations.

$dataHTML" + $ButtonUrl = "$CIPPURL/tenant/standards/manage-drift?tenantFilter=$($Tenant)&templateId=$($AuditLogLink)" + $ButtonText = 'Investigate and remediate deviations' + $AfterButtonText = 'Click the button above to go to the logbook and investigate the deviations. You can also use the standards page to remediate the deviations.' + } if ($InputObject -eq 'sherwebmig') { $DataHTML = ($Data | ConvertTo-Html | Out-String).Replace('
', '
') diff --git a/Modules/CIPPCore/Public/New-CIPPCAPolicy.ps1 b/Modules/CIPPCore/Public/New-CIPPCAPolicy.ps1 index a6c6ede0714d..e14bf71d6999 100644 --- a/Modules/CIPPCore/Public/New-CIPPCAPolicy.ps1 +++ b/Modules/CIPPCore/Public/New-CIPPCAPolicy.ps1 @@ -44,14 +44,14 @@ function New-CIPPCAPolicy { $GroupIds = [System.Collections.Generic.List[string]]::new() $groupNames | ForEach-Object { if (Test-IsGuid $_) { - Write-LogMessage -Headers $User -API $APINAME -message "Already GUID, no need to replace: $_" -Sev 'Debug' + Write-LogMessage -Headers $User -API 'Create CA Policy' -message "Already GUID, no need to replace: $_" -Sev 'Debug' $GroupIds.Add($_) # it's a GUID, so we keep it } else { $groupId = ($groups | Where-Object -Property displayName -EQ $_).id # it's a display name, so we get the group ID if ($groupId) { foreach ($gid in $groupId) { Write-Warning "Replaced group name $_ with ID $gid" - $null = Write-LogMessage -Headers $User -API $APINAME -message "Replaced group name $_ with ID $gid" -Sev 'Debug' + $null = Write-LogMessage -Headers $User -API 'Create CA Policy' -message "Replaced group name $_ with ID $gid" -Sev 'Debug' $GroupIds.Add($gid) # add the ID to the list } } else { @@ -68,14 +68,14 @@ function New-CIPPCAPolicy { $UserIds = [System.Collections.Generic.List[string]]::new() $userNames | ForEach-Object { if (Test-IsGuid $_) { - Write-LogMessage -Headers $User -API $APINAME -message "Already GUID, no need to replace: $_" -Sev 'Debug' + Write-LogMessage -Headers $User -API 'Create CA Policy' -message "Already GUID, no need to replace: $_" -Sev 'Debug' $UserIds.Add($_) # it's a GUID, so we keep it } else { $userId = ($users | Where-Object -Property displayName -EQ $_).id # it's a display name, so we get the user ID if ($userId) { foreach ($uid in $userId) { Write-Warning "Replaced user name $_ with ID $uid" - $null = Write-LogMessage -Headers $User -API $APINAME -message "Replaced user name $_ with ID $uid" -Sev 'Debug' + $null = Write-LogMessage -Headers $User -API 'Create CA Policy' -message "Replaced user name $_ with ID $uid" -Sev 'Debug' $UserIds.Add($uid) # add the ID to the list } } else { @@ -230,7 +230,7 @@ function New-CIPPCAPolicy { #Send request to disable security defaults. $body = '{ "isEnabled": false }' $null = New-GraphPostRequest -tenantid $tenant -Uri 'https://graph.microsoft.com/beta/policies/identitySecurityDefaultsEnforcementPolicy' -Type patch -Body $body -ContentType 'application/json' - Write-LogMessage -Headers $User -API $APINAME -tenant $($Tenant) -message "Disabled Security Defaults for tenant $($TenantFilter)" -Sev 'Info' + Write-LogMessage -Headers $User -API 'Create CA Policy' -tenant $($Tenant) -message "Disabled Security Defaults for tenant $($TenantFilter)" -Sev 'Info' Start-Sleep 3 } $RawJSON = ConvertTo-Json -InputObject $JSONObj -Depth 10 -Compress @@ -245,7 +245,7 @@ function New-CIPPCAPolicy { } else { Write-Information "overwriting $($CheckExististing.id)" $null = New-GraphPOSTRequest -uri "https://graph.microsoft.com/beta/identity/conditionalAccess/policies/$($CheckExististing.id)" -tenantid $tenantfilter -type PATCH -body $RawJSON -asApp $true - Write-LogMessage -Headers $User -API $APINAME -tenant $($Tenant) -message "Updated Conditional Access Policy $($JSONObj.Displayname) to the template standard." -Sev 'Info' + Write-LogMessage -Headers $User -API 'Create CA Policy' -tenant $($Tenant) -message "Updated Conditional Access Policy $($JSONObj.Displayname) to the template standard." -Sev 'Info' return "Updated policy $displayname for $tenantfilter" } } else { @@ -254,7 +254,7 @@ function New-CIPPCAPolicy { Start-Sleep 3 } $null = New-GraphPOSTRequest -uri 'https://graph.microsoft.com/beta/identity/conditionalAccess/policies' -tenantid $tenantfilter -type POST -body $RawJSON -asApp $true - Write-LogMessage -Headers $User -API $APINAME -tenant $($Tenant) -message "Added Conditional Access Policy $($JSONObj.Displayname)" -Sev 'Info' + Write-LogMessage -Headers $User -API 'Create CA Policy' -tenant $($Tenant) -message "Added Conditional Access Policy $($JSONObj.Displayname)" -Sev 'Info' return "Created policy $displayname for $tenantfilter" } } catch { diff --git a/Modules/CIPPCore/Public/New-CIPPUserTask.ps1 b/Modules/CIPPCore/Public/New-CIPPUserTask.ps1 index 97c14c02d6e6..e18f016429a6 100644 --- a/Modules/CIPPCore/Public/New-CIPPUserTask.ps1 +++ b/Modules/CIPPCore/Public/New-CIPPUserTask.ps1 @@ -54,7 +54,7 @@ function New-CIPPUserTask { try { if ($UserObj.AddedAliases) { - $AliasResults = Add-CIPPAlias -user $CreationResults.Username -Aliases ($UserObj.AddedAliases -split '\s') -UserprincipalName $CreationResults.Username -TenantFilter $UserObj.tenantFilter -APIName $APIName -Headers $Headers + $AliasResults = Add-CIPPAlias -User $CreationResults.Username -Aliases ($UserObj.AddedAliases -split '\s') -UserPrincipalName $CreationResults.Username -TenantFilter $UserObj.tenantFilter -APIName $APIName -Headers $Headers $Results.Add($AliasResults) } } catch { @@ -69,12 +69,12 @@ function New-CIPPUserTask { } if ($UserObj.setManager) { - $ManagerResult = Set-CIPPManager -user $CreationResults.Username -Manager $UserObj.setManager.value -TenantFilter $UserObj.tenantFilter -APIName 'Set Manager' -Headers $Headers + $ManagerResult = Set-CIPPManager -User $CreationResults.Username -Manager $UserObj.setManager.value -TenantFilter $UserObj.tenantFilter -Headers $Headers $Results.Add($ManagerResult) } if ($UserObj.setSponsor) { - $SponsorResult = Set-CIPPManager -user $CreationResults.Username -Manager $UserObj.setSponsor.value -TenantFilter $UserObj.tenantFilter -APIName 'Set Sponsor' -Headers $Headers + $SponsorResult = Set-CIPPSponsor -User $CreationResults.Username -Sponsor $UserObj.setSponsor.value -TenantFilter $UserObj.tenantFilter -Headers $Headers $Results.Add($SponsorResult) } diff --git a/Modules/CIPPCore/Public/New-CippUser.ps1 b/Modules/CIPPCore/Public/New-CippUser.ps1 index e5dde2e8bc73..fb3451234e72 100644 --- a/Modules/CIPPCore/Public/New-CippUser.ps1 +++ b/Modules/CIPPCore/Public/New-CippUser.ps1 @@ -21,17 +21,19 @@ function New-CIPPUser { 'surname' = $UserObj.surname 'accountEnabled' = $true 'displayName' = $UserObj.displayName - 'department' = $UserObj.Department - 'mailNickname' = $UserObj.Username ? $UserObj.username : $UserObj.mailNickname + 'department' = $UserObj.department + 'mailNickname' = $UserObj.username ? $UserObj.username : $UserObj.mailNickname 'userPrincipalName' = $UserPrincipalName 'usageLocation' = $UserObj.usageLocation.value ? $UserObj.usageLocation.value : $UserObj.usageLocation - 'city' = $UserObj.City - 'country' = $UserObj.Country - 'jobtitle' = $UserObj.Jobtitle - 'mobilePhone' = $UserObj.MobilePhone + 'otherMails' = $UserObj.otherMails ? @($UserObj.otherMails) : @() + 'jobTitle' = $UserObj.jobTitle + 'mobilePhone' = $UserObj.mobilePhone 'streetAddress' = $UserObj.streetAddress - 'postalCode' = $UserObj.PostalCode - 'companyName' = $UserObj.CompanyName + 'city' = $UserObj.city + 'state' = $UserObj.state + 'country' = $UserObj.country + 'postalCode' = $UserObj.postalCode + 'companyName' = $UserObj.companyName 'passwordProfile' = @{ 'forceChangePasswordNextSignIn' = [bool]$UserObj.MustChangePass 'password' = $password diff --git a/Modules/CIPPCore/Public/Send-CIPPAlert.ps1 b/Modules/CIPPCore/Public/Send-CIPPAlert.ps1 index 90f3e36c3649..e1d60e66bb87 100644 --- a/Modules/CIPPCore/Public/Send-CIPPAlert.ps1 +++ b/Modules/CIPPCore/Public/Send-CIPPAlert.ps1 @@ -7,6 +7,8 @@ function Send-CIPPAlert { $HTMLContent, $JSONContent, $TenantFilter, + $altEmail, + $altWebhook, $APIName = 'Send Alert', $Headers, $TableName, @@ -19,8 +21,11 @@ function Send-CIPPAlert { if ($Type -eq 'email') { Write-Information 'Trying to send email' try { - if ($Config.email -like '*@*') { - $Recipients = $Config.email.split($(if ($Config.email -like '*,*') { ',' } else { ';' })).trim() | ForEach-Object { if ($_ -like '*@*') { [pscustomobject]@{EmailAddress = @{Address = $_ } } } } + if ($Config.email -like '*@*' -or $altEmail -like '*@*') { + $Recipients = if ($AltEmail) { $AltEmail } + else { + $Config.email.split($(if ($Config.email -like '*,*') { ',' } else { ';' })).trim() | ForEach-Object { if ($_ -like '*@*') { [pscustomobject]@{EmailAddress = @{Address = $_ } } } } + } $PowerShellBody = [PSCustomObject]@{ message = @{ subject = $Title @@ -72,16 +77,17 @@ function Send-CIPPAlert { Write-Information 'Trying to send webhook' try { - if ($Config.webhook -ne '') { + if ($Config.webhook -ne '' -or $AltWebhook -ne '') { if ($PSCmdlet.ShouldProcess($Config.webhook, 'Sending webhook')) { - switch -wildcard ($config.webhook) { + $webhook = if ($AltWebhook) { $AltWebhook } else { $Config.webhook } + switch -wildcard ($webhook) { '*webhook.office.com*' { $JSONBody = "{`"text`": `"You've setup your alert policies to be alerted whenever specific events happen. We've found some of these events in the log.

$JSONContent`"}" - Invoke-RestMethod -Uri $config.webhook -Method POST -ContentType 'Application/json' -Body $JSONBody + Invoke-RestMethod -Uri $webhook -Method POST -ContentType 'Application/json' -Body $JSONBody } '*discord.com*' { $JSONBody = "{`"content`": `"You've setup your alert policies to be alerted whenever specific events happen. We've found some of these events in the log. $JSONContent`"}" - Invoke-RestMethod -Uri $config.webhook -Method POST -ContentType 'Application/json' -Body $JSONBody + Invoke-RestMethod -Uri $webhook -Method POST -ContentType 'Application/json' -Body $JSONBody } '*slack.com*' { $SlackBlocks = Get-SlackAlertBlocks -JSONBody $JSONContent @@ -90,10 +96,10 @@ function Send-CIPPAlert { } else { $JSONBody = "{`"text`": `"You've setup your alert policies to be alerted whenever specific events happen. We've found some of these events in the log. $JSONContent`"}" } - Invoke-RestMethod -Uri $config.webhook -Method POST -ContentType 'Application/json' -Body $JSONBody + Invoke-RestMethod -Uri $webhook -Method POST -ContentType 'Application/json' -Body $JSONBody } default { - Invoke-RestMethod -Uri $config.webhook -Method POST -ContentType 'Application/json' -Body $JSONContent + Invoke-RestMethod -Uri $webhook -Method POST -ContentType 'Application/json' -Body $JSONContent } } } diff --git a/Modules/CIPPCore/Public/Set-CIPPCAExclusion.ps1 b/Modules/CIPPCore/Public/Set-CIPPCAExclusion.ps1 index 41e7b560e982..9c6443c15103 100644 --- a/Modules/CIPPCore/Public/Set-CIPPCAExclusion.ps1 +++ b/Modules/CIPPCore/Public/Set-CIPPCAExclusion.ps1 @@ -43,7 +43,7 @@ function Set-CIPPCAExclusion { $RawJson = ConvertTo-Json -Depth 10 -InputObject $NewExclusions if ($PSCmdlet.ShouldProcess($PolicyId, "Add exclusion for $UserID")) { - New-GraphPOSTRequest -uri "https://graph.microsoft.com/beta/identity/conditionalAccess/policies/$($CheckExisting.id)" -tenantid $tenantfilter -type PATCH -body $RawJSON -AsApp $true + $null = New-GraphPOSTRequest -uri "https://graph.microsoft.com/beta/identity/conditionalAccess/policies/$($CheckExisting.id)" -tenantid $tenantfilter -type PATCH -body $RawJSON -AsApp $true } } @@ -65,7 +65,7 @@ function Set-CIPPCAExclusion { } $RawJson = ConvertTo-Json -Depth 10 -InputObject $NewExclusions if ($PSCmdlet.ShouldProcess($PolicyId, "Remove exclusion for $UserID")) { - New-GraphPOSTRequest -uri "https://graph.microsoft.com/beta/identity/conditionalAccess/policies/$($CheckExisting.id)" -tenantid $tenantfilter -type PATCH -body $RawJSON -AsApp $true + $null = New-GraphPOSTRequest -uri "https://graph.microsoft.com/beta/identity/conditionalAccess/policies/$($CheckExisting.id)" -tenantid $tenantfilter -type PATCH -body $RawJSON -AsApp $true } } diff --git a/Modules/CIPPCore/Public/Set-CIPPDriftDeviation.ps1 b/Modules/CIPPCore/Public/Set-CIPPDriftDeviation.ps1 new file mode 100644 index 000000000000..f2d13ea545fd --- /dev/null +++ b/Modules/CIPPCore/Public/Set-CIPPDriftDeviation.ps1 @@ -0,0 +1,63 @@ +function Set-CIPPDriftDeviation { + <# + .SYNOPSIS + Sets the status of a drift deviation in the tenant drift table + .DESCRIPTION + This function stores drift deviation status changes in the tenantDrift table. + It tracks when deviations are accepted, denied, or marked as customer specific. + .PARAMETER TenantFilter + The tenant filter (used as PartitionKey) + .PARAMETER StandardName + The standard name (used as RowKey, with '.' replaced by '_') + .PARAMETER Status + The status to set. Valid values: Accepted, New, Denied, CustomerSpecific, DeniedRemediate, DeniedDelete + .PARAMETER Reason + Optional reason for the status change + .FUNCTIONALITY + Internal + .EXAMPLE + Set-CIPPDriftDeviation -TenantFilter "contoso.onmicrosoft.com" -StandardName "IntuneTemplates.12345" -Status "Accepted" -Reason "Business requirement" + .EXAMPLE + Set-CIPPDriftDeviation -TenantFilter "contoso.onmicrosoft.com" -StandardName "standards.passwordComplexity" -Status "CustomerSpecific" -Reason "Custom security policy" + #> + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + [string]$TenantFilter, + [Parameter(Mandatory = $true)] + [string]$StandardName, + [Parameter(Mandatory = $true)] + [ValidateSet('Accepted', 'New', 'Denied', 'CustomerSpecific', 'DeniedRemediate', 'DeniedDelete')] + [string]$Status, + [Parameter(Mandatory = $false)] + [string]$Reason, + [string]$user + ) + + try { + $Table = Get-CippTable -tablename 'tenantDrift' + $RowKey = $StandardName -replace '\.', '_' + $Entity = @{ + PartitionKey = $TenantFilter + RowKey = $RowKey + StandardName = $StandardName + Status = $Status + User = $user + LastModified = (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ') + } + + # Add reason if provided + if ($Reason) { + $Entity.Reason = $Reason + } + + $Result = Add-CIPPAzDataTableEntity @Table -Entity $Entity -Force + + Write-Verbose "Successfully set drift deviation status for $StandardName to $Status" + return "Successfully set drift deviation status for $StandardName to $Status" + + } catch { + Write-Error "Error setting drift deviation status: $($_.Exception.Message)" + throw + } +} diff --git a/Modules/CIPPCore/Public/Set-CIPPIntunePolicy.ps1 b/Modules/CIPPCore/Public/Set-CIPPIntunePolicy.ps1 index 71564d7a732f..7b7ee7fe629b 100644 --- a/Modules/CIPPCore/Public/Set-CIPPIntunePolicy.ps1 +++ b/Modules/CIPPCore/Public/Set-CIPPIntunePolicy.ps1 @@ -109,6 +109,40 @@ function Set-CIPPIntunePolicy { $PlatformType = 'deviceManagement' $TemplateTypeURL = 'configurationPolicies' $DisplayName = ($RawJSON | ConvertFrom-Json).Name + + $Template = $RawJSON | ConvertFrom-Json + if ($Template.templateReference.templateId) { + Write-Information "Checking configuration policy template $($Template.templateReference.templateId) for $($DisplayName)" + # Remove unavailable settings from the template + $AvailableSettings = New-GraphGETRequest -uri "https://graph.microsoft.com/beta/deviceManagement/configurationPolicyTemplates('$($Template.templateReference.templateId)')/settingTemplates?`$expand=settingDefinitions&`$top=1000" -tenantid $tenantFilter + + if ($AvailableSettings) { + Write-Information "Available settings for template $($Template.templateReference.templateId): $($AvailableSettings.Count)" + $FilteredSettings = [system.collections.generic.list[psobject]]::new() + foreach ($setting in $Template.settings) { + if ($setting.settingInstance.settingInstanceTemplateReference.settingInstanceTemplateId -in $AvailableSettings.settingInstanceTemplate.settingInstanceTemplateId) { + $AvailableSetting = $AvailableSettings | Where-Object { $_.settingInstanceTemplate.settingInstanceTemplateId -eq $setting.settingInstance.settingInstanceTemplateReference.settingInstanceTemplateId } + + if ($AvailableSetting.settingInstanceTemplate.settingInstanceTemplateId -cnotmatch $setting.settingInstance.settingInstanceTemplateReference.settingInstanceTemplateId) { + # update casing + Write-Information "Fixing casing for setting instance template $($AvailableSetting.settingInstanceTemplate.settingInstanceTemplateId)" + $setting.settingInstance.settingInstanceTemplateReference.settingInstanceTemplateId = $AvailableSetting.settingInstanceTemplate.settingInstanceTemplateId + } + + if ($AvailableSetting.settingInstanceTemplate.choiceSettingValueTemplate -cnotmatch $setting.settingInstance.choiceSettingValue.settingValueTemplateReference.settingValueTemplateId) { + # update choice setting value template + Write-Information "Fixing casing for choice setting value template $($AvailableSetting.settingInstanceTemplate.choiceSettingValueTemplate.settingValueTemplateId)" + $setting.settingInstance.choiceSettingValue.settingValueTemplateReference.settingValueTemplateId = $AvailableSetting.settingInstanceTemplate.choiceSettingValueTemplate.settingValueTemplateId + } + + $FilteredSettings.Add($setting) + } + } + $Template.settings = $FilteredSettings + $RawJSON = $Template | ConvertTo-Json -Depth 100 -Compress + } + } + $CheckExististing = New-GraphGETRequest -uri "https://graph.microsoft.com/beta/$PlatformType/$TemplateTypeURL" -tenantid $tenantFilter if ($DisplayName -in $CheckExististing.name) { $PolicyFile = $RawJSON | ConvertFrom-Json | Select-Object * -ExcludeProperty Platform, PolicyType, CreationSource diff --git a/Modules/CIPPCore/Public/Set-CIPPManager.ps1 b/Modules/CIPPCore/Public/Set-CIPPManager.ps1 index e9d172853173..c1394b04936d 100644 --- a/Modules/CIPPCore/Public/Set-CIPPManager.ps1 +++ b/Modules/CIPPCore/Public/Set-CIPPManager.ps1 @@ -1,7 +1,7 @@ function Set-CIPPManager { [CmdletBinding()] param ( - $user, + $User, $Manager, $TenantFilter, $APIName = 'Set Manager', @@ -11,12 +11,13 @@ function Set-CIPPManager { try { $ManagerBody = [PSCustomObject]@{'@odata.id' = "https://graph.microsoft.com/beta/users/$($Manager)" } $ManagerBodyJSON = ConvertTo-Json -Compress -Depth 10 -InputObject $ManagerBody - New-GraphPostRequest -uri "https://graph.microsoft.com/beta/users/$($User)/manager/`$ref" -tenantid $TenantFilter -type PUT -body $ManagerBodyJSON -Verbose - Write-LogMessage -headers $Headers -API $APINAME -tenant $UserObj.tenantID -message "Set $user's manager to $Manager" -Sev 'Info' + $null = New-GraphPostRequest -uri "https://graph.microsoft.com/beta/users/$($User)/manager/`$ref" -tenantid $TenantFilter -type PUT -body $ManagerBodyJSON + Write-LogMessage -headers $Headers -API $APIName -tenant $TenantFilter -message "Set $User's manager to $Manager" -Sev 'Info' } catch { - Write-LogMessage -headers $Headers -API $APINAME -tenant $($UserObj.tenantID) -message "Failed to Set Manager. Error:$($_.Exception.Message)" -Sev 'Error' - throw "Failed to set manager: $($_.Exception.Message)" + $ErrorMessage = Get-CippException -Exception $_ + Write-LogMessage -headers $Headers -API $APIName -tenant $TenantFilter -message "Failed to Set Manager. Error:$($ErrorMessage.NormalizedError)" -Sev 'Error' -LogData $_ + throw "Failed to set manager: $($ErrorMessage.NormalizedError)" } - return "Set $user's manager to $Manager" + return "Set $User's manager to $Manager" } diff --git a/Modules/CIPPCore/Public/Set-CIPPSponsor.ps1 b/Modules/CIPPCore/Public/Set-CIPPSponsor.ps1 new file mode 100644 index 000000000000..ad2444c01b9f --- /dev/null +++ b/Modules/CIPPCore/Public/Set-CIPPSponsor.ps1 @@ -0,0 +1,22 @@ +function Set-CIPPSponsor { + [CmdletBinding()] + param ( + $User, + $Sponsor, + $TenantFilter, + $APIName = 'Set Sponsor', + $Headers + ) + + try { + $SponsorBody = [PSCustomObject]@{'@odata.id' = "https://graph.microsoft.com/beta/users/$($Sponsor)" } + $SponsorBodyJSON = ConvertTo-Json -Compress -Depth 10 -InputObject $SponsorBody + $null = New-GraphPostRequest -uri "https://graph.microsoft.com/beta/users/$($User)/sponsors/`$ref" -tenantid $TenantFilter -type PUT -body $SponsorBodyJSON + Write-LogMessage -headers $Headers -API $APIName -tenant $TenantFilter -message "Set $User's sponsor to $Sponsor" -Sev 'Info' + } catch { + $ErrorMessage = Get-CippException -Exception $_ + Write-LogMessage -headers $Headers -API $APIName -tenant $TenantFilter -message "Failed to Set Sponsor. Error:$($ErrorMessage.NormalizedError)" -Sev 'Error' -LogData $_ + throw "Failed to set sponsor: $($_.Exception.Message)" + } + return "Set $user's sponsor to $Sponsor" +} diff --git a/Modules/CIPPCore/Public/Standards/Get-CIPPStandards.ps1 b/Modules/CIPPCore/Public/Standards/Get-CIPPStandards.ps1 index 7eaf01a80d1e..b639cf088d80 100644 --- a/Modules/CIPPCore/Public/Standards/Get-CIPPStandards.ps1 +++ b/Modules/CIPPCore/Public/Standards/Get-CIPPStandards.ps1 @@ -61,12 +61,21 @@ function Get-CIPPStandards { $CurrentStandard | Add-Member -NotePropertyName 'TemplateId' -NotePropertyValue $Template.GUID -Force if ($CurrentStandard.action.value -contains 'Remediate' -and -not ($CurrentStandard.action.value -contains 'Report')) { + Write-Host 'STRDS: autoRemediate set to true, adding Remediate action' + $reportAction = [pscustomobject]@{ label = 'Report' value = 'Report' } $CurrentStandard.action = @($CurrentStandard.action) + $reportAction } + #If its a drift template, with autoRemediate, we add the Remediate action. + if ($CurrentStandard.autoRemediate -eq $true) { + $CurrentStandard.action = @($CurrentStandard.action) + [pscustomobject]@{ + label = 'Remediate' + value = 'Remediate' + } + } $Actions = $CurrentStandard.action.value if ($Actions -contains 'Remediate' -or $Actions -contains 'warn' -or $Actions -contains 'Report') { @@ -90,6 +99,14 @@ function Get-CIPPStandards { } $CurrentStandard.action = @($CurrentStandard.action) + $reportAction } + #If its a drift template, with autoRemediate, we add the Remediate action. + if ($CurrentStandard.autoRemediate -eq $true) { + Write-Host 'STRDS: autoRemediate set to true, adding Remediate action' + $CurrentStandard.action = @($CurrentStandard.action) + [pscustomobject]@{ + label = 'Remediate' + value = 'Remediate' + } + } $Actions = $CurrentStandard.action.value if ($Actions -contains 'Remediate' -or $Actions -contains 'warn' -or $Actions -contains 'Report') { @@ -235,6 +252,14 @@ function Get-CIPPStandards { } $CurrentStandard.action = @($CurrentStandard.action) + $reportAction } + #If its a drift template, with autoRemediate, we add the Remediate action. + if ($CurrentStandard.autoRemediate -eq $true) { + Write-Host 'STRDS: autoRemediate set to true, adding Remediate action' + $CurrentStandard.action = @($CurrentStandard.action) + [pscustomobject]@{ + label = 'Remediate' + value = 'Remediate' + } + } $Actions = $CurrentStandard.action.value if ($Actions -contains 'Remediate' -or $Actions -contains 'warn' -or $Actions -contains 'Report') { @@ -269,6 +294,14 @@ function Get-CIPPStandards { } $CurrentStandard.action = @($CurrentStandard.action) + $reportAction } + #If its a drift template, with autoRemediate, we add the Remediate action. + if ($CurrentStandard.autoRemediate -eq $true) { + Write-Host 'STRDS: autoRemediate set to true, adding Remediate action' + $CurrentStandard.action = @($CurrentStandard.action) + [pscustomobject]@{ + label = 'Remediate' + value = 'Remediate' + } + } # Filter actions only 'Remediate','warn','Report' $Actions = $CurrentStandard.action.value | Where-Object { $_ -in 'Remediate', 'warn', 'Report' } @@ -292,6 +325,14 @@ function Get-CIPPStandards { } $CurrentStandard.action = @($CurrentStandard.action) + $reportAction } + #If its a drift template, with autoRemediate, we add the Remediate action. + if ($CurrentStandard.autoRemediate -eq $true) { + Write-Host 'STRDS: autoRemediate set to true, adding Remediate action' + $CurrentStandard.action = @($CurrentStandard.action) + [pscustomobject]@{ + label = 'Remediate' + value = 'Remediate' + } + } $Actions = $CurrentStandard.action.value | Where-Object { $_ -in 'Remediate', 'warn', 'Report' } if ($Actions -contains 'Remediate' -or $Actions -contains 'warn' -or $Actions -contains 'Report') { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardActivityBasedTimeout.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardActivityBasedTimeout.ps1 index f9516efe5c20..9cb907acc42c 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardActivityBasedTimeout.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardActivityBasedTimeout.ps1 @@ -43,7 +43,14 @@ function Invoke-CIPPStandardActivityBasedTimeout { Return } - $CurrentState = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/activityBasedTimeoutPolicies' -tenantid $Tenant + try { + $CurrentState = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/activityBasedTimeoutPolicies' -tenantid $Tenant + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the ActivityBasedTimeout state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $StateIsCorrect = if ($CurrentState.definition -like "*$timeout*") { $true } else { $false } If ($Settings.remediate -eq $true) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAnonReportDisable.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAnonReportDisable.ps1 index 5a0d65510fe5..83accf8b4274 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAnonReportDisable.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAnonReportDisable.ps1 @@ -31,7 +31,14 @@ function Invoke-CIPPStandardAnonReportDisable { param($Tenant, $Settings) #$Rerun -Type Standard -Tenant $Tenant -API 'allowOTPTokens' -Settings $Settings - $CurrentInfo = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/admin/reportSettings' -tenantid $Tenant -AsApp $true + try { + $CurrentInfo = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/admin/reportSettings' -tenantid $Tenant -AsApp $true + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the AnonReportDisable state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } If ($Settings.remediate -eq $true) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAppDeploy.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAppDeploy.ps1 index 1848e47918a6..5af50f4b7d83 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAppDeploy.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAppDeploy.ps1 @@ -186,6 +186,27 @@ function Invoke-CIPPStandardAppDeploy { $ExistingApp = $AppExists | Where-Object { $_.displayName -eq $TemplateData.AppName } if ($ExistingApp) { Write-LogMessage -API 'Standards' -tenant $tenant -message "Application with name '$($TemplateData.AppName)' already exists in tenant $Tenant" -sev Info + + # get existing application + $App = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/applications(appId='$($ExistingApp.appId)')" -tenantid $Tenant + + # compare permissions + $ExistingPermissions = $App.requiredResourceAccess | ConvertTo-Json -Depth 10 + $NewPermissions = $ApplicationManifest.requiredResourceAccess | ConvertTo-Json -Depth 10 + if ($ExistingPermissions -ne $NewPermissions) { + Write-LogMessage -API 'Standards' -tenant $tenant -message "Updating permissions for existing application '$($TemplateData.AppName)' in tenant $Tenant" -sev Info + + # Update permissions for existing application + $UpdateBody = @{ + requiredResourceAccess = $ApplicationManifest.requiredResourceAccess + } | ConvertTo-Json -Depth 10 + $null = New-GraphPostRequest -type PATCH -uri "https://graph.microsoft.com/beta/applications(appId='$($ExistingApp.appId)')" -tenantid $Tenant -body $UpdateBody + + # consent new permissions + Add-CIPPDelegatedPermission -RequiredResourceAccess $ApplicationManifest.requiredResourceAccess -ApplicationId $ExistingApp.appId -Tenantfilter $Tenant + Add-CIPPApplicationPermission -RequiredResourceAccess $ApplicationManifest.requiredResourceAccess -ApplicationId $ExistingApp.appId -Tenantfilter $Tenant + } + continue } diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAuthMethodsPolicyMigration.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAuthMethodsPolicyMigration.ps1 index 2d40d7a7e266..1b659cba4dec 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAuthMethodsPolicyMigration.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAuthMethodsPolicyMigration.ps1 @@ -28,10 +28,21 @@ function Invoke-CIPPStandardAuthMethodsPolicyMigration { #> param($Tenant, $Settings) - $CurrentInfo = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy' -tenantid $Tenant + try { + $CurrentInfo = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy' -tenantid $Tenant + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the AuthMethodsPolicyMigration state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } + + if ($null -eq $CurrentInfo) { + throw "Failed to retrieve current authentication methods policy information" + } if ($Settings.remediate -eq $true) { - if ($CurrentInfo.policyMigrationState -eq 'migrationComplete') { + if ($CurrentInfo.policyMigrationState -eq 'migrationComplete' -or $null -eq $CurrentInfo.policyMigrationState) { Write-LogMessage -API 'Standards' -tenant $tenant -message 'Authentication methods policy migration is already complete.' -sev Info } else { try { @@ -44,14 +55,14 @@ function Invoke-CIPPStandardAuthMethodsPolicyMigration { } if ($Settings.alert -eq $true) { - if ($CurrentInfo.policyMigrationState -ne 'migrationComplete') { + if ($CurrentInfo.policyMigrationState -ne 'migrationComplete' -and $null -ne $CurrentInfo.policyMigrationState) { Write-StandardsAlert -message 'Authentication methods policy migration is not complete. Please check if you have legacy SSPR settings or MFA settings set: https://learn.microsoft.com/en-us/entra/identity/authentication/how-to-authentication-methods-manage' -object $CurrentInfo -tenant $tenant -standardName 'AuthMethodsPolicyMigration' -standardId $Settings.standardId Write-LogMessage -API 'Standards' -tenant $tenant -message 'Authentication methods policy migration is not complete' -sev Alert } } if ($Settings.report -eq $true) { - $migrationComplete = $CurrentInfo.policyMigrationState -eq 'migrationComplete' + $migrationComplete = $CurrentInfo.policyMigrationState -eq 'migrationComplete' -or $null -eq $CurrentInfo.policyMigrationState Set-CIPPStandardsCompareField -FieldName 'standards.AuthMethodsPolicyMigration' -FieldValue $migrationComplete -TenantFilter $tenant Add-CIPPBPAField -FieldName 'AuthMethodsPolicyMigration' -FieldValue $migrationComplete -StoreAs bool -Tenant $tenant } diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAutoAddProxy.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAutoAddProxy.ps1 index 09a97aabe0d2..bca072529478 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAutoAddProxy.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAutoAddProxy.ps1 @@ -35,9 +35,16 @@ function Invoke-CIPPStandardAutoAddProxy { $QueueItem ) - $Domains = New-ExoRequest -TenantId $Tenant -Cmdlet 'Get-AcceptedDomain' | Select-Object -ExpandProperty DomainName - $AllMailboxes = New-ExoRequest -TenantId $Tenant -Cmdlet 'Get-Mailbox' - + try { + $Domains = New-ExoRequest -TenantId $Tenant -Cmdlet 'Get-AcceptedDomain' | Select-Object -ExpandProperty DomainName + $AllMailboxes = New-ExoRequest -TenantId $Tenant -Cmdlet 'Get-Mailbox' + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the AutoAddProxy state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } + $MissingProxies = 0 foreach ($Domain in $Domains) { $ProcessMailboxes = $AllMailboxes | Where-Object { @@ -47,15 +54,15 @@ function Invoke-CIPPStandardAutoAddProxy { } $MissingProxies += $ProcessMailboxes.Count } - + $StateIsCorrect = $MissingProxies -eq 0 - + if ($Settings.report -eq $true) { $state = $StateIsCorrect ? $true : $MissingProxies Set-CIPPStandardsCompareField -FieldName 'standards.AutoAddProxy' -FieldValue $state -TenantFilter $Tenant Add-CIPPBPAField -FieldName 'AutoAddProxy' -FieldValue $StateIsCorrect -StoreAs bool -Tenant $Tenant } - + if ($Settings.alert -eq $true) { if ($StateIsCorrect -eq $true) { Write-LogMessage -API 'Standards' -tenant $Tenant -message 'All mailboxes have proxy addresses for all domains' -sev Info diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAutoExpandArchive.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAutoExpandArchive.ps1 index 940f2c236ac3..c0ee4bc5bdf3 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAutoExpandArchive.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAutoExpandArchive.ps1 @@ -36,7 +36,14 @@ function Invoke-CIPPStandardAutoExpandArchive { } #we're done. ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'AutoExpandArchive' - $CurrentState = (New-ExoRequest -tenantid $Tenant -cmdlet 'Get-OrganizationConfig').AutoExpandingArchiveEnabled + try { + $CurrentState = (New-ExoRequest -tenantid $Tenant -cmdlet 'Get-OrganizationConfig').AutoExpandingArchiveEnabled + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the AutoExpandArchive state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } If ($Settings.remediate -eq $true) { Write-Host 'Time to remediate' diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardBookings.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardBookings.ps1 index e1b10d9944cb..6c8f529b2de0 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardBookings.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardBookings.ps1 @@ -40,7 +40,14 @@ function Invoke-CIPPStandardBookings { # Get state value using null-coalescing operator $state = $Settings.state.value ?? $Settings.state - $CurrentState = (New-ExoRequest -tenantid $Tenant -cmdlet 'Get-OrganizationConfig').BookingsEnabled + try { + $CurrentState = (New-ExoRequest -tenantid $Tenant -cmdlet 'Get-OrganizationConfig').BookingsEnabled + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the Bookings state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $WantedState = if ($state -eq 'true') { $true } else { $false } $StateIsCorrect = if ($CurrentState -eq $WantedState) { $true } else { $false } diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardBranding.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardBranding.ps1 index 24ddefaffd0b..b3388943900b 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardBranding.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardBranding.ps1 @@ -40,9 +40,11 @@ function Invoke-CIPPStandardBranding { try { $CurrentState = New-GraphGetRequest -Uri "https://graph.microsoft.com/beta/organization/$($TenantId.customerId)/branding/localizations/0" -tenantID $Tenant -AsApp $true - } catch { + } + catch { $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message - Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the branding for $Tenant. This tenant might not have premium licenses available: $ErrorMessage" -Sev Error + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the Branding state for $Tenant. Error: $ErrorMessage" -Sev Error + return } # Get layoutTemplateType value using null-coalescing operator diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardCloudMessageRecall.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardCloudMessageRecall.ps1 index fed75ae9b8a3..c7ff49f9cc94 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardCloudMessageRecall.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardCloudMessageRecall.ps1 @@ -38,9 +38,16 @@ function Invoke-CIPPStandardCloudMessageRecall { ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'CloudMessageRecall' # Get state value using null-coalescing operator - $state = $Settings.state.value + $state = $Settings.state.value ?? $Settings.state - $CurrentState = (New-ExoRequest -tenantid $Tenant -cmdlet 'Get-OrganizationConfig').MessageRecallEnabled + try { + $CurrentState = (New-ExoRequest -tenantid $Tenant -cmdlet 'Get-OrganizationConfig').MessageRecallEnabled + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the CloudMessageRecall state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $WantedState = if ($state -eq 'true') { $true } else { $false } $StateIsCorrect = if ($CurrentState -eq $WantedState) { $true } else { $false } @@ -53,7 +60,7 @@ function Invoke-CIPPStandardCloudMessageRecall { # Input validation if (([string]::IsNullOrWhiteSpace($state) -or $state -eq 'Select a value') -and ($Settings.remediate -eq $true -or $Settings.alert -eq $true)) { - Write-LogMessage -API 'Standards' -tenant $Tenant -message 'MessageRecallEnabled: Invalid state parameter set' -sev Error + Write-LogMessage -API 'Standards' -tenant $Tenant -message 'CloudMessageRecall: Invalid state parameter set' -sev Error return } diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardConditionalAccessTemplate.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardConditionalAccessTemplate.ps1 index cdf52e7608a0..406f02485f1a 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardConditionalAccessTemplate.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardConditionalAccessTemplate.ps1 @@ -30,18 +30,29 @@ function Invoke-CIPPStandardConditionalAccessTemplate { #> param($Tenant, $Settings) ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'ConditionalAccess' - $TestResult = Test-CIPPStandardLicense -StandardName 'ConditionalAccessTemplate' -TenantFilter $Tenant -RequiredCapabilities @('AAD_PREMIUM', 'AAD_PREMIUM_P2') - + $Table = Get-CippTable -tablename 'templates' + $TestResult = Test-CIPPStandardLicense -StandardName 'ConditionalAccessTemplate_general' -TenantFilter $Tenant -RequiredCapabilities @('AAD_PREMIUM', 'AAD_PREMIUM_P2') if ($TestResult -eq $false) { + #writing to each item that the license is not present. + $settings.TemplateList | ForEach-Object { + Set-CIPPStandardsCompareField -FieldName "standards.ConditionalAccessTemplate.$($_.value)" -FieldValue 'This tenant does not have the required license for this standard.' -Tenant $Tenant + } Write-Host "We're exiting as the correct license is not present for this standard." return $true } #we're done. - if ($Settings.remediate -eq $true) { + try { $AllCAPolicies = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/identity/conditionalAccess/policies?$top=999' -tenantid $Tenant + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the ConditionalAccessTemplate state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } + + if ($Settings.remediate -eq $true) { foreach ($Setting in $Settings) { try { - $Table = Get-CippTable -tablename 'templates' $Filter = "PartitionKey eq 'CATemplate' and RowKey eq '$($Setting.TemplateList.value)'" $JSONObj = (Get-CippAzDataTableEntity @Table -Filter $Filter).JSON $null = New-CIPPCAPolicy -replacePattern 'displayName' -TenantFilter $tenant -state $Setting.state -RawJSON $JSONObj -Overwrite $true -APIName $APIName -Headers $Request.Headers -DisableSD $Setting.DisableSD @@ -51,20 +62,24 @@ function Invoke-CIPPStandardConditionalAccessTemplate { } } } - if ($Settings.report -eq $true) { - $Policies = $Settings.TemplateList.JSON | ConvertFrom-Json -Depth 10 + if ($Settings.report -eq $true -or $Settings.remediate -eq $true) { + $Filter = "PartitionKey eq 'CATemplate'" + $Policies = (Get-CippAzDataTableEntity @Table -Filter $Filter | Where-Object RowKey -In $Settings.TemplateList.value).JSON | ConvertFrom-Json -Depth 10 #check if all groups.displayName are in the existingGroups, if not $fieldvalue should contain all missing groups, else it should be true. - $MissingPolicies = foreach ($policy in $Policies) { - $CheckExististing = $AllCAPolicies | Where-Object -Property displayName -EQ $policy.displayname + $MissingPolicies = foreach ($Setting in $Settings.TemplateList) { + $policy = $Policies | Where-Object { $_.displayName -eq $Setting.label } + $CheckExististing = $AllCAPolicies | Where-Object -Property displayName -EQ $Setting.label if (!$CheckExististing) { - $policy.displayname + Set-CIPPStandardsCompareField -FieldName "standards.ConditionalAccessTemplate.$($Setting.value)" -FieldValue "Policy $($Setting.label) is missing from this tenant." -Tenant $Tenant + } else { + $CompareObj = ConvertFrom-Json -ErrorAction SilentlyContinue -InputObject (New-CIPPCATemplate -TenantFilter $tenant -JSON $CheckExististing) + $Compare = Compare-CIPPIntuneObject -ReferenceObject $policy -DifferenceObject $CompareObj + if (!$Compare) { + Set-CIPPStandardsCompareField -FieldName "standards.ConditionalAccessTemplate.$($Setting.value)" -FieldValue $true -Tenant $Tenant + } else { + Set-CIPPStandardsCompareField -FieldName "standards.ConditionalAccessTemplate.$($Setting.value)" -FieldValue $Compare -Tenant $Tenant + } } } - if ($MissingPolicies.Count -eq 0) { - $fieldValue = $true - } else { - $fieldValue = $MissingPolicies -join ', ' - } - Set-CIPPStandardsCompareField -FieldName 'standards.ConditionalAccessTemplate' -FieldValue $fieldValue -Tenant $Tenant } } diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardCustomBannedPasswordList.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardCustomBannedPasswordList.ps1 index 5191bf2cc04c..4231f56270c2 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardCustomBannedPasswordList.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardCustomBannedPasswordList.ps1 @@ -31,9 +31,8 @@ function Invoke-CIPPStandardCustomBannedPasswordList { #> param($Tenant, $Settings) - + Write-Host "All params received: $Tenant, $tenant, $($Settings | ConvertTo-Json -Depth 10 -Compress)" $PasswordRuleTemplateId = '5cf42378-d67d-4f36-ba46-e8b86229381d' - # Parse and validate banned words from input $BannedWordsInput = $Settings.BannedWords if ([string]::IsNullOrWhiteSpace($BannedWordsInput)) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDefaultPlatformRestrictions.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDefaultPlatformRestrictions.ps1 index 133f804ad5e9..c75ee0ecd60b 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDefaultPlatformRestrictions.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDefaultPlatformRestrictions.ps1 @@ -48,9 +48,11 @@ function Invoke-CIPPStandardDefaultPlatformRestrictions { try { $CurrentState = New-GraphGetRequest -Uri "https://graph.microsoft.com/beta/deviceManagement/deviceEnrollmentConfigurations?`$expand=assignments&orderBy=priority&`$filter=deviceEnrollmentConfigurationType eq 'SinglePlatformRestriction'" -tenantID $Tenant -AsApp $true | Select-Object -Property id, androidForWorkRestriction, androidRestriction, iosRestriction, macOSRestriction, windowsRestriction - } catch { + } + catch { $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message - Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the DefaultPlatformRestrictions for $Tenant. This tenant might not have premium licenses available: $ErrorMessage" -Sev Error + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the DefaultPlatformRestrictions state for $Tenant. Error: $ErrorMessage" -Sev Error + return } $StateIsCorrect = ($CurrentState.androidForWorkRestriction.platformBlocked -eq $Settings.platformAndroidForWorkBlocked) -and diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDefaultSharingLink.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDefaultSharingLink.ps1 index f7fd81002f10..ae4c238ce051 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDefaultSharingLink.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDefaultSharingLink.ps1 @@ -48,8 +48,15 @@ function Invoke-CIPPStandardDefaultSharingLink { } $DesiredSharingLinkTypeValue = $SharingLinkTypeMap[$DesiredSharingLinkType] - $CurrentState = Get-CIPPSPOTenant -TenantFilter $Tenant | - Select-Object -Property _ObjectIdentity_, TenantFilter, DefaultSharingLinkType, DefaultLinkPermission + try { + $CurrentState = Get-CIPPSPOTenant -TenantFilter $Tenant | + Select-Object -Property _ObjectIdentity_, TenantFilter, DefaultSharingLinkType, DefaultLinkPermission + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the DefaultSharingLink state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } # Check if the current state matches the desired configuration $StateIsCorrect = ($CurrentState.DefaultSharingLinkType -eq $DesiredSharingLinkTypeValue) -and ($CurrentState.DefaultLinkPermission -eq 1) diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDeletedUserRentention.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDeletedUserRentention.ps1 index 3f9e1d4bece1..aca77117f96b 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDeletedUserRentention.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDeletedUserRentention.ps1 @@ -37,7 +37,14 @@ function Invoke-CIPPStandardDeletedUserRentention { return $true } #we're done. - $CurrentInfo = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/admin/sharepoint/settings' -tenantid $Tenant -AsApp $true + try { + $CurrentInfo = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/admin/sharepoint/settings' -tenantid $Tenant -AsApp $true + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the DeletedUserRetention state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $Days = $Settings.Days.value ?? $Settings.Days if ($Settings.report -eq $true) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableAddShortcutsToOneDrive.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableAddShortcutsToOneDrive.ps1 index d8582c299a21..d926b5a69270 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableAddShortcutsToOneDrive.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableAddShortcutsToOneDrive.ps1 @@ -37,7 +37,15 @@ function Invoke-CIPPStandardDisableAddShortcutsToOneDrive { return $true } #we're done. - $CurrentState = Get-CIPPSPOTenant -TenantFilter $Tenant | Select-Object _ObjectIdentity_, TenantFilter, DisableAddToOneDrive + try { + $CurrentState = Get-CIPPSPOTenant -TenantFilter $Tenant | + Select-Object _ObjectIdentity_, TenantFilter, DisableAddToOneDrive + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the DisableAddShortcutsToOneDrive state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } # Input validation $StateValue = $Settings.state.value ?? $Settings.state diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableAdditionalStorageProviders.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableAdditionalStorageProviders.ps1 index ab555b551825..f0275a9eb92c 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableAdditionalStorageProviders.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableAdditionalStorageProviders.ps1 @@ -39,7 +39,14 @@ function Invoke-CIPPStandardDisableAdditionalStorageProviders { } #we're done. ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'DisableAdditionalStorageProviders' - $AdditionalStorageProvidersState = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-OwaMailboxPolicy' -cmdParams @{Identity = 'OwaMailboxPolicy-Default' } -Select 'Identity, AdditionalStorageProvidersAvailable' + try { + $AdditionalStorageProvidersState = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-OwaMailboxPolicy' -cmdParams @{Identity = 'OwaMailboxPolicy-Default' } -Select 'Identity, AdditionalStorageProvidersAvailable' + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the DisableAddShortcutsToOneDrive state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } if ($Settings.remediate -eq $true) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableAppCreation.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableAppCreation.ps1 index e295bf78dec1..477cc75827e6 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableAppCreation.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableAppCreation.ps1 @@ -34,7 +34,14 @@ function Invoke-CIPPStandardDisableAppCreation { ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'DisableAppCreation' - $CurrentInfo = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/authorizationPolicy/authorizationPolicy?$select=defaultUserRolePermissions' -tenantid $Tenant + try { + $CurrentInfo = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/authorizationPolicy/authorizationPolicy?$select=defaultUserRolePermissions' -tenantid $Tenant + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the DisableAppCreation state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } If ($Settings.remediate -eq $true) { if ($CurrentInfo.defaultUserRolePermissions.allowedToCreateApps -eq $false) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableBasicAuthSMTP.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableBasicAuthSMTP.ps1 index da0f37a1b4d8..37a64f99a319 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableBasicAuthSMTP.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableBasicAuthSMTP.ps1 @@ -38,8 +38,17 @@ function Invoke-CIPPStandardDisableBasicAuthSMTP { } #we're done. ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'DisableBasicAuthSMTP' - $CurrentInfo = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-TransportConfig' - $SMTPusers = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-CASMailbox' -cmdParams @{ ResultSize = 'Unlimited' } | Where-Object { ($_.SmtpClientAuthenticationDisabled -eq $false) } + try { + $CurrentInfo = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-TransportConfig' + + $SMTPusers = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-CASMailbox' -cmdParams @{ ResultSize = 'Unlimited' } | + Where-Object { ($_.SmtpClientAuthenticationDisabled -eq $false) } + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the DisableBasicAuthSMTP state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } If ($Settings.remediate -eq $true) { Write-Host 'Time to remediate' @@ -59,7 +68,7 @@ function Invoke-CIPPStandardDisableBasicAuthSMTP { # Disable SMTP Basic Authentication for all users $SMTPusers | ForEach-Object { try { - New-ExoRequest -tenantid $Tenant -cmdlet 'Set-CASMailbox' -cmdParams @{ Identity = $_.Identity; SmtpClientAuthenticationDisabled = $null } -UseSystemMailbox $true + New-ExoRequest -tenantid $Tenant -cmdlet 'Set-CASMailbox' -cmdParams @{ Identity = $_.Guid; SmtpClientAuthenticationDisabled = $null } -UseSystemMailbox $true Write-LogMessage -API 'Standards' -tenant $tenant -message "Disabled SMTP Basic Authentication for $($_.DisplayName), $($_.PrimarySmtpAddress)" -sev Info } catch { $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableEmail.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableEmail.ps1 index 2316844428a2..3ce32493c7e3 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableEmail.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableEmail.ps1 @@ -30,7 +30,14 @@ function Invoke-CIPPStandardDisableEmail { param($Tenant, $Settings) ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'DisableEmail' - $CurrentState = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/authenticationmethodspolicy/authenticationMethodConfigurations/Email' -tenantid $Tenant + try { + $CurrentState = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/authenticationmethodspolicy/authenticationMethodConfigurations/Email' -tenantid $Tenant + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the DisableEmail state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $StateIsCorrect = ($CurrentState.state -eq 'disabled') If ($Settings.remediate -eq $true) { @@ -54,7 +61,7 @@ function Invoke-CIPPStandardDisableEmail { } if ($Settings.report -eq $true) { - $state = $StateIsCorrect -eq $true ? $true : $CurrentState + $state = $StateIsCorrect -eq $true ? $true : $CurrentState Set-CIPPStandardsCompareField -FieldName 'standards.DisableEmail' -FieldValue $state -TenantFilter $Tenant Add-CIPPBPAField -FieldName 'DisableEmail' -FieldValue $StateIsCorrect -StoreAs bool -Tenant $tenant } diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableEntraPortal.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableEntraPortal.ps1 index 85f86c5d55aa..1f791cbd4b26 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableEntraPortal.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableEntraPortal.ps1 @@ -12,7 +12,14 @@ function Invoke-CIPPStandardDisableEntraPortal { param($Tenant, $Settings) #$Rerun -Type Standard -Tenant $Tenant -API 'allowOTPTokens' -Settings $Settings #This standard is still unlisted due to MS fixing some permissions. This will be added to the list once it is fixed. - $CurrentInfo = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/admin/entra/uxSetting' -tenantid $Tenant + try { + $CurrentInfo = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/admin/entra/uxSetting' -tenantid $Tenant + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the DisableEntraPortal state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } If ($Settings.remediate -eq $true) { if ($CurrentInfo.restrictNonAdminAccess) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableExchangeOnlinePowerShell.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableExchangeOnlinePowerShell.ps1 index 525cfd6149e2..82d25184ccaf 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableExchangeOnlinePowerShell.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableExchangeOnlinePowerShell.ps1 @@ -44,7 +44,7 @@ function Invoke-CIPPStandardDisableExchangeOnlinePowerShell { try { $AdminUsers = (New-GraphGetRequest -uri 'https://graph.microsoft.com/v1.0/roleManagement/directory/roleAssignments?$expand=principal' -tenantid $Tenant).principal.userPrincipalName - $UsersWithPowerShell = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-User' -Select 'userPrincipalName, identity, remotePowerShellEnabled' | Where-Object { $_.RemotePowerShellEnabled -eq $true -and $_.userPrincipalName -notin $AdminUsers } + $UsersWithPowerShell = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-User' -Select 'userPrincipalName, identity, guid, remotePowerShellEnabled' | Where-Object { $_.RemotePowerShellEnabled -eq $true -and $_.userPrincipalName -notin $AdminUsers } $PowerShellEnabledCount = ($UsersWithPowerShell | Measure-Object).Count $StateIsCorrect = $PowerShellEnabledCount -eq 0 } catch { @@ -61,7 +61,7 @@ function Invoke-CIPPStandardDisableExchangeOnlinePowerShell { @{ CmdletInput = @{ CmdletName = 'Set-User' - Parameters = @{Identity = $_.Identity; RemotePowerShellEnabled = $false } + Parameters = @{Identity = $_.Guid; RemotePowerShellEnabled = $false } } } } diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableExternalCalendarSharing.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableExternalCalendarSharing.ps1 index 9873bf4e088c..6b4c29c2be3b 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableExternalCalendarSharing.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableExternalCalendarSharing.ps1 @@ -39,7 +39,15 @@ function Invoke-CIPPStandardDisableExternalCalendarSharing { } #we're done. ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'DisableExternalCalendarSharing' - $CurrentInfo = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-SharingPolicy' | Where-Object { $_.Default -eq $true } + try { + $CurrentInfo = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-SharingPolicy' | + Where-Object { $_.Default -eq $true } + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the DisableExternalCalendarSharing state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } if ($Settings.remediate -eq $true) { if ($CurrentInfo.Enabled) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableGuestDirectory.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableGuestDirectory.ps1 index bd3f35789266..99b096c0ccd8 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableGuestDirectory.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableGuestDirectory.ps1 @@ -31,7 +31,14 @@ function Invoke-CIPPStandardDisableGuestDirectory { param($Tenant, $Settings) ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'DisableGuestDirectory' - $CurrentInfo = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/authorizationPolicy/authorizationPolicy' -tenantid $Tenant + try { + $CurrentInfo = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/authorizationPolicy/authorizationPolicy' -tenantid $Tenant + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the DisableGuestDirectory state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } If ($Settings.remediate -eq $true) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableGuests.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableGuests.ps1 index 56ac2349159d..887740de5fef 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableGuests.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableGuests.ps1 @@ -36,8 +36,15 @@ function Invoke-CIPPStandardDisableGuests { $Lookup = $90Days.ToString('o') $AuditLookup = (Get-Date).AddDays(-7).ToUniversalTime().ToString('o') - $GraphRequest = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/users?`$filter=createdDateTime le $Lookup and userType eq 'Guest' and accountEnabled eq true &`$select=id,UserPrincipalName,signInActivity,mail,userType,accountEnabled,createdDateTime,externalUserState" -scope 'https://graph.microsoft.com/.default' -tenantid $Tenant | + try { + $GraphRequest = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/users?`$filter=createdDateTime le $Lookup and userType eq 'Guest' and accountEnabled eq true &`$select=id,UserPrincipalName,signInActivity,mail,userType,accountEnabled,createdDateTime,externalUserState" -scope 'https://graph.microsoft.com/.default' -tenantid $Tenant | Where-Object { $_.signInActivity.lastSuccessfulSignInDateTime -le $90Days } + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the DisableGuests state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $RecentlyReactivatedUsers = (New-GraphGetRequest -uri "https://graph.microsoft.com/beta/auditLogs/directoryAudits?`$filter=activityDisplayName eq 'Enable account' and activityDateTime ge $AuditLookup" -scope 'https://graph.microsoft.com/.default' -tenantid $Tenant | ForEach-Object { $_.targetResources[0].id } | Select-Object -Unique) @@ -63,7 +70,8 @@ function Invoke-CIPPStandardDisableGuests { if ($Settings.alert -eq $true) { if ($GraphRequest.Count -gt 0) { - Write-StandardsAlert -message "Guests accounts with a login longer than 90 days ago: $($GraphRequest.count)" -object $GraphRequest -tenant $tenant -standardName 'DisableGuests' -standardId $Settings.standardId + $Filtered = $GraphRequest | Select-Object -Property UserPrincipalName, id, signInActivity, mail, userType, accountEnabled, externalUserState + Write-StandardsAlert -message "Guests accounts with a login longer than 90 days ago: $($GraphRequest.count)" -object $Filtered -tenant $tenant -standardName 'DisableGuests' -standardId $Settings.standardId Write-LogMessage -API 'Standards' -tenant $tenant -message "Guests accounts with a login longer than 90 days ago: $($GraphRequest.count)" -sev Info } else { Write-LogMessage -API 'Standards' -tenant $tenant -message 'No guests accounts with a login longer than 90 days ago.' -sev Info diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableM365GroupUsers.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableM365GroupUsers.ps1 index 44bc07465732..ad06e06ce457 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableM365GroupUsers.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableM365GroupUsers.ps1 @@ -36,7 +36,15 @@ function Invoke-CIPPStandardDisableM365GroupUsers { return $true } #we're done. - $CurrentState = (New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/settings' -tenantid $tenant) | Where-Object -Property displayname -EQ 'Group.unified' + try { + $CurrentState = (New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/settings' -tenantid $tenant) | + Where-Object -Property displayname -EQ 'Group.unified' + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the DisableM365GroupUsers state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } If ($Settings.remediate -eq $true) { if (($CurrentState.values | Where-Object { $_.name -eq 'EnableGroupCreation' }).value -eq 'false') { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableOutlookAddins.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableOutlookAddins.ps1 index 39ab24e016bf..11e3db4739cf 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableOutlookAddins.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableOutlookAddins.ps1 @@ -39,7 +39,15 @@ function Invoke-CIPPStandardDisableOutlookAddins { } #we're done. ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'DisableOutlookAddins' - $CurrentInfo = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-RoleAssignmentPolicy' | Where-Object { $_.IsDefault -eq $true } + try { + $CurrentInfo = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-RoleAssignmentPolicy' | + Where-Object { $_.IsDefault -eq $true } + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the DisableOutlookAddins state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $Roles = @('My Custom Apps', 'My Marketplace Apps', 'My ReadWriteMailbox Apps') $RolesToRemove = foreach ($Role in $Roles) { if ($CurrentInfo.AssignedRoles -contains $Role) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableQRCodePin.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableQRCodePin.ps1 index 358f1bfb76e5..d724a8f779c3 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableQRCodePin.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableQRCodePin.ps1 @@ -29,7 +29,14 @@ function Invoke-CIPPStandardDisableQRCodePin { param($Tenant, $Settings) - $CurrentState = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/authenticationmethodspolicy/authenticationMethodConfigurations/QRCodePin' -tenantid $Tenant + try { + $CurrentState = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/authenticationmethodspolicy/authenticationMethodConfigurations/QRCodePin' -tenantid $Tenant + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the DisableQRCodePin state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $StateIsCorrect = ($CurrentState.state -eq 'disabled') If ($Settings.remediate -eq $true) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableReshare.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableReshare.ps1 index 94504d7792a6..ffb0dba555dc 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableReshare.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableReshare.ps1 @@ -39,7 +39,14 @@ function Invoke-CIPPStandardDisableReshare { return $true } #we're done. - $CurrentInfo = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/admin/sharepoint/settings' -tenantid $Tenant -AsApp $true + try { + $CurrentInfo = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/admin/sharepoint/settings' -tenantid $Tenant -AsApp $true + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the DisableReshare state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } If ($Settings.remediate -eq $true) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableResourceMailbox.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableResourceMailbox.ps1 index 0635a3eeef52..4f284d19857f 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableResourceMailbox.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableResourceMailbox.ps1 @@ -40,10 +40,17 @@ function Invoke-CIPPStandardDisableResourceMailbox { ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'DisableResourceMailbox' # Get all users that are able to be - $UserList = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/users?$top=999&$filter=accountEnabled eq true and onPremisesSyncEnabled ne true and assignedLicenses/$count eq 0&$count=true' -Tenantid $Tenant -ComplexFilter | + try { + $UserList = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/users?$top=999&$filter=accountEnabled eq true and onPremisesSyncEnabled ne true and assignedLicenses/$count eq 0&$count=true' -Tenantid $Tenant -ComplexFilter | Where-Object { $_.userType -eq 'Member' } - $ResourceMailboxList = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-Mailbox' -cmdParams @{ Filter = "RecipientTypeDetails -eq 'RoomMailbox' -or RecipientTypeDetails -eq 'EquipmentMailbox'" } -Select 'UserPrincipalName,DisplayName,RecipientTypeDetails,ExternalDirectoryObjectId' | + $ResourceMailboxList = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-Mailbox' -cmdParams @{ Filter = "RecipientTypeDetails -eq 'RoomMailbox' -or RecipientTypeDetails -eq 'EquipmentMailbox'" } -Select 'UserPrincipalName,DisplayName,RecipientTypeDetails,ExternalDirectoryObjectId' | Where-Object { $_.ExternalDirectoryObjectId -in $UserList.id } + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the DisableResourceMailbox state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } If ($Settings.remediate -eq $true) { Write-Host 'Time to remediate' diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableSMS.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableSMS.ps1 index 076ea346d5d3..3361dbde8160 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableSMS.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableSMS.ps1 @@ -31,7 +31,14 @@ function Invoke-CIPPStandardDisableSMS { param($Tenant, $Settings) ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'DisableSMS' - $CurrentState = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/authenticationmethodspolicy/authenticationMethodConfigurations/SMS' -tenantid $Tenant + try { + $CurrentState = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/authenticationmethodspolicy/authenticationMethodConfigurations/SMS' -tenantid $Tenant + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the DisableSMS state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $StateIsCorrect = ($CurrentState.state -eq 'disabled') If ($Settings.remediate -eq $true) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableSecurityGroupUsers.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableSecurityGroupUsers.ps1 index 434754a89f43..d010fb24668d 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableSecurityGroupUsers.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableSecurityGroupUsers.ps1 @@ -30,7 +30,14 @@ function Invoke-CIPPStandardDisableSecurityGroupUsers { param($Tenant, $Settings) ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'DisableSecurityGroupUsers' - $CurrentInfo = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/authorizationPolicy/authorizationPolicy' -tenantid $Tenant + try { + $CurrentInfo = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/authorizationPolicy/authorizationPolicy' -tenantid $Tenant + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the DisableSecurityGroupUsers state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } If ($Settings.remediate -eq $true) { if ($CurrentInfo.defaultUserRolePermissions.allowedToCreateSecurityGroups -eq $false) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableSharePointLegacyAuth.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableSharePointLegacyAuth.ps1 index 99535b28cf89..eda6a2a4014f 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableSharePointLegacyAuth.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableSharePointLegacyAuth.ps1 @@ -40,7 +40,14 @@ function Invoke-CIPPStandardDisableSharePointLegacyAuth { return $true } #we're done. - $CurrentInfo = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/admin/sharepoint/settings?$select=isLegacyAuthProtocolsEnabled' -tenantid $Tenant -AsApp $true + try { + $CurrentInfo = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/admin/sharepoint/settings?$select=isLegacyAuthProtocolsEnabled' -tenantid $Tenant -AsApp $true + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the DisableSharePointLegacyAuth state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } If ($Settings.remediate -eq $true) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableSharedMailbox.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableSharedMailbox.ps1 index 257229cb8aea..f9daa926db02 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableSharedMailbox.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableSharedMailbox.ps1 @@ -33,8 +33,15 @@ function Invoke-CIPPStandardDisableSharedMailbox { param($Tenant, $Settings) ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'DisableSharedMailbox' - $UserList = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/users?$top=999&$filter=accountEnabled eq true and onPremisesSyncEnabled ne true&$count=true' -Tenantid $Tenant -ComplexFilter - $SharedMailboxList = (New-GraphGetRequest -uri "https://outlook.office365.com/adminapi/beta/$($Tenant)/Mailbox" -Tenantid $Tenant -scope ExchangeOnline | Where-Object { $_.RecipientTypeDetails -EQ 'SharedMailbox' -or $_.RecipientTypeDetails -eq 'SchedulingMailbox' -and $_.UserPrincipalName -in $UserList.UserPrincipalName }) + try { + $UserList = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/users?$top=999&$filter=accountEnabled eq true and onPremisesSyncEnabled ne true&$count=true' -Tenantid $Tenant -ComplexFilter + $SharedMailboxList = (New-GraphGetRequest -uri "https://outlook.office365.com/adminapi/beta/$($Tenant)/Mailbox" -Tenantid $Tenant -scope ExchangeOnline | Where-Object { $_.RecipientTypeDetails -EQ 'SharedMailbox' -or $_.RecipientTypeDetails -eq 'SchedulingMailbox' -and $_.UserPrincipalName -in $UserList.UserPrincipalName }) + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the DisableSharedMailbox state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } If ($Settings.remediate -eq $true) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableTNEF.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableTNEF.ps1 index f9d16a185bd2..e6bd16f501ba 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableTNEF.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableTNEF.ps1 @@ -36,7 +36,15 @@ function Invoke-CIPPStandardDisableTNEF { Write-Host "We're exiting as the correct license is not present for this standard." return $true } #we're done. - $CurrentState = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-RemoteDomain' -cmdParams @{Identity = 'Default' } + + try { + $CurrentState = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-RemoteDomain' -cmdParams @{Identity = 'Default' } + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the DisableTNEF state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } if ($Settings.remediate -eq $true) { Write-Host 'Time to remediate' diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableTenantCreation.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableTenantCreation.ps1 index dcdab63d0073..f65a7c8ae124 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableTenantCreation.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableTenantCreation.ps1 @@ -33,7 +33,14 @@ function Invoke-CIPPStandardDisableTenantCreation { param($Tenant, $Settings) ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'DisableTenantCreation' - $CurrentState = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/authorizationPolicy/authorizationPolicy' -tenantid $Tenant + try { + $CurrentState = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/authorizationPolicy/authorizationPolicy' -tenantid $Tenant + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the DisableTenantCreation state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $StateIsCorrect = ($CurrentState.defaultUserRolePermissions.allowedToCreateTenants -eq $false) If ($Settings.remediate -eq $true) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableUserSiteCreate.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableUserSiteCreate.ps1 index d88f4cbc3fc4..74e2628e219e 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableUserSiteCreate.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableUserSiteCreate.ps1 @@ -36,7 +36,14 @@ function Invoke-CIPPStandardDisableUserSiteCreate { return $true } #we're done. - $CurrentInfo = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/admin/sharepoint/settings' -tenantid $Tenant -AsApp $true + try { + $CurrentInfo = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/admin/sharepoint/settings' -tenantid $Tenant -AsApp $true + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the DisableUserSiteCreate state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } If ($Settings.remediate -eq $true) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableVoice.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableVoice.ps1 index 3ad258c913e1..cad37045dada 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableVoice.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisableVoice.ps1 @@ -31,7 +31,14 @@ function Invoke-CIPPStandardDisableVoice { param($Tenant, $Settings) ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'DisableVoice' - $CurrentState = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/authenticationmethodspolicy/authenticationMethodConfigurations/Voice' -tenantid $Tenant + try { + $CurrentState = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/authenticationmethodspolicy/authenticationMethodConfigurations/Voice' -tenantid $Tenant + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the DisableVoice state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $StateIsCorrect = ($CurrentState.state -eq 'disabled') If ($Settings.remediate -eq $true) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisablex509Certificate.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisablex509Certificate.ps1 index 518c4ab96cd1..4367b445c686 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisablex509Certificate.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardDisablex509Certificate.ps1 @@ -30,7 +30,14 @@ function Invoke-CIPPStandardDisablex509Certificate { param($Tenant, $Settings) ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'Disablex509Certificate' - $CurrentState = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/authenticationmethodspolicy/authenticationMethodConfigurations/x509Certificate' -tenantid $Tenant + try { + $CurrentState = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/authenticationmethodspolicy/authenticationMethodConfigurations/x509Certificate' -tenantid $Tenant + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the Disablex509Certificate state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $StateIsCorrect = ($CurrentState.state -eq 'disabled') If ($Settings.remediate -eq $true) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEXODisableAutoForwarding.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEXODisableAutoForwarding.ps1 index 9ad98d78c2cf..b98f28fe2fd3 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEXODisableAutoForwarding.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEXODisableAutoForwarding.ps1 @@ -41,7 +41,14 @@ function Invoke-CIPPStandardEXODisableAutoForwarding { } #we're done. ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'EXODisableAutoForwarding' - $CurrentInfo = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-HostedOutboundSpamFilterPolicy' -cmdParams @{Identity = 'Default' } -useSystemMailbox $true + try { + $CurrentInfo = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-HostedOutboundSpamFilterPolicy' -cmdParams @{Identity = 'Default' } -useSystemMailbox $true + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the EXODisableAutoForwarding state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $StateIsCorrect = $CurrentInfo.AutoForwardingMode -eq 'Off' if ($Settings.remediate -eq $true) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEXOOutboundSpamLimits.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEXOOutboundSpamLimits.ps1 index 10d8a82011b0..d29e2af9634f 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEXOOutboundSpamLimits.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEXOOutboundSpamLimits.ps1 @@ -68,7 +68,15 @@ function Invoke-CIPPStandardEXOOutboundSpamLimits { } # Get current settings - $CurrentInfo = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-HostedOutboundSpamFilterPolicy' -cmdParams @{Identity = 'Default' } -Select 'RecipientLimitExternalPerHour, RecipientLimitInternalPerHour, RecipientLimitPerDay, ActionWhenThresholdReached' -useSystemMailbox $true | Select-Object -ExcludeProperty *data.type* + try { + $CurrentInfo = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-HostedOutboundSpamFilterPolicy' -cmdParams @{Identity = 'Default' } -Select 'RecipientLimitExternalPerHour, RecipientLimitInternalPerHour, RecipientLimitPerDay, ActionWhenThresholdReached' -useSystemMailbox $true | + Select-Object -ExcludeProperty *data.type* + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the EXOOutboundSpamLimits state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } # Check if settings are already correct $StateIsCorrect = ($CurrentInfo.RecipientLimitExternalPerHour -eq $Settings.RecipientLimitExternalPerHour) -and diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableAppConsentRequests.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableAppConsentRequests.ps1 index 4f2fe48c8f42..36ea59379a42 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableAppConsentRequests.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableAppConsentRequests.ps1 @@ -33,7 +33,14 @@ function Invoke-CIPPStandardEnableAppConsentRequests { param($Tenant, $Settings) ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'EnableAppConsentRequests' - $CurrentInfo = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/policies/adminConsentRequestPolicy' -tenantid $Tenant + try { + $CurrentInfo = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/policies/adminConsentRequestPolicy' -tenantid $Tenant + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the EnableAppConsentRequests state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } If ($Settings.remediate -eq $true) { try { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableCustomerLockbox.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableCustomerLockbox.ps1 index ee6badad343b..aec70509a6d5 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableCustomerLockbox.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableCustomerLockbox.ps1 @@ -39,7 +39,15 @@ function Invoke-CIPPStandardEnableCustomerLockbox { return $true } #we're done. - $CustomerLockboxStatus = (New-ExoRequest -tenantid $Tenant -cmdlet 'Get-OrganizationConfig').CustomerLockboxEnabled + try { + $CustomerLockboxStatus = (New-ExoRequest -tenantid $Tenant -cmdlet 'Get-OrganizationConfig').CustomerLockboxEnabled + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the EnableCustomerLockbox state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } + if ($Settings.remediate -eq $true) { Write-Host 'Time to remediate' try { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableFIDO2.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableFIDO2.ps1 index dc5b9e9e5da7..bb1940101a45 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableFIDO2.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableFIDO2.ps1 @@ -31,7 +31,14 @@ function Invoke-CIPPStandardEnableFIDO2 { param($Tenant, $Settings) ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'EnableFIDO2' - $CurrentState = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/authenticationmethodspolicy/authenticationMethodConfigurations/Fido2' -tenantid $Tenant + try { + $CurrentState = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/authenticationmethodspolicy/authenticationMethodConfigurations/Fido2' -tenantid $Tenant + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the EnableFIDO2 state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $StateIsCorrect = ($CurrentState.state -eq 'enabled') If ($Settings.remediate -eq $true) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableHardwareOAuth.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableHardwareOAuth.ps1 index 405a2747e0bc..1cc932350136 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableHardwareOAuth.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableHardwareOAuth.ps1 @@ -30,7 +30,14 @@ function Invoke-CIPPStandardEnableHardwareOAuth { param($Tenant, $Settings) ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'EnableHardwareOAuth' - $CurrentState = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/authenticationmethodspolicy/authenticationMethodConfigurations/HardwareOath' -tenantid $Tenant + try { + $CurrentState = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/authenticationmethodspolicy/authenticationMethodConfigurations/HardwareOath' -tenantid $Tenant + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the EnableHardwareOAuth state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $StateIsCorrect = ($CurrentState.state -eq 'enabled') If ($Settings.remediate -eq $true) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableLitigationHold.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableLitigationHold.ps1 index 97173ce9afc5..1e1df95f041d 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableLitigationHold.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableLitigationHold.ps1 @@ -37,7 +37,15 @@ function Invoke-CIPPStandardEnableLitigationHold { } #we're done. ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'EnableLitigationHold' - $MailboxesNoLitHold = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-Mailbox' -cmdParams @{ Filter = 'LitigationHoldEnabled -eq "False"' } -Select 'UserPrincipalName,PersistedCapabilities,LitigationHoldEnabled' | Where-Object { $_.PersistedCapabilities -contains 'BPOS_S_DlpAddOn' -or $_.PersistedCapabilities -contains 'BPOS_S_Enterprise' } + try { + $MailboxesNoLitHold = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-Mailbox' -cmdParams @{ Filter = 'LitigationHoldEnabled -eq "False"' } -Select 'UserPrincipalName,PersistedCapabilities,LitigationHoldEnabled' | + Where-Object { $_.PersistedCapabilities -contains 'BPOS_S_DlpAddOn' -or $_.PersistedCapabilities -contains 'BPOS_S_Enterprise' } + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the EnableLitigationHold state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } if ($Settings.remediate -eq $true) { if ($null -eq $MailboxesNoLitHold) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableMailTips.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableMailTips.ps1 index 1532e458a2ab..4e251944fc17 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableMailTips.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableMailTips.ps1 @@ -41,7 +41,14 @@ function Invoke-CIPPStandardEnableMailTips { } #we're done. ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'EnableMailTips' - $MailTipsState = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-OrganizationConfig' | Select-Object MailTipsAllTipsEnabled, MailTipsExternalRecipientsTipsEnabled, MailTipsGroupMetricsEnabled, MailTipsLargeAudienceThreshold + try { + $MailTipsState = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-OrganizationConfig' | Select-Object MailTipsAllTipsEnabled, MailTipsExternalRecipientsTipsEnabled, MailTipsGroupMetricsEnabled, MailTipsLargeAudienceThreshold + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the EnableMailTips state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $StateIsCorrect = if ($MailTipsState.MailTipsAllTipsEnabled -and $MailTipsState.MailTipsExternalRecipientsTipsEnabled -and $MailTipsState.MailTipsGroupMetricsEnabled -and $MailTipsState.MailTipsLargeAudienceThreshold -eq $Settings.MailTipsLargeAudienceThreshold) { $true } else { $false } if ($Settings.remediate -eq $true) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableMailboxAuditing.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableMailboxAuditing.ps1 index 6a2397ce7139..2676d7577a04 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableMailboxAuditing.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableMailboxAuditing.ps1 @@ -40,7 +40,14 @@ function Invoke-CIPPStandardEnableMailboxAuditing { } #we're done. ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'EnableMailboxAuditing' - $AuditState = (New-ExoRequest -tenantid $Tenant -cmdlet 'Get-OrganizationConfig').AuditDisabled + try { + $AuditState = (New-ExoRequest -tenantid $Tenant -cmdlet 'Get-OrganizationConfig').AuditDisabled + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the EnableMailboxAuditing state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } if ($Settings.remediate -eq $true) { if ($AuditState) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableOnlineArchiving.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableOnlineArchiving.ps1 index fe06e02928a4..6710fce40846 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableOnlineArchiving.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardEnableOnlineArchiving.ps1 @@ -76,7 +76,8 @@ function Invoke-CIPPStandardEnableOnlineArchiving { if ($Settings.alert -eq $true) { if ($MailboxesNoArchive) { - Write-StandardsAlert -message "Mailboxes without Online Archiving: $($MailboxesNoArchive.Count)" -object $MailboxesNoArchive -tenant $Tenant -standardName 'EnableOnlineArchiving' -standardId $Settings.standardId + $Object = $MailboxesNoArchive | Select-Object -Property UserPrincipalName, ArchiveGuid + Write-StandardsAlert -message "Mailboxes without Online Archiving: $($MailboxesNoArchive.Count)" -object $Object -tenant $Tenant -standardName 'EnableOnlineArchiving' -standardId $Settings.standardId Write-LogMessage -API 'Standards' -tenant $Tenant -message "Mailboxes without Online Archiving: $($MailboxesNoArchive.Count)" -sev Info } else { Write-LogMessage -API 'Standards' -tenant $Tenant -message 'All mailboxes have Online Archiving enabled' -sev Info diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardExConnector.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardExConnector.ps1 index 359f32f4206e..b57860ecf55b 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardExConnector.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardExConnector.ps1 @@ -1,4 +1,4 @@ -function Invoke-CIPPStandardExConnector { +function Invoke-CIPPStandardExchangeConnectorTemplate { <# .FUNCTIONALITY Internal @@ -12,7 +12,7 @@ function Invoke-CIPPStandardExConnector { } #we're done. ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'ExConnector' - If ($Settings.remediate -eq $true) { + if ($Settings.remediate -eq $true) { foreach ($Template in $Settings.TemplateList) { try { @@ -20,7 +20,7 @@ function Invoke-CIPPStandardExConnector { $Filter = "PartitionKey eq 'ExConnectorTemplate' and RowKey eq '$($Template.value)'" $connectorType = (Get-AzDataTableEntity @Table -Filter $Filter).direction $RequestParams = (Get-AzDataTableEntity @Table -Filter $Filter).JSON | ConvertFrom-Json - if($RequestParams.comment) { $RequestParams.comment = Get-CIPPTextReplacement -Text $RequestParams.comment -TenantFilter $Tenant } else { $RequestParams | Add-Member -NotePropertyValue "no comment" -NotePropertyName comment -Force } + if ($RequestParams.comment) { $RequestParams.comment = Get-CIPPTextReplacement -Text $RequestParams.comment -TenantFilter $Tenant } else { $RequestParams | Add-Member -NotePropertyValue 'no comment' -NotePropertyName comment -Force } $Existing = New-ExoRequest -ErrorAction SilentlyContinue -tenantid $Tenant -cmdlet "Get-$($ConnectorType)connector" | Where-Object -Property Identity -EQ $RequestParams.name if ($Existing) { $RequestParams | Add-Member -NotePropertyValue $Existing.Identity -NotePropertyName Identity -Force diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardExcludedfileExt.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardExcludedfileExt.ps1 index fc1fd74618ce..fac3406a91f8 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardExcludedfileExt.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardExcludedfileExt.ps1 @@ -37,7 +37,14 @@ function Invoke-CIPPStandardExcludedfileExt { return $true } #we're done. - $CurrentInfo = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/admin/sharepoint/settings' -tenantid $Tenant -AsApp $true + try { + $CurrentInfo = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/admin/sharepoint/settings' -tenantid $Tenant -AsApp $true + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the ExcludedfileExt state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $Exts = ($Settings.ext -replace ' ', '') -split ',' # Add a wildcard to the extensions since thats what the SP admin center does $Exts = $Exts | ForEach-Object { if ($_ -notlike '*.*') { "*.$_" } else { $_ } } diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardExternalMFATrusted.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardExternalMFATrusted.ps1 index 05320c8fbb03..166709d0b71e 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardExternalMFATrusted.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardExternalMFATrusted.ps1 @@ -31,7 +31,14 @@ function Invoke-CIPPStandardExternalMFATrusted { param($Tenant, $Settings) ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'ExternalMFATrusted' - $ExternalMFATrusted = (New-GraphGetRequest -uri 'https://graph.microsoft.com/v1.0/policies/crossTenantAccessPolicy/default?$select=inboundTrust' -tenantid $Tenant) + try { + $ExternalMFATrusted = (New-GraphGetRequest -uri 'https://graph.microsoft.com/v1.0/policies/crossTenantAccessPolicy/default?$select=inboundTrust' -tenantid $Tenant) + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the ExternalMFATrusted state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } # Get state value using null-coalescing operator $state = $Settings.state.value ?? $Settings.state diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardFocusedInbox.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardFocusedInbox.ps1 index e45a8920d3e3..268a521c020c 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardFocusedInbox.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardFocusedInbox.ps1 @@ -46,7 +46,14 @@ function Invoke-CIPPStandardFocusedInbox { Return } - $CurrentState = (New-ExoRequest -tenantid $Tenant -cmdlet 'Get-OrganizationConfig').FocusedInboxOn + try { + $CurrentState = (New-ExoRequest -tenantid $Tenant -cmdlet 'Get-OrganizationConfig').FocusedInboxOn + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the FocusedInbox state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $WantedState = if ($state -eq 'enabled') { $true } else { $false } $StateIsCorrect = if ($CurrentState -eq $WantedState) { $true } else { $false } diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardGlobalQuarantineNotifications.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardGlobalQuarantineNotifications.ps1 index 138fb8a2464d..ea0284c0e2ba 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardGlobalQuarantineNotifications.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardGlobalQuarantineNotifications.ps1 @@ -36,7 +36,16 @@ function Invoke-CIPPStandardGlobalQuarantineNotifications { Write-Host "We're exiting as the correct license is not present for this standard." return $true } #we're done. - $CurrentState = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-QuarantinePolicy' -cmdParams @{ QuarantinePolicyType = 'GlobalQuarantinePolicy' } | Select-Object -ExcludeProperty '*data.type' + + try { + $CurrentState = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-QuarantinePolicy' -cmdParams @{ QuarantinePolicyType = 'GlobalQuarantinePolicy' } | + Select-Object -ExcludeProperty '*data.type' + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the GlobalQuarantineNotifications state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } # This might take the cake on ugly hacky stuff i've done, # but i just cant understand why the API returns the values it does and not a timespan like the equivalent powershell command does diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardGuestInvite.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardGuestInvite.ps1 index 4cb15592d663..b767665fc283 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardGuestInvite.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardGuestInvite.ps1 @@ -30,7 +30,14 @@ function Invoke-CIPPStandardGuestInvite { param($Tenant, $Settings) - $CurrentState = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/authorizationPolicy/authorizationPolicy' -tenantid $Tenant + try { + $CurrentState = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/authorizationPolicy/authorizationPolicy' -tenantid $Tenant + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the GuestInvite state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } # Input validation and value handling $AllowInvitesFromValue = $Settings.allowInvitesFrom.value ?? $Settings.allowInvitesFrom diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardIntuneComplianceSettings.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardIntuneComplianceSettings.ps1 index bbdd69edd3c6..62477214d4f3 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardIntuneComplianceSettings.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardIntuneComplianceSettings.ps1 @@ -37,7 +37,15 @@ function Invoke-CIPPStandardIntuneComplianceSettings { return $true } #we're done. - $CurrentState = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/deviceManagement/settings' -tenantid $Tenant | Select-Object secureByDefault, deviceComplianceCheckinThresholdDays + try { + $CurrentState = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/deviceManagement/settings' -tenantid $Tenant | + Select-Object secureByDefault, deviceComplianceCheckinThresholdDays + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the intuneDeviceReg state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } if ($null -eq $Settings.deviceComplianceCheckinThresholdDays) { $Settings.deviceComplianceCheckinThresholdDays = $CurrentState.deviceComplianceCheckinThresholdDays } $SecureByDefault = [bool]($Settings.secureByDefault.value ? $Settings.secureByDefault.value : $Settings.secureByDefault) diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardIntuneTemplate.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardIntuneTemplate.ps1 index 04b3f8eb6c86..53fd2f4cee4e 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardIntuneTemplate.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardIntuneTemplate.ps1 @@ -31,10 +31,14 @@ function Invoke-CIPPStandardIntuneTemplate { https://docs.cipp.app/user-documentation/tenant/standards/list-standards #> param($Tenant, $Settings) - $TestResult = Test-CIPPStandardLicense -StandardName 'IntuneTemplate' -TenantFilter $Tenant -RequiredCapabilities @('INTUNE_A', 'MDM_Services', 'EMS', 'SCCM', 'MICROSOFTINTUNEPLAN1') + $TestResult = Test-CIPPStandardLicense -StandardName 'IntuneTemplate_general' -TenantFilter $Tenant -RequiredCapabilities @('INTUNE_A', 'MDM_Services', 'EMS', 'SCCM', 'MICROSOFTINTUNEPLAN1') ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'intuneTemplate' if ($TestResult -eq $false) { + #writing to each item that the license is not present. + $settings.TemplateList | ForEach-Object { + Set-CIPPStandardsCompareField -FieldName "standards.IntuneTemplate.$($_.value)" -FieldValue 'This tenant does not have the required license for this standard.' -Tenant $Tenant + } Write-Host "We're exiting as the correct license is not present for this standard." return $true } #we're done. diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardMDMScope.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardMDMScope.ps1 index ab52addfe26d..193ab399da83 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardMDMScope.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardMDMScope.ps1 @@ -37,7 +37,14 @@ function Invoke-CIPPStandardMDMScope { return $true } #we're done. - $CurrentInfo = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/policies/mobileDeviceManagementPolicies/0000000a-0000-0000-c000-000000000000?$expand=includedGroups' -tenantid $Tenant + try { + $CurrentInfo = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/policies/mobileDeviceManagementPolicies/0000000a-0000-0000-c000-000000000000?$expand=includedGroups' -tenantid $Tenant + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the MDM Scope state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $StateIsCorrect = ($CurrentInfo.termsOfUseUrl -eq 'https://portal.manage.microsoft.com/TermsofUse.aspx') -and ($CurrentInfo.discoveryUrl -eq 'https://enrollment.manage.microsoft.com/enrollmentserver/discovery.svc') -and diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardMailContacts.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardMailContacts.ps1 index 55655ccc1d8f..1cd033c26a67 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardMailContacts.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardMailContacts.ps1 @@ -34,8 +34,15 @@ function Invoke-CIPPStandardMailContacts { param($Tenant, $Settings) ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'MailContacts' - $TenantID = (New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/organization' -tenantid $tenant) - $CurrentInfo = New-GraphGetRequest -Uri "https://graph.microsoft.com/beta/organization/$($TenantID.id)" -tenantid $Tenant + try { + $TenantID = (New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/organization' -tenantid $tenant) + $CurrentInfo = New-GraphGetRequest -Uri "https://graph.microsoft.com/beta/organization/$($TenantID.id)" -tenantid $Tenant + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the MailContacts state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $contacts = $settings $TechAndSecurityContacts = @($Contacts.SecurityContact, $Contacts.TechContact) diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardMailboxRecipientLimits.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardMailboxRecipientLimits.ps1 index 0225f6423bfe..cf2459c7071c 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardMailboxRecipientLimits.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardMailboxRecipientLimits.ps1 @@ -44,7 +44,14 @@ function Invoke-CIPPStandardMailboxRecipientLimits { } # Get mailbox plans first - $MailboxPlans = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-MailboxPlan' -cmdParams @{ ResultSize = 'Unlimited' } + try { + $MailboxPlans = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-MailboxPlan' -cmdParams @{ ResultSize = 'Unlimited' } + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the MailboxRecipientLimits state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } # Create a hashtable of mailbox plans for quick lookup $MailboxPlanLookup = @{} diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardMalwareFilterPolicy.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardMalwareFilterPolicy.ps1 index 632ae123f34e..dfc4135d4fa8 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardMalwareFilterPolicy.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardMalwareFilterPolicy.ps1 @@ -63,9 +63,16 @@ function Invoke-CIPPStandardMalwareFilterPolicy { $RuleName = $ExistingRule.Name } - $CurrentState = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-MalwareFilterPolicy' | - Where-Object -Property Name -EQ $PolicyName | - Select-Object Name, EnableFileFilter, FileTypeAction, FileTypes, ZapEnabled, QuarantineTag, EnableInternalSenderAdminNotifications, InternalSenderAdminAddress, EnableExternalSenderAdminNotifications, ExternalSenderAdminAddress + try { + $CurrentState = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-MalwareFilterPolicy' | + Where-Object -Property Name -EQ $PolicyName | + Select-Object Name, EnableFileFilter, FileTypeAction, FileTypes, ZapEnabled, QuarantineTag, EnableInternalSenderAdminNotifications, InternalSenderAdminAddress, EnableExternalSenderAdminNotifications, ExternalSenderAdminAddress + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the MalwareFilterPolicy state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $DefaultFileTypes = @('ace', 'ani', 'apk', 'app', 'appx', 'arj', 'bat', 'cab', 'cmd', 'com', 'deb', 'dex', 'dll', 'docm', 'elf', 'exe', 'hta', 'img', 'iso', 'jar', 'jnlp', 'kext', 'lha', 'lib', 'library', 'lnk', 'lzh', 'macho', 'msc', 'msi', 'msix', 'msp', 'mst', 'pif', 'ppa', 'ppam', 'reg', 'rev', 'scf', 'scr', 'sct', 'sys', 'uif', 'vb', 'vbe', 'vbs', 'vxd', 'wsc', 'wsf', 'wsh', 'xll', 'xz', 'z') diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardMessageExpiration.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardMessageExpiration.ps1 index fcbf37358bd4..af905f43d0a3 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardMessageExpiration.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardMessageExpiration.ps1 @@ -36,7 +36,14 @@ function Invoke-CIPPStandardMessageExpiration { } #we're done. ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'MessageExpiration' - $MessageExpiration = (New-ExoRequest -tenantid $Tenant -cmdlet 'Get-TransportConfig').messageExpiration + try { + $MessageExpiration = (New-ExoRequest -tenantid $Tenant -cmdlet 'Get-TransportConfig').messageExpiration + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the MessageExpiration state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } if ($Settings.remediate -eq $true) { Write-Host 'Time to remediate' diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardOauthConsent.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardOauthConsent.ps1 index 9d00b6a527e7..d18f6fb68a3f 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardOauthConsent.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardOauthConsent.ps1 @@ -33,7 +33,14 @@ function Invoke-CIPPStandardOauthConsent { param($tenant, $settings) - $State = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/authorizationPolicy/authorizationPolicy' -tenantid $tenant + try { + $State = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/authorizationPolicy/authorizationPolicy' -tenantid $tenant + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the OauthConsent state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $StateIsCorrect = if ($State.permissionGrantPolicyIdsAssignedToDefaultUserRole -eq 'managePermissionGrantsForSelf.cipp-consent-policy') { $true } else { $false } if ($Settings.remediate -eq $true) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardOauthConsentLowSec.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardOauthConsentLowSec.ps1 index 8338bcf4b108..22c465de76a7 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardOauthConsentLowSec.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardOauthConsentLowSec.ps1 @@ -29,8 +29,17 @@ function Invoke-CIPPStandardOauthConsentLowSec { param($Tenant, $Settings) - $State = (New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/authorizationPolicy/authorizationPolicy' -tenantid $tenant) - $PermissionState = (New-GraphGetRequest -Uri "https://graph.microsoft.com/beta/servicePrincipals(appId='00000003-0000-0000-c000-000000000000')/delegatedPermissionClassifications" -tenantid $tenant) | Select-Object -Property permissionName + try { + $State = (New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/authorizationPolicy/authorizationPolicy' -tenantid $tenant) + + $PermissionState = (New-GraphGetRequest -Uri "https://graph.microsoft.com/beta/servicePrincipals(appId='00000003-0000-0000-c000-000000000000')/delegatedPermissionClassifications" -tenantid $tenant) | + Select-Object -Property permissionName + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the OauthConsentLowSec state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $requiredPermissions = @('offline_access', 'openid', 'User.Read', 'profile', 'email') $missingPermissions = $requiredPermissions | Where-Object { $PermissionState.permissionName -notcontains $_ } diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardOutBoundSpamAlert.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardOutBoundSpamAlert.ps1 index 5216f08af174..51049bddc07d 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardOutBoundSpamAlert.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardOutBoundSpamAlert.ps1 @@ -38,7 +38,14 @@ function Invoke-CIPPStandardOutBoundSpamAlert { return $true } #we're done. - $CurrentInfo = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-HostedOutboundSpamFilterPolicy' -cmdParams @{ Identity = 'Default' } -useSystemMailbox $true + try { + $CurrentInfo = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-HostedOutboundSpamFilterPolicy' -cmdParams @{ Identity = 'Default' } -useSystemMailbox $true + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the OutBoundSpamAlert state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } if ($Settings.remediate -eq $true) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPWcompanionAppAllowedState.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPWcompanionAppAllowedState.ps1 index a52afe311852..7497a5e5e797 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPWcompanionAppAllowedState.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPWcompanionAppAllowedState.ps1 @@ -30,7 +30,14 @@ function Invoke-CIPPStandardPWcompanionAppAllowedState { param($Tenant, $Settings) - $AuthenticatorFeaturesState = (New-GraphGetRequest -tenantid $Tenant -Uri 'https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy/authenticationMethodConfigurations/microsoftAuthenticator') + try { + $AuthenticatorFeaturesState = (New-GraphGetRequest -tenantid $Tenant -Uri 'https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy/authenticationMethodConfigurations/microsoftAuthenticator') + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the PWcompanionAppAllowedState state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } # Get state value using null-coalescing operator $CurrentState = $AuthenticatorFeaturesState.featureSettings.companionAppAllowedState.state diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPWdisplayAppInformationRequiredState.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPWdisplayAppInformationRequiredState.ps1 index 7dacd622acbd..f8616c61c62f 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPWdisplayAppInformationRequiredState.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPWdisplayAppInformationRequiredState.ps1 @@ -31,7 +31,15 @@ function Invoke-CIPPStandardPWdisplayAppInformationRequiredState { param($Tenant, $Settings) - $CurrentState = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy/authenticationMethodConfigurations/microsoftAuthenticator' -tenantid $Tenant + try { + $CurrentState = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy/authenticationMethodConfigurations/microsoftAuthenticator' -tenantid $Tenant + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the PWdisplayAppInformationRequiredState state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } + $StateIsCorrect = ($CurrentState.state -eq 'enabled') -and ($CurrentState.featureSettings.numberMatchingRequiredState.state -eq 'enabled') -and ($CurrentState.featureSettings.displayAppInformationRequiredState.state -eq 'enabled') diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPasswordExpireDisabled.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPasswordExpireDisabled.ps1 index a801f3a32244..a540deb8c0cc 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPasswordExpireDisabled.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPasswordExpireDisabled.ps1 @@ -33,7 +33,15 @@ function Invoke-CIPPStandardPasswordExpireDisabled { param($Tenant, $Settings) - $GraphRequest = New-GraphGetRequest -uri 'https://graph.microsoft.com/v1.0/domains' -tenantid $Tenant + try { + $GraphRequest = New-GraphGetRequest -uri 'https://graph.microsoft.com/v1.0/domains' -tenantid $Tenant + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the PasswordExpireDisabled state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } + $DomainsWithoutPassExpire = $GraphRequest | Where-Object { $_.isVerified -eq $true -and $_.passwordValidityPeriodInDays -ne 2147483647 } diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPerUserMFA.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPerUserMFA.ps1 index a9cb8cf01c4e..2dc4d907d921 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPerUserMFA.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPerUserMFA.ps1 @@ -29,7 +29,14 @@ function Invoke-CIPPStandardPerUserMFA { param($Tenant, $Settings) - $GraphRequest = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/users?`$top=999&`$select=userPrincipalName,displayName,accountEnabled,perUserMfaState&`$filter=userType eq 'Member' and accountEnabled eq true and displayName ne 'On-Premises Directory Synchronization Service Account'&`$count=true" -tenantid $Tenant -ComplexFilter + try { + $GraphRequest = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/users?`$top=999&`$select=userPrincipalName,displayName,accountEnabled,perUserMfaState&`$filter=userType eq 'Member' and accountEnabled eq true and displayName ne 'On-Premises Directory Synchronization Service Account'&`$count=true" -tenantid $Tenant -ComplexFilter + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the PerUserMFA state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $UsersWithoutMFA = $GraphRequest | Where-Object -Property perUserMfaState -NE 'enforced' | Select-Object -Property userPrincipalName, displayName, accountEnabled, perUserMfaState If ($Settings.remediate -eq $true) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPhishSimSpoofIntelligence.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPhishSimSpoofIntelligence.ps1 index d385120c7449..1cf8c915b826 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPhishSimSpoofIntelligence.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPhishSimSpoofIntelligence.ps1 @@ -37,8 +37,15 @@ function Invoke-CIPPStandardPhishSimSpoofIntelligence { return $true } #we're done. # Fetch current Phishing Simulations Spoof Intelligence domains and ensure it is correctly configured - $DomainState = New-ExoRequest -TenantId $Tenant -cmdlet 'Get-TenantAllowBlockListSpoofItems' | - Select-Object -Property Identity,SendingInfrastructure + try { + $DomainState = New-ExoRequest -TenantId $Tenant -cmdlet 'Get-TenantAllowBlockListSpoofItems' | + Select-Object -Property Identity, SendingInfrastructure + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the PhishSimSpoofIntelligence state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } [String[]]$AddDomain = $Settings.AllowedDomains.value | Where-Object { $_ -notin $DomainState.SendingInfrastructure } diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPhishingSimulations.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPhishingSimulations.ps1 index 6b4b4005846f..0fa3af87bf61 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPhishingSimulations.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardPhishingSimulations.ps1 @@ -41,9 +41,16 @@ function Invoke-CIPPStandardPhishingSimulations { $PolicyName = 'CIPPPhishSim' # Fetch current Phishing Simulations Policy settings and ensure it is correctly configured - $PolicyState = New-ExoRequest -TenantId $Tenant -cmdlet 'Get-PhishSimOverridePolicy' | - Where-Object -Property Name -EQ 'PhishSimOverridePolicy' | - Select-Object -Property Identity,Name,Mode,Enabled + try { + $PolicyState = New-ExoRequest -TenantId $Tenant -cmdlet 'Get-PhishSimOverridePolicy' | + Where-Object -Property Name -EQ 'PhishSimOverridePolicy' | + Select-Object -Property Identity, Name, Mode, Enabled + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the PhishingSimulations state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $PolicyIsCorrect = ($PolicyState.Name -eq 'PhishSimOverridePolicy') -and ($PolicyState.Enabled -eq $true) diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardProfilePhotos.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardProfilePhotos.ps1 index 58ced8cd5211..979f690a1222 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardProfilePhotos.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardProfilePhotos.ps1 @@ -49,8 +49,15 @@ function Invoke-CIPPStandardProfilePhotos { $DesiredState = $StateValue -eq 'enabled' # Get current Graph policy state - $Uri = 'https://graph.microsoft.com/beta/admin/people/photoUpdateSettings' - $CurrentGraphState = New-GraphGetRequest -uri $Uri -tenantid $Tenant + try { + $Uri = 'https://graph.microsoft.com/beta/admin/people/photoUpdateSettings' + $CurrentGraphState = New-GraphGetRequest -uri $Uri -tenantid $Tenant + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the ProfilePhotos state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $UsersCanChangePhotos = if ([string]::IsNullOrWhiteSpace($CurrentGraphState.allowedRoles) ) { $true } else { $false } $GraphStateCorrect = $UsersCanChangePhotos -eq $DesiredState diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardQuarantineRequestAlert.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardQuarantineRequestAlert.ps1 index 9fd1b7426f98..9a61e0478eb2 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardQuarantineRequestAlert.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardQuarantineRequestAlert.ps1 @@ -37,9 +37,16 @@ function Invoke-CIPPStandardQuarantineRequestAlert { } #we're done. $PolicyName = 'CIPP User requested to release a quarantined message' - $CurrentState = New-ExoRequest -TenantId $Tenant -cmdlet 'Get-ProtectionAlert' -Compliance | - Where-Object { $_.Name -eq $PolicyName } | - Select-Object -Property * + try { + $CurrentState = New-ExoRequest -TenantId $Tenant -cmdlet 'Get-ProtectionAlert' -Compliance | + Where-Object { $_.Name -eq $PolicyName } | + Select-Object -Property * + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the QuarantineRequestAlert state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $StateIsCorrect = ($CurrentState.NotifyUser -contains $Settings.NotifyUser) diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardRetentionPolicyTag.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardRetentionPolicyTag.ps1 index cc96f1cb4053..a1135e0dc558 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardRetentionPolicyTag.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardRetentionPolicyTag.ps1 @@ -37,11 +37,19 @@ function Invoke-CIPPStandardRetentionPolicyTag { } #we're done. $PolicyName = 'CIPP Deleted Items' - $CurrentState = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-RetentionPolicyTag' | - Where-Object -Property Identity -EQ $PolicyName - $PolicyState = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-RetentionPolicy' | - Where-Object -Property Identity -EQ 'Default MRM Policy' + try { + $CurrentState = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-RetentionPolicyTag' | + Where-Object -Property Identity -EQ $PolicyName + + $PolicyState = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-RetentionPolicy' | + Where-Object -Property Identity -EQ 'Default MRM Policy' + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the RetentionPolicy state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $StateIsCorrect = ($CurrentState.Name -eq $PolicyName) -and ($CurrentState.RetentionEnabled -eq $true) -and diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardRotateDKIM.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardRotateDKIM.ps1 index d99d8e2800e3..4756d667c395 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardRotateDKIM.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardRotateDKIM.ps1 @@ -38,7 +38,14 @@ function Invoke-CIPPStandardRotateDKIM { return $true } #we're done. - $DKIM = (New-ExoRequest -tenantid $tenant -cmdlet 'Get-DkimSigningConfig') | Where-Object { $_.Selector1KeySize -eq 1024 -and $_.Enabled -eq $true } + try { + $DKIM = (New-ExoRequest -tenantid $tenant -cmdlet 'Get-DkimSigningConfig') | Where-Object { $_.Selector1KeySize -eq 1024 -and $_.Enabled -eq $true } + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the DKIM state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } if ($Settings.remediate -eq $true) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPAzureB2B.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPAzureB2B.ps1 index b27311d068a9..7112e90148c1 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPAzureB2B.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPAzureB2B.ps1 @@ -37,8 +37,15 @@ function Invoke-CIPPStandardSPAzureB2B { return $true } #we're done. - $CurrentState = Get-CIPPSPOTenant -TenantFilter $Tenant | + try { + $CurrentState = Get-CIPPSPOTenant -TenantFilter $Tenant | Select-Object -Property _ObjectIdentity_, TenantFilter, EnableAzureADB2BIntegration + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the SPAzureB2B state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $StateIsCorrect = ($CurrentState.EnableAzureADB2BIntegration -eq $true) diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPDirectSharing.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPDirectSharing.ps1 index 08e3e1ca1c81..b30e945af564 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPDirectSharing.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPDirectSharing.ps1 @@ -40,8 +40,15 @@ function Invoke-CIPPStandardSPDirectSharing { Write-LogMessage -API 'Standards' -Tenant $Tenant -Message 'The default sharing to Direct users standard has been deprecated in favor of the "Set Default Sharing Link Settings" standard. Please update your standards to use new standard. However this will continue to function.' -Sev Alert - $CurrentState = Get-CIPPSPOTenant -TenantFilter $Tenant | - Select-Object -Property _ObjectIdentity_, TenantFilter, DefaultSharingLinkType + try { + $CurrentState = Get-CIPPSPOTenant -TenantFilter $Tenant | + Select-Object -Property _ObjectIdentity_, TenantFilter, DefaultSharingLinkType + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the SPDirectSharing state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $StateIsCorrect = ($CurrentState.DefaultSharingLinkType -eq 'Direct' -or $CurrentState.DefaultSharingLinkType -eq 1) diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPDisableLegacyWorkflows.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPDisableLegacyWorkflows.ps1 index ae96250dba3e..59d08d079528 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPDisableLegacyWorkflows.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPDisableLegacyWorkflows.ps1 @@ -34,8 +34,15 @@ function Invoke-CIPPStandardSPDisableLegacyWorkflows { return $true } #we're done. - $CurrentState = Get-CIPPSPOTenant -TenantFilter $Tenant | + try { + $CurrentState = Get-CIPPSPOTenant -TenantFilter $Tenant | Select-Object -Property * + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the SPDisableLegacyWorkflows state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $StateIsCorrect = ($CurrentState.StopNew2010Workflows -eq $true) -and ($CurrentState.StopNew2013Workflows -eq $true) -and diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPDisallowInfectedFiles.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPDisallowInfectedFiles.ps1 index 812f35819c74..f1883357e1c3 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPDisallowInfectedFiles.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPDisallowInfectedFiles.ps1 @@ -38,8 +38,15 @@ function Invoke-CIPPStandardSPDisallowInfectedFiles { return $true } #we're done. - $CurrentState = Get-CIPPSPOTenant -TenantFilter $Tenant | + try { + $CurrentState = Get-CIPPSPOTenant -TenantFilter $Tenant | Select-Object -Property _ObjectIdentity_, TenantFilter, DisallowInfectedFileDownload + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the SPDisallowInfectedFiles state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $StateIsCorrect = ($CurrentState.DisallowInfectedFileDownload -eq $true) diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPEmailAttestation.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPEmailAttestation.ps1 index 1445b22f2723..b76fb5939521 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPEmailAttestation.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPEmailAttestation.ps1 @@ -39,7 +39,15 @@ function Invoke-CIPPStandardSPEmailAttestation { return $true } #we're done. - $CurrentState = Get-CIPPSPOTenant -TenantFilter $Tenant | Select-Object -Property _ObjectIdentity_, TenantFilter, EmailAttestationReAuthDays, EmailAttestationRequired + try { + $CurrentState = Get-CIPPSPOTenant -TenantFilter $Tenant | + Select-Object -Property _ObjectIdentity_, TenantFilter, EmailAttestationReAuthDays, EmailAttestationRequired + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the SPEmailAttestation state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $StateIsCorrect = ($CurrentState.EmailAttestationReAuthDays -eq [int]$Settings.Days) -and ($CurrentState.EmailAttestationRequired -eq $true) diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPExternalUserExpiration.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPExternalUserExpiration.ps1 index 27d90d437ef3..99f563cab686 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPExternalUserExpiration.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPExternalUserExpiration.ps1 @@ -38,8 +38,15 @@ function Invoke-CIPPStandardSPExternalUserExpiration { return $true } #we're done. - $CurrentState = Get-CIPPSPOTenant -TenantFilter $Tenant | + try { + $CurrentState = Get-CIPPSPOTenant -TenantFilter $Tenant | Select-Object -Property _ObjectIdentity_, TenantFilter, ExternalUserExpireInDays, ExternalUserExpirationRequired + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the SPExternalUserExpiration state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $StateIsCorrect = ($CurrentState.ExternalUserExpireInDays -eq $Settings.Days) -and ($CurrentState.ExternalUserExpirationRequired -eq $true) diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPFileRequests.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPFileRequests.ps1 new file mode 100644 index 000000000000..6290bc0ed0f3 --- /dev/null +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPFileRequests.ps1 @@ -0,0 +1,134 @@ +function Invoke-CIPPStandardSPFileRequests { + <# + .FUNCTIONALITY + Internal + .COMPONENT + (APIName) SPFileRequests + .SYNOPSIS + (Label) Set SharePoint and OneDrive File Requests + .DESCRIPTION + (Helptext) Enables or disables File Requests for SharePoint and OneDrive, allowing users to create secure upload-only links. Optionally sets the maximum number of days for the link to remain active. + (DocsDescription) File Requests allow users to create secure upload-only share links where uploads are hidden from other people using the link. This creates a secure and private way for people to upload files to a folder. This standard enables or disables this feature and optionally configures link expiration settings. + .NOTES + CAT + SharePoint Standards + TAG + ADDEDCOMPONENT + {"type":"switch","name":"standards.SPFileRequests.state","label":"Enable File Requests"} + {"type":"number","name":"standards.SPFileRequests.expirationDays","label":"Link Expiration Days (Optional - 1-730)","required":false} + IMPACT + Medium Impact + ADDEDDATE + 2025-07-30 + POWERSHELLEQUIVALENT + Set-SPOTenant -CoreRequestFilesLinkEnabled \$true -OneDriveRequestFilesLinkEnabled \$true -CoreRequestFilesLinkExpirationInDays 30 -OneDriveRequestFilesLinkExpirationInDays 30 + RECOMMENDEDBY + "CIPP" + UPDATECOMMENTBLOCK + Run the Tools\Update-StandardsComments.ps1 script to update this comment block + .LINK + https://docs.cipp.app/user-documentation/tenant/standards/list-standards + #> + + param($Tenant, $Settings) + $TestResult = Test-CIPPStandardLicense -StandardName 'SPFileRequests' -TenantFilter $Tenant -RequiredCapabilities @('SHAREPOINTWAC', 'SHAREPOINTSTANDARD', 'SHAREPOINTENTERPRISE', 'ONEDRIVE_BASIC', 'ONEDRIVE_ENTERPRISE') + + if ($TestResult -eq $false) { + Write-LogMessage -API 'Standards' -tenant $tenant -message 'The tenant is not licenced for this standard SPFileRequests' -sev Error + return $true + } + + try { + $CurrentState = Get-CIPPSPOTenant -TenantFilter $Tenant | Select-Object _ObjectIdentity_, TenantFilter, CoreRequestFilesLinkEnabled, OneDriveRequestFilesLinkEnabled, CoreRequestFilesLinkExpirationInDays, OneDriveRequestFilesLinkExpirationInDays + } + catch { + Write-LogMessage -API 'Standards' -tenant $tenant -message 'Failed to get current state of SPO tenant details' -sev Error + return + } + + # Input validation + if (($Settings.state -eq $null) -and ($Settings.remediate -eq $true -or $Settings.alert -eq $true)) { + Write-LogMessage -API 'Standards' -tenant $tenant -message 'Invalid state parameter set for standard SPFileRequests' -sev Error + return + } + + $WantedState = $Settings.state + $ExpirationDays = $Settings.expirationDays + $HumanReadableState = if ($WantedState -eq $true) { 'enabled' } else { 'disabled' } + + # Check if current state matches desired state + $CoreStateIsCorrect = if ($CurrentState.CoreRequestFilesLinkEnabled -eq $WantedState) { $true } else { $false } + $OneDriveStateIsCorrect = if ($CurrentState.OneDriveRequestFilesLinkEnabled -eq $WantedState) { $true } else { $false } + $StateIsCorrect = $CoreStateIsCorrect -and $OneDriveStateIsCorrect + + # Check expiration settings if specified + $ExpirationIsCorrect = $true + if ($ExpirationDays -ne $null -and $WantedState -eq $true) { + $CoreExpirationIsCorrect = ($CurrentState.CoreRequestFilesLinkExpirationInDays -eq $ExpirationDays) + $OneDriveExpirationIsCorrect = ($CurrentState.OneDriveRequestFilesLinkExpirationInDays -eq $ExpirationDays) + $ExpirationIsCorrect = $CoreExpirationIsCorrect -and $OneDriveExpirationIsCorrect + } + + $AllSettingsCorrect = $StateIsCorrect -and $ExpirationIsCorrect + + if ($Settings.remediate -eq $true) { + + if ($AllSettingsCorrect -eq $false) { + try { + $Properties = @{ + CoreRequestFilesLinkEnabled = $WantedState + OneDriveRequestFilesLinkEnabled = $WantedState + } + + # Add expiration settings if specified and feature is being enabled + if ($ExpirationDays -ne $null -and $WantedState -eq $true) { + $Properties['CoreRequestFilesLinkExpirationInDays'] = $ExpirationDays + $Properties['OneDriveRequestFilesLinkExpirationInDays'] = $ExpirationDays + } + + $CurrentState | Set-CIPPSPOTenant -Properties $Properties + + $ExpirationMessage = if ($ExpirationDays -ne $null -and $WantedState -eq $true) { " with $ExpirationDays day expiration" } else { "" } + Write-LogMessage -API 'Standards' -tenant $tenant -message "Successfully set File Requests to $HumanReadableState$ExpirationMessage" -sev Info + } catch { + $ErrorMessage = Get-CippException -Exception $_ + Write-LogMessage -API 'Standards' -tenant $tenant -message "Failed to set File Requests to $HumanReadableState. Error: $($ErrorMessage.NormalizedError)" -sev Error -LogData $ErrorMessage + } + } else { + $ExpirationMessage = if ($ExpirationDays -ne $null -and $WantedState -eq $true) { " with $ExpirationDays day expiration" } else { "" } + Write-LogMessage -API 'Standards' -tenant $tenant -message "File Requests are already set to the wanted state of $HumanReadableState$ExpirationMessage" -sev Info + } + } + + if ($Settings.alert -eq $true) { + if ($AllSettingsCorrect -eq $true) { + $ExpirationMessage = if ($ExpirationDays -ne $null -and $WantedState -eq $true) { " with $ExpirationDays day expiration" } else { "" } + Write-LogMessage -API 'Standards' -tenant $tenant -message "File Requests are already set to the wanted state of $HumanReadableState$ExpirationMessage" -sev Info + } else { + $AlertMessage = "File Requests are not set to the wanted state of $HumanReadableState" + if ($ExpirationDays -ne $null -and $WantedState -eq $true) { + $AlertMessage += " with $ExpirationDays day expiration" + } + Write-StandardsAlert -message $AlertMessage -object $CurrentState -tenant $tenant -standardName 'SPFileRequests' -standardId $Settings.standardId + Write-LogMessage -API 'Standards' -tenant $tenant -message $AlertMessage -sev Info + } + } + + if ($Settings.report -eq $true) { + Add-CIPPBPAField -FieldName 'SPFileRequestsEnabled' -FieldValue $StateIsCorrect -StoreAs bool -Tenant $Tenant + Add-CIPPBPAField -FieldName 'SPCoreFileRequestsEnabled' -FieldValue $CurrentState.CoreRequestFilesLinkEnabled -StoreAs bool -Tenant $Tenant + Add-CIPPBPAField -FieldName 'SPOneDriveFileRequestsEnabled' -FieldValue $CurrentState.OneDriveRequestFilesLinkEnabled -StoreAs bool -Tenant $Tenant + + if ($ExpirationDays -ne $null) { + Add-CIPPBPAField -FieldName 'SPCoreFileRequestsExpirationDays' -FieldValue $CurrentState.CoreRequestFilesLinkExpirationInDays -StoreAs string -Tenant $Tenant + Add-CIPPBPAField -FieldName 'SPOneDriveFileRequestsExpirationDays' -FieldValue $CurrentState.OneDriveRequestFilesLinkExpirationInDays -StoreAs string -Tenant $Tenant + } + + if ($AllSettingsCorrect) { + $FieldValue = $true + } else { + $FieldValue = $CurrentState + } + Set-CIPPStandardsCompareField -FieldName 'standards.SPFileRequests' -FieldValue $FieldValue -Tenant $Tenant + } +} diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPSyncButtonState.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPSyncButtonState.ps1 index f8a10d66b685..279deaac3ba5 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPSyncButtonState.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSPSyncButtonState.ps1 @@ -36,7 +36,14 @@ function Invoke-CIPPStandardSPSyncButtonState { return $true } #we're done. - $CurrentState = Get-CIPPSPOTenant -TenantFilter $Tenant | Select-Object _ObjectIdentity_, TenantFilter, HideSyncButtonOnDocLib + try { + $CurrentState = Get-CIPPSPOTenant -TenantFilter $Tenant | Select-Object _ObjectIdentity_, TenantFilter, HideSyncButtonOnDocLib + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the SPSyncButtonState state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } # Input validation diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSafeAttachmentPolicy.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSafeAttachmentPolicy.ps1 index 0909e492eae6..f3f0dc8c5ce5 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSafeAttachmentPolicy.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSafeAttachmentPolicy.ps1 @@ -64,9 +64,16 @@ function Invoke-CIPPStandardSafeAttachmentPolicy { $RuleName = $ExistingRule.Name } - $CurrentState = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-SafeAttachmentPolicy' | - Where-Object -Property Name -EQ $PolicyName | - Select-Object Name, Enable, Action, QuarantineTag, Redirect, RedirectAddress + try { + $CurrentState = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-SafeAttachmentPolicy' | + Where-Object -Property Name -EQ $PolicyName | + Select-Object Name, Enable, Action, QuarantineTag, Redirect, RedirectAddress + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the SafeAttachmentPolicy state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $StateIsCorrect = ($CurrentState.Name -eq $PolicyName) -and ($CurrentState.Enable -eq $true) -and diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSafeLinksPolicy.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSafeLinksPolicy.ps1 index 28fab38d23fe..6ae32ddd6a39 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSafeLinksPolicy.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSafeLinksPolicy.ps1 @@ -63,9 +63,16 @@ function Invoke-CIPPStandardSafeLinksPolicy { $RuleName = $ExistingRule.Name } - $CurrentState = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-SafeLinksPolicy' | + try { + $CurrentState = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-SafeLinksPolicy' | Where-Object -Property Name -EQ $PolicyName | Select-Object Name, EnableSafeLinksForEmail, EnableSafeLinksForTeams, EnableSafeLinksForOffice, TrackClicks, AllowClickThrough, ScanUrls, EnableForInternalSenders, DeliverMessageAfterScan, DisableUrlRewrite, EnableOrganizationBranding, DoNotRewriteUrls + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the SafeLinksPolicy state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $StateIsCorrect = ($CurrentState.Name -eq $PolicyName) -and ($CurrentState.EnableSafeLinksForEmail -eq $true) -and diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSecurityDefaults.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSecurityDefaults.ps1 index a8f7552c87c5..5a9133fa35b5 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSecurityDefaults.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSecurityDefaults.ps1 @@ -29,7 +29,14 @@ function Invoke-CIPPStandardSecurityDefaults { param($Tenant, $Settings) - $SecureDefaultsState = (New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/identitySecurityDefaultsEnforcementPolicy' -tenantid $tenant) + try { + $SecureDefaultsState = (New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/identitySecurityDefaultsEnforcementPolicy' -tenantid $tenant) + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the Security Defaults state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } if ($Settings.remediate -eq $true) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSendFromAlias.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSendFromAlias.ps1 index 4b8e561fb1a1..28692ae363c5 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSendFromAlias.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSendFromAlias.ps1 @@ -36,7 +36,14 @@ function Invoke-CIPPStandardSendFromAlias { return $true } #we're done. - $CurrentInfo = (New-ExoRequest -tenantid $Tenant -cmdlet 'Get-OrganizationConfig').SendFromAliasEnabled + try { + $CurrentInfo = (New-ExoRequest -tenantid $Tenant -cmdlet 'Get-OrganizationConfig').SendFromAliasEnabled + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the SendFromAlias state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } if ($Settings.remediate -eq $true) { if ($CurrentInfo -ne $true) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSendReceiveLimitTenant.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSendReceiveLimitTenant.ps1 index 2ee64931533e..958e822a9c9d 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSendReceiveLimitTenant.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSendReceiveLimitTenant.ps1 @@ -49,8 +49,16 @@ function Invoke-CIPPStandardSendReceiveLimitTenant { return } + try { + $AllMailBoxPlans = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-MailboxPlan' | + Select-Object DisplayName, MaxSendSize, MaxReceiveSize, GUID + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the SendReceiveLimitTenant state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } - $AllMailBoxPlans = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-MailboxPlan' | Select-Object DisplayName, MaxSendSize, MaxReceiveSize, GUID $MaxSendSize = [int64]"$($Settings.SendLimit)MB" $MaxReceiveSize = [int64]"$($Settings.ReceiveLimit)MB" diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSharePointMassDeletionAlert.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSharePointMassDeletionAlert.ps1 index b0193ee91b5c..c210a783def6 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSharePointMassDeletionAlert.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSharePointMassDeletionAlert.ps1 @@ -40,9 +40,16 @@ function Invoke-CIPPStandardSharePointMassDeletionAlert { $PolicyName = 'CIPP SharePoint mass deletion of files by a user' - $CurrentState = New-ExoRequest -TenantId $Tenant -cmdlet 'Get-ProtectionAlert' -Compliance | - Where-Object { $_.Name -eq $PolicyName } | - Select-Object -Property * + try { + $CurrentState = New-ExoRequest -TenantId $Tenant -cmdlet 'Get-ProtectionAlert' -Compliance | + Where-Object { $_.Name -eq $PolicyName } | + Select-Object -Property * + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the sharingCapability state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $EmailsOutsideSettings = $CurrentState.NotifyUser | Where-Object { $_ -notin $Settings.NotifyUser.value } $MissingEmailsInSettings = $Settings.NotifyUser.value | Where-Object { $_ -notin $CurrentState.NotifyUser } diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardShortenMeetings.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardShortenMeetings.ps1 index 28d40a8e3881..2d567060ab95 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardShortenMeetings.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardShortenMeetings.ps1 @@ -41,7 +41,16 @@ function Invoke-CIPPStandardShortenMeetings { # Get state value using null-coalescing operator $scopeDefault = $Settings.ShortenEventScopeDefault.value ? $Settings.ShortenEventScopeDefault.value : $Settings.ShortenEventScopeDefault - $CurrentState = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-OrganizationConfig' | Select-Object -Property ShortenEventScopeDefault, DefaultMinutesToReduceShortEventsBy, DefaultMinutesToReduceLongEventsBy + try { + $CurrentState = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-OrganizationConfig' | + Select-Object -Property ShortenEventScopeDefault, DefaultMinutesToReduceShortEventsBy, DefaultMinutesToReduceLongEventsBy + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the ShortenMeetings state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } + $CorrectState = if ($CurrentState.ShortenEventScopeDefault -eq $scopeDefault -and $CurrentState.DefaultMinutesToReduceShortEventsBy -eq $Settings.DefaultMinutesToReduceShortEventsBy -and $CurrentState.DefaultMinutesToReduceLongEventsBy -eq $Settings.DefaultMinutesToReduceLongEventsBy) { $true } else { $false } diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSpamFilterPolicy.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSpamFilterPolicy.ps1 index 485c1b7fb475..3b811a9a712e 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSpamFilterPolicy.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSpamFilterPolicy.ps1 @@ -60,7 +60,16 @@ function Invoke-CIPPStandardSpamFilterPolicy { $PolicyName = 'CIPP Default Spam Filter Policy' - $CurrentState = New-ExoRequest -TenantId $Tenant -cmdlet 'Get-HostedContentFilterPolicy' | Where-Object -Property Name -EQ $PolicyName | Select-Object -Property * + try { + $CurrentState = New-ExoRequest -TenantId $Tenant -cmdlet 'Get-HostedContentFilterPolicy' | + Where-Object -Property Name -EQ $PolicyName | + Select-Object -Property * + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the SpamFilterPolicy state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $SpamAction = $Settings.SpamAction.value ?? $Settings.SpamAction $SpamQuarantineTag = $Settings.SpamQuarantineTag.value ?? $Settings.SpamQuarantineTag diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSpoofWarn.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSpoofWarn.ps1 index 9bc4b571f543..15a49616c884 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSpoofWarn.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardSpoofWarn.ps1 @@ -40,7 +40,14 @@ function Invoke-CIPPStandardSpoofWarn { return $true } #we're done. - $CurrentInfo = (New-ExoRequest -tenantid $Tenant -cmdlet 'Get-ExternalInOutlook') + try { + $CurrentInfo = (New-ExoRequest -tenantid $Tenant -cmdlet 'Get-ExternalInOutlook') + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the SpoofWarn state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } # Get state value using null-coalescing operator $state = $Settings.state.value ?? $Settings.state @@ -48,16 +55,22 @@ function Invoke-CIPPStandardSpoofWarn { # Test if all entries in the AllowListAdd variable are in the AllowList $AllowListCorrect = $true - $AllowListAddEntries = foreach ($entry in $AllowListAdd) { - if ($CurrentInfo.AllowList -notcontains $entry) { - $AllowListCorrect = $false - Write-Host "AllowList entry $entry not found in current AllowList" - $entry - } else { - Write-Host "AllowList entry $entry found in current AllowList." + + if ($AllowListAdd -eq $null -or $AllowListAdd.Count -eq 0) { + Write-Host 'No AllowList entries provided, skipping AllowList check.' + $AllowListAdd = @{'@odata.type' = '#Exchange.GenericHashTable'; Add = @() } + } else { + $AllowListAddEntries = foreach ($entry in $AllowListAdd) { + if ($CurrentInfo.AllowList -notcontains $entry) { + $AllowListCorrect = $false + Write-Host "AllowList entry $entry not found in current AllowList" + $entry + } else { + Write-Host "AllowList entry $entry found in current AllowList." + } } + $AllowListAdd = @{'@odata.type' = '#Exchange.GenericHashTable'; Add = $AllowListAddEntries } } - $AllowListAdd = @{'@odata.type' = '#Exchange.GenericHashTable'; Add = $AllowListAddEntries } # Debug output # Write-Host ($CurrentInfo | ConvertTo-Json -Depth 10) diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardStaleEntraDevices.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardStaleEntraDevices.ps1 index 6b648dc70556..ddc412de1fb9 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardStaleEntraDevices.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardStaleEntraDevices.ps1 @@ -40,7 +40,16 @@ function Invoke-CIPPStandardStaleEntraDevices { Write-Host "We're exiting as the correct license is not present for this standard." return $true } #we're done. - $AllDevices = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/devices' -tenantid $Tenant | Where-Object { $null -ne $_.approximateLastSignInDateTime } + + try { + $AllDevices = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/devices' -tenantid $Tenant | Where-Object { $null -ne $_.approximateLastSignInDateTime } + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the StaleEntraDevices state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } + $Date = (Get-Date).AddDays( - [int]$Settings.deviceAgeThreshold) $StaleDevices = $AllDevices | Where-Object { $_.approximateLastSignInDateTime -lt $Date } diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTAP.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTAP.ps1 index 22343488189b..734edf0f0ca8 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTAP.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTAP.ps1 @@ -32,7 +32,14 @@ function Invoke-CIPPStandardTAP { param($Tenant, $Settings) ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'TAP' - $CurrentState = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/authenticationmethodspolicy/authenticationMethodConfigurations/TemporaryAccessPass' -tenantid $Tenant + try { + $CurrentState = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/policies/authenticationmethodspolicy/authenticationMethodConfigurations/TemporaryAccessPass' -tenantid $Tenant + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the TAP state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } # Get config value using null-coalescing operator $config = $Settings.config.value ?? $Settings.config diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsEmailIntegration.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsEmailIntegration.ps1 index 9fdb94e267f6..8a4e6e7e6dfe 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsEmailIntegration.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsEmailIntegration.ps1 @@ -38,11 +38,19 @@ Function Invoke-CIPPStandardTeamsEmailIntegration { return $true } #we're done. - $CurrentState = New-TeamsRequest -TenantFilter $Tenant -Cmdlet 'Get-CsTeamsClientConfiguration' -CmdParams @{Identity = 'Global' } | Select-Object AllowEmailIntoChannel + try { + $CurrentState = New-TeamsRequest -TenantFilter $Tenant -Cmdlet 'Get-CsTeamsClientConfiguration' -CmdParams @{Identity = 'Global' } | + Select-Object AllowEmailIntoChannel + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the TeamsEmailIntegration state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } - if ($null -eq $Settings.AllowEmailIntoChannel) { $Settings.AllowEmailIntoChannel = $false } + $AllowEmailIntoChannel = $Settings.AllowEmailIntoChannel ?? $false - $StateIsCorrect = ($CurrentState.AllowEmailIntoChannel -eq $Settings.AllowEmailIntoChannel) + $StateIsCorrect = ($CurrentState.AllowEmailIntoChannel -eq $AllowEmailIntoChannel) if ($Settings.remediate -eq $true) { if ($StateIsCorrect -eq $true) { @@ -50,7 +58,7 @@ Function Invoke-CIPPStandardTeamsEmailIntegration { } else { $cmdParams = @{ Identity = 'Global' - AllowEmailIntoChannel = $Settings.AllowEmailIntoChannel + AllowEmailIntoChannel = $AllowEmailIntoChannel } try { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsEnrollUser.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsEnrollUser.ps1 index b4c5789fdd2f..341f97464d25 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsEnrollUser.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsEnrollUser.ps1 @@ -39,7 +39,15 @@ function Invoke-CIPPStandardTeamsEnrollUser { } #we're done. $enrollUserOverride = $Settings.EnrollUserOverride.value ?? $Settings.EnrollUserOverride - $CurrentState = New-TeamsRequest -TenantFilter $Tenant -Cmdlet 'Get-CsTeamsMeetingPolicy' -cmdParams @{Identity = 'Global' } | Select-Object EnrollUserOverride + try { + $CurrentState = New-TeamsRequest -TenantFilter $Tenant -Cmdlet 'Get-CsTeamsMeetingPolicy' -cmdParams @{Identity = 'Global' } | + Select-Object EnrollUserOverride + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the TeamsEnrollUser state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $StateIsCorrect = ($CurrentState.EnrollUserOverride -eq $enrollUserOverride) diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsExternalAccessPolicy.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsExternalAccessPolicy.ps1 index 905d098d093e..b9a4c91bbca7 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsExternalAccessPolicy.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsExternalAccessPolicy.ps1 @@ -38,7 +38,15 @@ function Invoke-CIPPStandardTeamsExternalAccessPolicy { return $true } #we're done. - $CurrentState = New-TeamsRequest -TenantFilter $Tenant -Cmdlet 'Get-CsExternalAccessPolicy' -CmdParams @{Identity = 'Global' } | Select-Object * + try { + $CurrentState = New-TeamsRequest -TenantFilter $Tenant -Cmdlet 'Get-CsExternalAccessPolicy' -CmdParams @{Identity = 'Global' } | + Select-Object * + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the TeamsExternalAccessPolicy state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $EnableFederationAccess = $Settings.EnableFederationAccess ?? $false $EnableTeamsConsumerAccess = $Settings.EnableTeamsConsumerAccess ?? $false diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsExternalFileSharing.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsExternalFileSharing.ps1 index 69c7d08f55c2..5beea221c5dc 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsExternalFileSharing.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsExternalFileSharing.ps1 @@ -41,8 +41,16 @@ function Invoke-CIPPStandardTeamsExternalFileSharing { Write-Host "We're exiting as the correct license is not present for this standard." return $true } #we're done. - Write-Host "TeamsExternalFileSharing: $($Settings | ConvertTo-Json)" - $CurrentState = New-TeamsRequest -TenantFilter $Tenant -Cmdlet 'Get-CsTeamsClientConfiguration' | Select-Object AllowGoogleDrive, AllowShareFile, AllowBox, AllowDropBox, AllowEgnyte + + try { + $CurrentState = New-TeamsRequest -TenantFilter $Tenant -Cmdlet 'Get-CsTeamsClientConfiguration' | + Select-Object AllowGoogleDrive, AllowShareFile, AllowBox, AllowDropBox, AllowEgnyte + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the TeamsExternalFileSharing state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $StateIsCorrect = ($CurrentState.AllowGoogleDrive -eq $Settings.AllowGoogleDrive ?? $false ) -and ($CurrentState.AllowShareFile -eq $Settings.AllowShareFile ?? $false ) -and diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsFederationConfiguration.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsFederationConfiguration.ps1 index 7fcd2d8020b3..30128852abad 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsFederationConfiguration.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsFederationConfiguration.ps1 @@ -39,7 +39,15 @@ function Invoke-CIPPStandardTeamsFederationConfiguration { return $true } #we're done. - $CurrentState = New-TeamsRequest -TenantFilter $Tenant -Cmdlet 'Get-CsTenantFederationConfiguration' -CmdParams @{Identity = 'Global' } | Select-Object * + try { + $CurrentState = New-TeamsRequest -TenantFilter $Tenant -Cmdlet 'Get-CsTenantFederationConfiguration' -CmdParams @{Identity = 'Global' } | + Select-Object * + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the TeamsFederationConfiguration state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $AllowAllKnownDomains = New-CsEdgeAllowAllKnownDomains $DomainControl = $Settings.DomainControl.value ?? $Settings.DomainControl diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsGlobalMeetingPolicy.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsGlobalMeetingPolicy.ps1 index 3c1780fa0dc0..ee30521dafb3 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsGlobalMeetingPolicy.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsGlobalMeetingPolicy.ps1 @@ -40,7 +40,16 @@ function Invoke-CIPPStandardTeamsGlobalMeetingPolicy { Write-Host "We're exiting as the correct license is not present for this standard." return $true } #we're done. - $CurrentState = New-TeamsRequest -TenantFilter $Tenant -Cmdlet 'Get-CsTeamsMeetingPolicy' -CmdParams @{Identity = 'Global' } | Select-Object AllowAnonymousUsersToJoinMeeting, AllowAnonymousUsersToStartMeeting, AutoAdmittedUsers, AllowPSTNUsersToBypassLobby, MeetingChatEnabledType, DesignatedPresenterRoleMode, AllowExternalParticipantGiveRequestControl + + try { + $CurrentState = New-TeamsRequest -TenantFilter $Tenant -Cmdlet 'Get-CsTeamsMeetingPolicy' -CmdParams @{Identity = 'Global' } | + Select-Object AllowAnonymousUsersToJoinMeeting, AllowAnonymousUsersToStartMeeting, AutoAdmittedUsers, AllowPSTNUsersToBypassLobby, MeetingChatEnabledType, DesignatedPresenterRoleMode, AllowExternalParticipantGiveRequestControl + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the TeamsGlobalMeetingPolicy state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $MeetingChatEnabledType = $Settings.MeetingChatEnabledType.value ?? $Settings.MeetingChatEnabledType $DesignatedPresenterRoleMode = $Settings.DesignatedPresenterRoleMode.value ?? $Settings.DesignatedPresenterRoleMode diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsGuestAccess.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsGuestAccess.ps1 index c4d50f9b9fe4..26fb5519707d 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsGuestAccess.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsGuestAccess.ps1 @@ -36,11 +36,19 @@ function Invoke-CIPPStandardTeamsGuestAccess { return $true } #we're done. - $CurrentState = New-TeamsRequest -TenantFilter $Tenant -Cmdlet 'Get-CsTeamsClientConfiguration' -CmdParams @{Identity = 'Global' } | Select-Object AllowGuestUser + try { + $CurrentState = New-TeamsRequest -TenantFilter $Tenant -Cmdlet 'Get-CsTeamsClientConfiguration' -CmdParams @{Identity = 'Global' } | + Select-Object AllowGuestUser + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the TeamsGuestAccess state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } - if ($null -eq $Settings.AllowGuestUser) { $Settings.AllowGuestUser = $false } + $AllowGuestUser = $Settings.AllowGuestUser ?? $false - $StateIsCorrect = ($CurrentState.AllowGuestUser -eq $Settings.AllowGuestUser) + $StateIsCorrect = ($CurrentState.AllowGuestUser -eq $AllowGuestUser) if ($Settings.remediate -eq $true) { if ($StateIsCorrect -eq $true) { @@ -48,7 +56,7 @@ function Invoke-CIPPStandardTeamsGuestAccess { } else { $cmdParams = @{ Identity = 'Global' - AllowGuestUser = $Settings.AllowGuestUser + AllowGuestUser = $AllowGuestUser } try { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsMeetingRecordingExpiration.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsMeetingRecordingExpiration.ps1 index 28b88c3bcff5..6c1584fb4ecf 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsMeetingRecordingExpiration.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsMeetingRecordingExpiration.ps1 @@ -44,7 +44,15 @@ function Invoke-CIPPStandardTeamsMeetingRecordingExpiration { return } - $CurrentExpirationDays = (New-TeamsRequest -TenantFilter $Tenant -Cmdlet 'Get-CsTeamsMeetingPolicy' -CmdParams @{Identity = 'Global' }).NewMeetingRecordingExpirationDays + try { + $CurrentExpirationDays = (New-TeamsRequest -TenantFilter $Tenant -Cmdlet 'Get-CsTeamsMeetingPolicy' -CmdParams @{Identity = 'Global' }).NewMeetingRecordingExpirationDays + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the TeamsMeetingRecordingExpiration state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } + $StateIsCorrect = if ($CurrentExpirationDays -eq $ExpirationDays) { $true } else { $false } if ($Settings.remediate -eq $true) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsMeetingVerification.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsMeetingVerification.ps1 index c051ad79e9ba..95ef3efd57a3 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsMeetingVerification.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsMeetingVerification.ps1 @@ -39,7 +39,17 @@ function Invoke-CIPPStandardTeamsMeetingVerification { Write-Host "We're exiting as the correct license is not present for this standard." return $true } #we're done. - $CurrentState = New-TeamsRequest -TenantFilter $Tenant -Cmdlet 'Get-CsTeamsMeetingPolicy' -CmdParams @{Identity = 'Global' } | Select-Object CaptchaVerificationForMeetingJoin + + try { + $CurrentState = New-TeamsRequest -TenantFilter $Tenant -Cmdlet 'Get-CsTeamsMeetingPolicy' -CmdParams @{Identity = 'Global' } | + Select-Object CaptchaVerificationForMeetingJoin + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the TeamsMeetingVerification state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } + $CaptchaVerificationForMeetingJoin = $Settings.CaptchaVerificationForMeetingJoin.value ?? $Settings.CaptchaVerificationForMeetingJoin $StateIsCorrect = ($CurrentState.CaptchaVerificationForMeetingJoin -eq $CaptchaVerificationForMeetingJoin) diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsMeetingsByDefault.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsMeetingsByDefault.ps1 index 9cb67826b0a0..bcb2881ab356 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsMeetingsByDefault.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsMeetingsByDefault.ps1 @@ -40,7 +40,15 @@ function Invoke-CIPPStandardTeamsMeetingsByDefault { # Get state value using null-coalescing operator $state = $Settings.state.value ?? $Settings.state - $CurrentState = (New-ExoRequest -tenantid $Tenant -cmdlet 'Get-OrganizationConfig').OnlineMeetingsByDefaultEnabled + try { + $CurrentState = (New-ExoRequest -tenantid $Tenant -cmdlet 'Get-OrganizationConfig').OnlineMeetingsByDefaultEnabled + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the TeamsMeetingsByDefault state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } + $WantedState = if ($state -eq 'true') { $true } else { $false } $StateIsCorrect = if ($CurrentState -eq $WantedState) { $true } else { $false } diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsMessagingPolicy.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsMessagingPolicy.ps1 index c2775c1542c3..c2d5a4702922 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsMessagingPolicy.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTeamsMessagingPolicy.ps1 @@ -44,7 +44,15 @@ Function Invoke-CIPPStandardTeamsMessagingPolicy { Write-Host "We're exiting as the correct license is not present for this standard." return $true } #we're done. - $CurrentState = New-TeamsRequest -TenantFilter $Tenant -Cmdlet 'Get-CsTeamsMessagingPolicy' -CmdParams @{Identity = 'Global' } + + try { + $CurrentState = New-TeamsRequest -TenantFilter $Tenant -Cmdlet 'Get-CsTeamsMessagingPolicy' -CmdParams @{Identity = 'Global' } + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the TeamsMessagingPolicy state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } if ($null -eq $Settings.AllowOwnerDeleteMessage) { $Settings.AllowOwnerDeleteMessage = $CurrentState.AllowOwnerDeleteMessage } if ($null -eq $Settings.AllowUserDeleteMessage) { $Settings.AllowUserDeleteMessage = $CurrentState.AllowUserDeleteMessage } diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTenantDefaultTimezone.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTenantDefaultTimezone.ps1 index 64c062713b66..aefe21354eb0 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTenantDefaultTimezone.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardTenantDefaultTimezone.ps1 @@ -37,7 +37,15 @@ function Invoke-CIPPStandardTenantDefaultTimezone { return $true } #we're done. - $CurrentState = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/admin/sharepoint/settings' -tenantid $Tenant -AsApp $true + try { + $CurrentState = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/admin/sharepoint/settings' -tenantid $Tenant -AsApp $true + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the TenantDefaultTimezone state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } + $ExpectedTimezone = $Settings.Timezone.value $StateIsCorrect = $CurrentState.tenantDefaultTimezone -eq $ExpectedTimezone diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardUndoOauth.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardUndoOauth.ps1 index fd43f8ced664..166321bccad1 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardUndoOauth.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardUndoOauth.ps1 @@ -30,7 +30,15 @@ function Invoke-CIPPStandardUndoOauth { param($Tenant, $Settings) ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'UndoOauth' - $CurrentState = New-GraphGetRequest -tenantid $Tenant -Uri 'https://graph.microsoft.com/beta/policies/authorizationPolicy/authorizationPolicy?$select=permissionGrantPolicyIdsAssignedToDefaultUserRole' + try { + $CurrentState = New-GraphGetRequest -tenantid $Tenant -Uri 'https://graph.microsoft.com/beta/policies/authorizationPolicy/authorizationPolicy?$select=permissionGrantPolicyIdsAssignedToDefaultUserRole' + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the App Consent state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } + $StateIsCorrect = ($CurrentState.permissionGrantPolicyIdsAssignedToDefaultUserRole -eq 'ManagePermissionGrantsForSelf.microsoft-user-default-legacy') If ($Settings.remediate -eq $true) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardUserPreferredLanguage.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardUserPreferredLanguage.ps1 index e2b65e9c9bba..f203d1a427d1 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardUserPreferredLanguage.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardUserPreferredLanguage.ps1 @@ -31,7 +31,15 @@ function Invoke-CIPPStandardUserPreferredLanguage { param($Tenant, $Settings) $preferredLanguage = $Settings.preferredLanguage.value - $IncorrectUsers = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/users?`$top=999&`$select=userPrincipalName,displayName,preferredLanguage,userType,onPremisesSyncEnabled&`$filter=preferredLanguage ne '$preferredLanguage' and userType eq 'Member' and onPremisesSyncEnabled ne true&`$count=true" -tenantid $Tenant -ComplexFilter + + try { + $IncorrectUsers = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/users?`$top=999&`$select=userPrincipalName,displayName,preferredLanguage,userType,onPremisesSyncEnabled&`$filter=preferredLanguage ne '$preferredLanguage' and userType eq 'Member' and onPremisesSyncEnabled ne true&`$count=true" -tenantid $Tenant -ComplexFilter + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the UserPreferredLanguage state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } if ($Settings.remediate -eq $true) { if (($IncorrectUsers | Measure-Object).Count -gt 0) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardUserSubmissions.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardUserSubmissions.ps1 index 3c5cf3e8a606..66833d43dd9c 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardUserSubmissions.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardUserSubmissions.ps1 @@ -57,8 +57,14 @@ function Invoke-CIPPStandardUserSubmissions { } } - $PolicyState = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-ReportSubmissionPolicy' - $RuleState = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-ReportSubmissionRule' + try { + $PolicyState = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-ReportSubmissionPolicy' + $RuleState = New-ExoRequest -tenantid $Tenant -cmdlet 'Get-ReportSubmissionRule' + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the UserSubmissions state for $Tenant. Error: $ErrorMessage" -Sev Error + } if ($state -eq 'enable') { if (([string]::IsNullOrWhiteSpace($Email))) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardallowOAuthTokens.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardallowOAuthTokens.ps1 index 500c0af7ec87..c6103ab615bd 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardallowOAuthTokens.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardallowOAuthTokens.ps1 @@ -30,7 +30,14 @@ function Invoke-CIPPStandardallowOAuthTokens { param($Tenant, $Settings) #$Rerun -Type Standard -Tenant $Tenant -API 'AddDKIM' -Settings $Settings - $CurrentState = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy/authenticationMethodConfigurations/softwareOath' -tenantid $Tenant + try { + $CurrentState = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy/authenticationMethodConfigurations/softwareOath' -tenantid $Tenant + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the allowOTPTokens state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $StateIsCorrect = ($CurrentState.state -eq 'enabled') If ($Settings.remediate -eq $true) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardallowOTPTokens.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardallowOTPTokens.ps1 index ce374e86fd14..8aaa1977d17b 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardallowOTPTokens.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardallowOTPTokens.ps1 @@ -30,7 +30,14 @@ function Invoke-CIPPStandardallowOTPTokens { param($Tenant, $Settings) #$Rerun -Type Standard -Tenant $Tenant -API 'allowOTPTokens' -Settings $Settings - $CurrentInfo = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy/authenticationMethodConfigurations/microsoftAuthenticator' -tenantid $Tenant + try { + $CurrentInfo = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy/authenticationMethodConfigurations/microsoftAuthenticator' -tenantid $Tenant + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the allowOTPTokens state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } If ($Settings.remediate -eq $true) { if ($CurrentInfo.isSoftwareOathEnabled) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandarddisableMacSync.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandarddisableMacSync.ps1 index 828e956e49ff..baeaed8e7d18 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandarddisableMacSync.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandarddisableMacSync.ps1 @@ -36,7 +36,14 @@ function Invoke-CIPPStandarddisableMacSync { return $true } #we're done. - $CurrentInfo = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/admin/sharepoint/settings' -tenantid $Tenant -AsApp $true + try { + $CurrentInfo = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/admin/sharepoint/settings' -tenantid $Tenant -AsApp $true + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the DisableMacSync state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } If ($Settings.remediate -eq $true) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardintuneBrandingProfile.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardintuneBrandingProfile.ps1 index 4d4712823972..b997164726d3 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardintuneBrandingProfile.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardintuneBrandingProfile.ps1 @@ -46,7 +46,14 @@ function Invoke-CIPPStandardintuneBrandingProfile { return $true } #we're done. - $CurrentState = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/deviceManagement/intuneBrandingProfiles/c3a59481-1bf2-46ce-94b3-66eec07a8d60' -tenantid $Tenant -AsApp $true + try { + $CurrentState = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/deviceManagement/intuneBrandingProfiles/c3a59481-1bf2-46ce-94b3-66eec07a8d60' -tenantid $Tenant -AsApp $true + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the intuneBrandingProfile state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $StateIsCorrect = ((-not $Settings.displayName) -or ($CurrentState.displayName -eq $Settings.displayName)) -and ((-not $Settings.showLogo) -or ($CurrentState.showLogo -eq $Settings.showLogo)) -and diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardintuneDeviceReg.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardintuneDeviceReg.ps1 index 5dc65b27bc64..58eac0249a95 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardintuneDeviceReg.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardintuneDeviceReg.ps1 @@ -37,7 +37,14 @@ function Invoke-CIPPStandardintuneDeviceReg { return $true } #we're done. - $PreviousSetting = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/policies/deviceRegistrationPolicy' -tenantid $Tenant + try { + $PreviousSetting = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/policies/deviceRegistrationPolicy' -tenantid $Tenant + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the intuneDeviceReg state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $StateIsCorrect = if ($PreviousSetting.userDeviceQuota -eq $Settings.max) { $true } else { $false } If ($Settings.remediate -eq $true) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardintuneDeviceRetirementDays.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardintuneDeviceRetirementDays.ps1 index 895eaf943222..b1427cb507d1 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardintuneDeviceRetirementDays.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardintuneDeviceRetirementDays.ps1 @@ -38,7 +38,15 @@ function Invoke-CIPPStandardintuneDeviceRetirementDays { return $true } #we're done. - $CurrentInfo = (New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/deviceManagement/managedDeviceCleanupRules' -tenantid $Tenant) + try { + $CurrentInfo = (New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/deviceManagement/managedDeviceCleanupRules' -tenantid $Tenant) + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the intuneDeviceRetirementDays state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } + $StateIsCorrect = if ($CurrentInfo.DeviceInactivityBeforeRetirementInDays -eq $Settings.days) { $true } else { $false } if ($Settings.remediate -eq $true) { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardintuneRequireMFA.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardintuneRequireMFA.ps1 index f70b182365bc..e59197e0c573 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardintuneRequireMFA.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardintuneRequireMFA.ps1 @@ -35,7 +35,14 @@ function Invoke-CIPPStandardintuneRequireMFA { return $true } #we're done. - $PreviousSetting = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/policies/deviceRegistrationPolicy' -tenantid $Tenant + try { + $PreviousSetting = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/policies/deviceRegistrationPolicy' -tenantid $Tenant + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the intuneRequireMFA state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } If ($Settings.remediate -eq $true) { if ($PreviousSetting.multiFactorAuthConfiguration -eq 'required') { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardlaps.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardlaps.ps1 index b6f53ee67fda..d0fcac4280e4 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardlaps.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardlaps.ps1 @@ -31,7 +31,14 @@ function Invoke-CIPPStandardlaps { param($Tenant, $Settings) ##$Rerun -Type Standard -Tenant $Tenant -Settings $Settings 'laps' - $PreviousSetting = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/policies/deviceRegistrationPolicy' -tenantid $Tenant + try { + $PreviousSetting = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/policies/deviceRegistrationPolicy' -tenantid $Tenant + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the DeviceRegistrationPolicy state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } If ($Settings.remediate -eq $true) { try { diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardsharingCapability.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardsharingCapability.ps1 index 4a54c8ba0270..37b1e7653ffd 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardsharingCapability.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardsharingCapability.ps1 @@ -39,7 +39,14 @@ function Invoke-CIPPStandardsharingCapability { return $true } #we're done. - $CurrentInfo = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/admin/sharepoint/settings' -tenantid $Tenant -AsApp $true + try { + $CurrentInfo = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/admin/sharepoint/settings' -tenantid $Tenant -AsApp $true + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the sharingCapability state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } if ($Settings.report -eq $true) { Add-CIPPBPAField -FieldName 'sharingCapability' -FieldValue $CurrentInfo.sharingCapability -StoreAs string -Tenant $Tenant diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardsharingDomainRestriction.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardsharingDomainRestriction.ps1 index e1b5a62f4c62..033d49556c15 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardsharingDomainRestriction.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardsharingDomainRestriction.ps1 @@ -38,7 +38,14 @@ function Invoke-CIPPStandardsharingDomainRestriction { return $true } #we're done. - $CurrentState = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/admin/sharepoint/settings' -tenantid $Tenant -AsApp $true + try { + $CurrentState = New-GraphGetRequest -Uri 'https://graph.microsoft.com/beta/admin/sharepoint/settings' -tenantid $Tenant -AsApp $true + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the SharingDomainRestriction state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } # Get mode value using null-coalescing operator $mode = $Settings.Mode.value ?? $Settings.Mode diff --git a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardunmanagedSync.ps1 b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardunmanagedSync.ps1 index ed8241ce919e..d46a2736d3e6 100644 --- a/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardunmanagedSync.ps1 +++ b/Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardunmanagedSync.ps1 @@ -38,7 +38,15 @@ function Invoke-CIPPStandardunmanagedSync { return $true } #we're done. - $CurrentState = Get-CIPPSPOTenant -TenantFilter $Tenant | Select-Object _ObjectIdentity_, TenantFilter, ConditionalAccessPolicy + try { + $CurrentState = Get-CIPPSPOTenant -TenantFilter $Tenant | + Select-Object _ObjectIdentity_, TenantFilter, ConditionalAccessPolicy + } + catch { + $ErrorMessage = Get-NormalizedError -Message $_.Exception.Message + Write-LogMessage -API 'Standards' -Tenant $Tenant -Message "Could not get the unmanagedSync state for $Tenant. Error: $ErrorMessage" -Sev Error + return + } $WantedState = [int]($Settings.state.value ?? 2) # Default to 2 (Block Access) if not set, for pre v8.0.3 standard compatibility $Label = $Settings.state.label ?? 'Block Access' # Default label if not set, for pre v8.0.3 standard compatibility diff --git a/Modules/CIPPCore/Public/Webhooks/Test-CIPPAuditLogRules.ps1 b/Modules/CIPPCore/Public/Webhooks/Test-CIPPAuditLogRules.ps1 index 6824b08058ea..3456ae75bd08 100644 --- a/Modules/CIPPCore/Public/Webhooks/Test-CIPPAuditLogRules.ps1 +++ b/Modules/CIPPCore/Public/Webhooks/Test-CIPPAuditLogRules.ps1 @@ -7,7 +7,103 @@ function Test-CIPPAuditLogRules { $Rows ) - $FunctionStartTime = Get-Date + # Helper function to map GUIDs and partner UPNs to user objects + function Add-CIPPGuidMappings { + param( + [Parameter(Mandatory = $true)] + $DataObject, + [Parameter(Mandatory = $true)] + $Users, + [Parameter(Mandatory = $true)] + $Groups, + [Parameter(Mandatory = $true)] + $Devices, + [Parameter(Mandatory = $true)] + $ServicePrincipals, + [Parameter(Mandatory = $true)] + $PartnerUsers, + [Parameter(Mandatory = $false)] + [string]$PropertyPrefix = '' + ) + + $DataObject.PSObject.Properties | ForEach-Object { + # Check for standard GUID format OR partner UPN formats + if ($_.Value -match '^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$' -or + $_.Value -match 'user_[0-9a-f]{32}@[^@]+\.onmicrosoft\.com' -or + $_.Value -match '[^\\]+\.onmicrosoft\.com\\tenant:\s*[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12},\s*object:\s*[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}') { + + # Use regex from guid-resolver hook to match various partner user formats + # Format 1: user_@.onmicrosoft.com + if ($_.Value -match 'user_([0-9a-f]{32})@([^@]+\.onmicrosoft\.com)') { + $hexId = $matches[1] + $tenantDomain = $matches[2] + if ($hexId.Length -eq 32) { + # Convert the 32-character hex string to GUID format + $guid = "$($hexId.Substring(0,8))-$($hexId.Substring(8,4))-$($hexId.Substring(12,4))-$($hexId.Substring(16,4))-$($hexId.Substring(20,12))" + Write-Information "Found partner UPN format: $($_.Value) with GUID: $guid and tenant: $tenantDomain" + + # Check partner users for this GUID + foreach ($PartnerUser in $PartnerUsers) { + if ($PartnerUser.id -eq $guid) { + $DataObject | Add-Member -NotePropertyName "$PropertyPrefix$($_.Name)" -NotePropertyValue $PartnerUser.userPrincipalName -Force -ErrorAction SilentlyContinue + Write-Information "Mapped Partner User UPN: $($PartnerUser.userPrincipalName) to $PropertyPrefix$($_.Name)" + return + } + } + } + } + + # Format 2: TenantName.onmicrosoft.com\tenant: , object: + if ($_.Value -match '([^\\]+\.onmicrosoft\.com)\\tenant:\s*([0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}),\s*object:\s*([0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12})') { + $customerTenantDomain = $matches[1] + $partnerTenantGuid = $matches[2] + $objectGuid = $matches[3] + Write-Information "Found partner exchange format: customer tenant $customerTenantDomain, partner tenant $partnerTenantGuid, object $objectGuid" + + # Check partner users for this object GUID + foreach ($PartnerUser in $PartnerUsers) { + if ($PartnerUser.id -eq $objectGuid) { + $DataObject | Add-Member -NotePropertyName "$PropertyPrefix$($_.Name)" -NotePropertyValue $PartnerUser.userPrincipalName -Force -ErrorAction SilentlyContinue + Write-Information "Mapped Partner User UPN: $($PartnerUser.userPrincipalName) to $PropertyPrefix$($_.Name)" + return + } + } + } + + # Check standard directory objects (users, groups, devices, service principals) + foreach ($User in $Users) { + if ($User.id -eq $_.Value) { + $DataObject | Add-Member -NotePropertyName "$PropertyPrefix$($_.Name)" -NotePropertyValue $User.userPrincipalName -Force -ErrorAction SilentlyContinue + Write-Information "Mapped User: $($User.userPrincipalName) to $PropertyPrefix$($_.Name)" + return + } + } + foreach ($Group in $Groups) { + if ($Group.id -eq $_.Value) { + $DataObject | Add-Member -NotePropertyName "$PropertyPrefix$($_.Name)" -NotePropertyValue $Group -Force -ErrorAction SilentlyContinue + Write-Information "Mapped Group: $($Group.displayName) to $PropertyPrefix$($_.Name)" + return + } + } + foreach ($Device in $Devices) { + if ($Device.id -eq $_.Value) { + $DataObject | Add-Member -NotePropertyName "$PropertyPrefix$($_.Name)" -NotePropertyValue $Device -Force -ErrorAction SilentlyContinue + Write-Information "Mapped Device: $($Device.displayName) to $PropertyPrefix$($_.Name)" + return + } + } + foreach ($ServicePrincipal in $ServicePrincipals) { + if ($ServicePrincipal.id -eq $_.Value -or $ServicePrincipal.appId -eq $_.Value) { + $DataObject | Add-Member -NotePropertyName "$PropertyPrefix$($_.Name)" -NotePropertyValue $ServicePrincipal -Force -ErrorAction SilentlyContinue + Write-Information "Mapped Service Principal: $($ServicePrincipal.displayName) to $PropertyPrefix$($_.Name)" + return + } + } + } + } + } + + #$FunctionStartTime = Get-Date $Results = [PSCustomObject]@{ TotalLogs = 0 @@ -41,6 +137,40 @@ function Test-CIPPAuditLogRules { } } + # Collect bulk data for users/groups/devices/applications + $Requests = @( + @{ + id = 'users' + url = '/users?$select=id,displayName,userPrincipalName,accountEnabled&$top=999' + method = 'GET' + } + @{ + id = 'groups' + url = '/groups?$select=id,displayName,mailEnabled,securityEnabled&$top=999' + method = 'GET' + } + @{ + id = 'devices' + url = '/devices?$select=id,displayName,deviceId&$top=999' + method = 'GET' + } + @{ + id = 'servicePrincipals' + url = '/servicePrincipals?$select=id,displayName&$top=999' + method = 'GET' + } + ) + $Response = New-GraphBulkRequest -TenantId $TenantFilter -Requests $Requests + + # partner users + $PartnerUsers = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/users?`$select=id,displayName,userPrincipalName,accountEnabled&`$top=999" -AsApp $true -NoAuthCheck $true + + + $Users = $Response | Where-Object { $_.id -eq 'users' } | Select-Object -ExpandProperty body | Select-Object -ExpandProperty value + $Groups = $Response | Where-Object { $_.id -eq 'groups' } | Select-Object -ExpandProperty body | Select-Object -ExpandProperty value + $Devices = $Response | Where-Object { $_.id -eq 'devices' } | Select-Object -ExpandProperty body | Select-Object -ExpandProperty value + $ServicePrincipals = $Response | Where-Object { $_.id -eq 'servicePrincipals' } | Select-Object -ExpandProperty body | Select-Object -ExpandProperty value + Write-Warning '## Audit Log Configuration ##' Write-Information ($Configuration | ConvertTo-Json -Depth 10) @@ -61,10 +191,18 @@ function Test-CIPPAuditLogRules { $LocationTable = Get-CIPPTable -TableName 'knownlocationdbv2' $ProcessedData = foreach ($AuditRecord in $SearchResults) { $RecordStartTime = Get-Date - Write-Host "Processing RowKey $($AuditRecord.id)" + Write-Information "Processing RowKey $($AuditRecord.id)" $RootProperties = $AuditRecord | Select-Object * -ExcludeProperty auditData $Data = $AuditRecord.auditData | Select-Object *, CIPPAction, CIPPClause, CIPPGeoLocation, CIPPBadRepIP, CIPPHostedIP, CIPPIPDetected, CIPPLocationInfo, CIPPExtendedProperties, CIPPDeviceProperties, CIPPParameters, CIPPModifiedProperties, AuditRecord -ErrorAction SilentlyContinue try { + # Attempt to locate GUIDs in $Data and match them with their corresponding user, group, device, or service principal recursively by checking each key/value once located lets store these mapped values in a CIPP$KeyName property + Write-Information 'Checking Data for GUIDs to map to users, groups, devices, or service principals' + Add-CIPPGuidMappings -DataObject $Data -Users $Users -Groups $Groups -Devices $Devices -ServicePrincipals $ServicePrincipals -PartnerUsers $PartnerUsers -PropertyPrefix 'CIPP' + + # Also check root properties for GUIDs and partner UPNs + Write-Information 'Checking RootProperties for GUIDs to map to users, groups, devices, or service principals' + Add-CIPPGuidMappings -DataObject $RootProperties -Users $Users -Groups $Groups -Devices $Devices -ServicePrincipals $ServicePrincipals -PartnerUsers $PartnerUsers + if ($Data.ExtendedProperties) { $Data.CIPPExtendedProperties = ($Data.ExtendedProperties | ConvertTo-Json -Compress) $Data.ExtendedProperties | ForEach-Object { @@ -173,10 +311,8 @@ function Test-CIPPAuditLogRules { #write-warning "Audit log: Error processing data: $($_.Exception.Message)`r`n$($_.InvocationInfo.PositionMessage)" Write-LogMessage -API 'Webhooks' -message 'Error Processing Audit Log Data' -LogData (Get-CippException -Exception $_) -sev Error -tenant $TenantFilter } - $RecordEndTime = Get-Date - $RecordSeconds = ($RecordEndTime - $RecordStartTime).TotalSeconds - Write-Warning "Task took $RecordSeconds seconds for RowKey $($AuditRecord.id)" - Write-Host "Removing row $($AuditRecord.id) from cache" + + Write-Information "Removing row $($AuditRecord.id) from cache" try { Write-Information 'Removing processed rows from cache' $RowEntity = Get-CIPPAzDataTableEntity @CacheWebhooksTable -Filter "PartitionKey eq '$TenantFilter' and RowKey eq '$($AuditRecord.id)'" @@ -184,6 +320,10 @@ function Test-CIPPAuditLogRules { Write-Information "Removed row $($AuditRecord.id) from cache" } catch { Write-Information "Error removing rows from cache: $($_.Exception.Message)" + } finally { + $RecordEndTime = Get-Date + $RecordSeconds = ($RecordEndTime - $RecordStartTime).TotalSeconds + Write-Warning "Task took $RecordSeconds seconds for RowKey $($AuditRecord.id)" } } #write-warning "Processed Data: $(($ProcessedData | Measure-Object).Count) - This should be higher than 0 in many cases, because the where object has not run yet." diff --git a/Modules/CippEntrypoints/CippEntrypoints.psm1 b/Modules/CippEntrypoints/CippEntrypoints.psm1 index 546ca1cebb7a..524a3f6fd0ad 100644 --- a/Modules/CippEntrypoints/CippEntrypoints.psm1 +++ b/Modules/CippEntrypoints/CippEntrypoints.psm1 @@ -53,7 +53,16 @@ function Receive-CippHttpTrigger { if ($FunctionName -eq 'Invoke-Me') { return } - + } catch { + Write-Information "Access denied for $FunctionName : $($_.Exception.Message)" + Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{ + StatusCode = [HttpStatusCode]::Forbidden + Body = $_.Exception.Message + }) + return + } + + try { Write-Information "Access: $Access" if ($Access) { & $FunctionName @HttpTrigger diff --git a/Modules/CippExtensions/ConversionTable.csv b/Modules/CippExtensions/ConversionTable.csv index 2658524b8cf2..9407f70d6cb7 100644 --- a/Modules/CippExtensions/ConversionTable.csv +++ b/Modules/CippExtensions/ConversionTable.csv @@ -240,6 +240,9 @@ Dynamics 365 Enterprise Edition - Additional Production Instance for Government, "Dynamics 365 Field Service, Enterprise Edition - Resource Scheduling Optimization",CRM_AUTO_ROUTING_ADDON,977464c4-bfaf-4b67-b761-a9bb735a2196,CRM_AUTO_ROUTING_ENGINE_ADDON,24435e4b-87d0-4d7d-8beb-63a9b1573022,Field Service – Automated Routing Engine Add-On "Dynamics 365 Field Service, Enterprise Edition - Resource Scheduling Optimization",CRM_AUTO_ROUTING_ADDON,977464c4-bfaf-4b67-b761-a9bb735a2196,CRM_AUTO_ROUTING_ADDON,2ba394e0-6f18-4b77-b45f-a5663bbab540,RETIRED - Field Service – Automated Routing Engine Add-On "Dynamics 365 Field Service, Enterprise Edition - Resource Scheduling Optimization",CRM_AUTO_ROUTING_ADDON,977464c4-bfaf-4b67-b761-a9bb735a2196,EXCHANGE_S_FOUNDATION,113feb6c-3fe4-4440-bddc-54d774bf0318,Exchange Foundation +Dynamics 365 Field Service Contractor,D365_FIELD_SERVICE_CONTRACTOR,23e6e135-e869-4ce4-9ae4-5710cd69ac13,CDS_FIELD_SERVICE_CONTRACTOR,f4614a66-d632-443a-bc77-afe92987b322,Common Data Service Field service Part Time Contractors +Dynamics 365 Field Service Contractor,D365_FIELD_SERVICE_CONTRACTOR,23e6e135-e869-4ce4-9ae4-5710cd69ac13,EXCHANGE_S_FOUNDATION,113feb6c-3fe4-4440-bddc-54d774bf0318,Exchange Foundation +Dynamics 365 Field Service Contractor,D365_FIELD_SERVICE_CONTRACTOR,23e6e135-e869-4ce4-9ae4-5710cd69ac13,POWERAPPS_DYN_APPS,874fc546-6efe-4d22-90b8-5c4e7aa59f4b,Power Apps for Dynamics 365 Dynamics 365 Field Service Contractor for Government,D365_FIELD_SERVICE_CONTRACTOR_GOV,e7965e3a-1f49-4d67-a3de-ad1ce460bbcc,CDS_FIELD_SERVICE_CONTRACTOR_GCC,2457fe40-65be-48a1-935f-924ad6e62dba,Common Data Service Field service Part Time Contractors for Government Dynamics 365 Field Service Contractor for Government,D365_FIELD_SERVICE_CONTRACTOR_GOV,e7965e3a-1f49-4d67-a3de-ad1ce460bbcc,EXCHANGE_S_FOUNDATION_GOV,922ba911-5694-4e99-a794-73aed9bfeec8,Exchange Foundation for Government Dynamics 365 Field Service Contractor for Government,D365_FIELD_SERVICE_CONTRACTOR_GOV,e7965e3a-1f49-4d67-a3de-ad1ce460bbcc,POWERAPPS_DYN_APPS_GOV,3089c02b-e533-4b73-96a5-01fa648c3c3c,PowerApps for Dynamics 365 for Government @@ -2246,6 +2249,93 @@ Microsoft 365 E5,SPE_E5,06ebc4ee-1bb5-47dd-8120-11324bc54e06,AAD_PREMIUM_P2,eec0 Microsoft 365 E5,SPE_E5,06ebc4ee-1bb5-47dd-8120-11324bc54e06,INTUNE_A,c1ec4a95-1f05-45b3-a911-aa3fa01094f5,Microsoft Intune Plan 1 Microsoft 365 E5,SPE_E5,06ebc4ee-1bb5-47dd-8120-11324bc54e06,FLOW_O365_P3,07699545-9485-468e-95b6-2fca3738be01,Power Automate for Office 365 Microsoft 365 E5,SPE_E5,06ebc4ee-1bb5-47dd-8120-11324bc54e06,POWER_VIRTUAL_AGENTS_O365_P3,ded3d325-1bdc-453e-8432-5bac26d7a014,Power Virtual Agents for Office 365 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MESH_AVATARS_FOR_TEAMS,dcf9d2f4-772e-4434-b757-77a453cfbc02,Avatars for Teams +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MESH_AVATARS_ADDITIONAL_FOR_TEAMS,3efbd4ed-8958-4824-8389-1321f8730af8,Avatars for Teams (additional) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,CDS_O365_P3,afa73018-811e-46e9-988f-f75d2b1b8430,Common Data Service for Teams +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,LOCKBOX_ENTERPRISE,9f431833-0334-42de-a7dc-70aa40db46db,Customer Lockbox +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,CustomerLockboxA_Enterprise,3ec18638-bd4c-4d3b-8905-479ed636b83e,Customer Lockbox (A) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MIP_S_Exchange,cd31b152-6326-4d1b-ae1b-997b625182e6,Data Classification in Microsoft 365 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,EXCHANGE_S_ENTERPRISE,efb87545-963c-4e0d-99df-69c6916d9eb0,Exchange Online (Plan 2) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,GRAPH_CONNECTORS_SEARCH_INDEX,a6520331-d7d4-4276-95f5-15c0933bc757,Graph Connectors Search with Index +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MESH_IMMERSIVE_FOR_TEAMS,f0ff6ac6-297d-49cd-be34-6dfef97f0c28,Immersive spaces for Teams +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,INFORMATION_BARRIERS,c4801e8a-cb58-4c35-aca6-f2dcc106f287,Information Barriers +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,Content_Explorer,d9fa6af4-e046-4c89-9226-729a0786685d,Information Protection and Governance Analytics - Premium +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,ContentExplorer_Standard,2b815d45-56e4-4e3a-b65c-66cb9175b560,Information Protection and Governance Analytics – Standard +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MIP_S_CLP2,efb0351d-3b08-4503-993d-383af8de41e3,Information Protection for Office 365 - Premium +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MIP_S_CLP1,5136a095-5cf0-4aff-bec3-e84448b38ea5,Information Protection for Office 365 - Standard +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MYANALYTICS_P2,33c4f319-9bdd-48d6-9c4d-410b750a4a5a,Insights by MyAnalytics +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,M365_ADVANCED_AUDITING,2f442157-a11c-46b9-ae5b-6e39ff4e5849,Microsoft 365 Advanced Auditing +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,OFFICESUBSCRIPTION,43de0ff5-c92c-492b-9116-175376d08c38,Microsoft 365 Apps for enterprise +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MCOMEETADV,3e26ee1f-8a5f-4d52-aee2-b81ce45c8f40,Microsoft 365 Audio Conferencing +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,M365_AUDIT_PLATFORM,f6de4823-28fa-440b-b886-4783fa86ddba,Microsoft 365 Audit Platform +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MICROSOFT_COMMUNICATION_COMPLIANCE,a413a9ff-720c-4822-98ef-2f37c2a21f4c,Microsoft 365 Communication Compliance +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MTP,bf28f719-7844-4079-9c78-c1307898e192,Microsoft 365 Defender +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,M365_LIGHTHOUSE_CUSTOMER_PLAN1,6f23d6a9-adbf-481c-8538-b4c095654487,Microsoft 365 Lighthouse (Plan 1) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MCOEV,4828c8ec-dc2e-4779-b502-87ac9ce28ab7,Microsoft 365 Phone System +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MICROSOFTBOOKINGS,199a5c09-e0ca-4e37-8f7c-b05d533e1ea2,Microsoft Bookings +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,CLIPCHAMP,a1ace008-72f3-4ea0-8dac-33b3a23a2472,Microsoft Clipchamp +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,COMMUNICATIONS_DLP,6dc145d6-95dd-4191-b9c3-185575ee6f6b,Microsoft Communications DLP +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,CUSTOMER_KEY,6db1f1db-2b46-403f-be40-e39395f08dbb,Microsoft Customer Key +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,ATP_ENTERPRISE,f20fedf3-f3c3-43c3-8267-2bfdd51c0939,Microsoft Defender for Office 365 (Plan 1) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,THREAT_INTELLIGENCE,8e0c0a52-6a6c-4d40-8370-dd62790dcd70,Microsoft Defender for Office 365 (Plan 2) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,EXCEL_PREMIUM,531ee2f8-b1cb-453b-9c21-d2180d014ca5,Microsoft Excel Advanced Analytics +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,FORMS_PLAN_E5,e212cbc7-0961-4c40-9825-01117710dcb1,Microsoft Forms (Plan E5) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,INFO_GOVERNANCE,e26c2fcc-ab91-4a61-b35c-03cdc8dddf66,Microsoft Information Governance +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,INSIDER_RISK,d587c7a3-bda9-4f99-8776-9bcf59c84f75,Microsoft Insider Risk Management +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,INSIDER_RISK_MANAGEMENT,9d0c4ee5-e4a1-4625-ab39-d82b619b1a34,Microsoft Insider Risk Management - Exchange +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,KAIZALA_STANDALONE,0898bdbb-73b0-471a-81e5-20f1fe4dd66e,Microsoft Kaizala Pro +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MICROSOFT_LOOP,c4b8c31a-fb44-4c65-9837-a21f55fcabda,Microsoft Loop +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,ML_CLASSIFICATION,d2d51368-76c9-4317-ada2-a12c004c432f,Microsoft ML-Based Classification +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,EXCHANGE_ANALYTICS,34c0d7a0-a70f-4668-9238-47f9fc208882,Microsoft MyAnalytics (Full) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,PROJECTWORKMANAGEMENT,b737dad2-2f6c-4c65-90e3-ca563267e8b9,Microsoft Planner +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,RECORDS_MANAGEMENT,65cc641f-cccd-4643-97e0-a17e3045e541,Microsoft Records Management +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MICROSOFT_SEARCH,94065c59-bc8e-4e8b-89e5-5138d471eaff,Microsoft Search +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,Deskless,8c7d2df8-86f0-4902-b2ed-a0458298f3b3,Microsoft StaffHub +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,STREAM_O365_E5,6c6042f5-6f01-4d67-b8c1-eb99d36eed3e,Microsoft Stream for Office 365 E5 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,INTUNE_O365,882e1d05-acd1-4ccb-8708-6ee03664b117,Mobile Device Management for Office 365 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,Nucleus,db4d623d-b514-490b-b7ef-8885eee514de,Nucleus +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,EQUIVIO_ANALYTICS,4de31727-a228-4ec3-a5bf-8e45b5ca48cc,Office 365 Advanced eDiscovery +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,ADALLOM_S_O365,8c098270-9dd4-4350-9b30-ba4703f3b36b,Office 365 Cloud App Security +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,PAM_ENTERPRISE,b1188c4c-1b36-4018-b48b-ee07604f6feb,Office 365 Privileged Access Management +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,SAFEDOCS,bf6f5520-59e3-4f82-974b-7dbbc4fd27c7,Office 365 SafeDocs +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,SHAREPOINTWAC,e95bec33-7c88-4a70-8e19-b10bd9d0c014,Office for the Web +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,PEOPLE_SKILLS_FOUNDATION,13b6da2c-0d84-450e-9f69-a33e221387ca,People Skills - Foundation +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,POWERAPPS_O365_P3,9c0dab89-a30c-4117-86e7-97bda240acd2,Power Apps for Office 365 (Plan 3) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,BI_AZURE_P2,70d33638-9c74-4d01-bfd3-562de28bd4ba,Power BI Pro +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,PREMIUM_ENCRYPTION,617b097b-4b93-4ede-83de-5f075bb5fb2f,Premium Encryption in Office 365 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,PROJECT_O365_P3,b21a6b06-1988-436e-a07b-51ec6d9f52ad,Project for Office (Plan E5) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,PURVIEW_DISCOVERY,c948ea65-2053-4a5a-8a62-9eaaaf11b522,Purview Discovery +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,Bing_Chat_Enterprise,0d0c0d31-fae7-41f2-b909-eaf4d7f26dba,RETIRED - Commercial data protection for Microsoft Copilot +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,COMMUNICATIONS_COMPLIANCE,41fcdd7d-4733-4863-9cf4-c65b83ce2df4,RETIRED - Microsoft Communications Compliance +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,DATA_INVESTIGATIONS,46129a58-a698-46f0-aa5b-17f6586297d9,Retired - Microsoft Data Investigations +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,PLACES_CORE,1fe6227d-3e01-46d0-9510-0acad4ff6e94,RETIRED - Places Core +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,SHAREPOINTENTERPRISE,5dbe027f-2339-4123-9542-606e4d348a72,SharePoint (Plan 2) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MCOSTANDARD,0feaeb32-d00e-4d66-bd5a-43b5b83db82c,Skype for Business Online (Plan 2) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,SWAY,a23b959c-7ce8-4e57-9140-b90eb88a9e97,Sway +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,BPOS_S_TODO_3,3fb82609-8c27-4f7b-bd51-30634711ee67,To-Do (Plan 3) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,VIVAENGAGE_CORE,a82fbf69-b4d7-49f4-83a6-915b2cf354f4,Viva Engage Core +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,VIVA_LEARNING_SEEDED,b76fb638-6ba6-402a-b9f9-83d28acb3d86,Viva Learning Seeded +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,WHITEBOARD_PLAN3,4a51bca5-1eff-43f5-878c-177680f191af,Whiteboard (Plan 3) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,YAMMER_ENTERPRISE,7547a3fe-08ee-4ccb-b430-5077c5041653,Yammer Enterprise +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,WINDEFATP,871d91ec-ec1a-452b-a83f-bd76c7d770ef,Microsoft Defender for Endpoint +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MICROSOFTENDPOINTDLP,64bfac92-2b17-4482-b5e5-a0304429de3e,Microsoft Endpoint DLP +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,UNIVERSAL_PRINT_01,795f6fe0-cc4d-4773-b050-5dde4dc704c9,Universal Print +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,WIN10_PRO_ENT_SUB,21b439ba-a0ca-424f-a6cc-52f954a5b111,Windows 10/11 Enterprise (Original) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,Windows_Autopatch,9a6eeb79-0b4b-4bf0-9808-39d99a2cd5a3,Windows Autopatch +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,WINDOWSUPDATEFORBUSINESS_DEPLOYMENTSERVICE,7bf960f6-2cd9-443a-8046-5dbff9558365,Windows Update for Business Deployment Service +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,RMS_S_PREMIUM,6c57d4b6-3b23-47a5-9bc9-69f17b4947b3,Azure Information Protection Premium P1 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,RMS_S_PREMIUM2,5689bec4-755d-4753-8b61-40975025187c,Azure Information Protection Premium P2 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,RMS_S_ENTERPRISE,bea4c11e-220a-4e6d-8eb8-8ea15d019f90,Azure Rights Management +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,DYN365_CDS_O365_P3,28b0fa46-c39a-4188-89e2-58e979a6b014,Common Data Service +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,Defender_for_Iot_Enterprise,99cd49a9-0e54-4e07-aea1-d8d9f5f704f5,Defender for IoT - Enterprise IoT Security +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MFA_PREMIUM,8a256a2b-b617-496d-b51b-e76466e88db0,Microsoft Azure Multi-Factor Authentication +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,ADALLOM_S_STANDALONE,2e2ddb96-6af9-4b1d-a3f0-d6ecfd22edb2,Microsoft Defender for Cloud Apps +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,ATA,14ab5db5-e6c4-4b20-b4bc-13e36fd2227f,Microsoft Defender for Identity +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,AAD_PREMIUM,41781fb2-bc02-4b7c-bd55-b576c07bb09d,Microsoft Entra ID P1 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,AAD_PREMIUM_P2,eec0eb4f-6444-4f95-aba0-50c24d67f998,Microsoft Entra ID P2 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,INTUNE_A,c1ec4a95-1f05-45b3-a911-aa3fa01094f5,Microsoft Intune Plan 1 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,FLOW_O365_P3,07699545-9485-468e-95b6-2fca3738be01,Power Automate for Office 365 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,POWER_VIRTUAL_AGENTS_O365_P3,ded3d325-1bdc-453e-8432-5bac26d7a014,Power Virtual Agents for Office 365 Microsoft 365 E5 EEA (no Teams) with Calling Minutes,Microsoft_365_E5_EEA_(no_Teams)_with_Calling_Minutes,6ee4114a-9b2d-4577-9e7a-49fa43d222d3,CDS_O365_P3,afa73018-811e-46e9-988f-f75d2b1b8430,Common Data Service for Teams Microsoft 365 E5 EEA (no Teams) with Calling Minutes,Microsoft_365_E5_EEA_(no_Teams)_with_Calling_Minutes,6ee4114a-9b2d-4577-9e7a-49fa43d222d3,LOCKBOX_ENTERPRISE,9f431833-0334-42de-a7dc-70aa40db46db,Customer Lockbox Microsoft 365 E5 EEA (no Teams) with Calling Minutes,Microsoft_365_E5_EEA_(no_Teams)_with_Calling_Minutes,6ee4114a-9b2d-4577-9e7a-49fa43d222d3,MIP_S_Exchange,cd31b152-6326-4d1b-ae1b-997b625182e6,Data Classification in Microsoft 365 @@ -5127,6 +5217,11 @@ Power Apps Premium,POWERAPPS_PER_USER,b30411f5-fea1-4a59-9ad9-3db7c7ead579,DYN36 Power Apps Premium,POWERAPPS_PER_USER,b30411f5-fea1-4a59-9ad9-3db7c7ead579,CDSAICAPACITY_PERUSER,91f50f7b-2204-4803-acac-5cf5668b8b39,DO NOT USE - AI Builder capacity Per User add-on Power Apps Premium,POWERAPPS_PER_USER,b30411f5-fea1-4a59-9ad9-3db7c7ead579,POWERAPPS_PER_USER,ea2cf03b-ac60-46ae-9c1d-eeaeb63cec86,Power Apps per User Plan Power Apps Premium,POWERAPPS_PER_USER,b30411f5-fea1-4a59-9ad9-3db7c7ead579,Flow_PowerApps_PerUser,dc789ed8-0170-4b65-a415-eb77d5bb350a,Power Automate for Power Apps per User Plan +Power Apps Premium embedded,POWERAPPS_PER_USER_ISVEMB,2a6fb3c6-30cc-4558-a69d-032425c1a3ba,Power_Pages_Internal_User,60bf28f9-2b70-4522-96f7-335f5e06c941,Power Pages Internal User +Power Apps Premium embedded,POWERAPPS_PER_USER_ISVEMB,2a6fb3c6-30cc-4558-a69d-032425c1a3ba,EXCHANGE_S_FOUNDATION,113feb6c-3fe4-4440-bddc-54d774bf0318,Exchange Foundation +Power Apps Premium embedded,POWERAPPS_PER_USER_ISVEMB,2a6fb3c6-30cc-4558-a69d-032425c1a3ba,DYN365_CDS_P2,6ea4c1ef-c259-46df-bce2-943342cd3cb2,Common Data Service +Power Apps Premium embedded,POWERAPPS_PER_USER_ISVEMB,2a6fb3c6-30cc-4558-a69d-032425c1a3ba,POWERAPPS_PER_USER,ea2cf03b-ac60-46ae-9c1d-eeaeb63cec86,Power Apps per User Plan +Power Apps Premium embedded,POWERAPPS_PER_USER_ISVEMB,2a6fb3c6-30cc-4558-a69d-032425c1a3ba,Flow_PowerApps_PerUser,dc789ed8-0170-4b65-a415-eb77d5bb350a,Power Automate for Power Apps per User Plan Power Apps Premium for Government,POWERAPPS_PER_USER_GCC,8e4c6baa-f2ff-4884-9c38-93785d0d7ba1,CDSAICAPACITY_PERUSER,91f50f7b-2204-4803-acac-5cf5668b8b39,AI Builder capacity Per User add-on Power Apps Premium for Government,POWERAPPS_PER_USER_GCC,8e4c6baa-f2ff-4884-9c38-93785d0d7ba1,CDSAICAPACITY_PERUSER_NEW,74d93933-6f22-436e-9441-66d205435abb,AI Builder capacity Per User add-on Power Apps Premium for Government,POWERAPPS_PER_USER_GCC,8e4c6baa-f2ff-4884-9c38-93785d0d7ba1,DYN365_CDS_P2_GOV,37396c73-2203-48e6-8be1-d882dae53275,Common Data Service for Government diff --git a/Modules/CippExtensions/Public/ConversionTable.csv b/Modules/CippExtensions/Public/ConversionTable.csv index 2658524b8cf2..9407f70d6cb7 100644 --- a/Modules/CippExtensions/Public/ConversionTable.csv +++ b/Modules/CippExtensions/Public/ConversionTable.csv @@ -240,6 +240,9 @@ Dynamics 365 Enterprise Edition - Additional Production Instance for Government, "Dynamics 365 Field Service, Enterprise Edition - Resource Scheduling Optimization",CRM_AUTO_ROUTING_ADDON,977464c4-bfaf-4b67-b761-a9bb735a2196,CRM_AUTO_ROUTING_ENGINE_ADDON,24435e4b-87d0-4d7d-8beb-63a9b1573022,Field Service – Automated Routing Engine Add-On "Dynamics 365 Field Service, Enterprise Edition - Resource Scheduling Optimization",CRM_AUTO_ROUTING_ADDON,977464c4-bfaf-4b67-b761-a9bb735a2196,CRM_AUTO_ROUTING_ADDON,2ba394e0-6f18-4b77-b45f-a5663bbab540,RETIRED - Field Service – Automated Routing Engine Add-On "Dynamics 365 Field Service, Enterprise Edition - Resource Scheduling Optimization",CRM_AUTO_ROUTING_ADDON,977464c4-bfaf-4b67-b761-a9bb735a2196,EXCHANGE_S_FOUNDATION,113feb6c-3fe4-4440-bddc-54d774bf0318,Exchange Foundation +Dynamics 365 Field Service Contractor,D365_FIELD_SERVICE_CONTRACTOR,23e6e135-e869-4ce4-9ae4-5710cd69ac13,CDS_FIELD_SERVICE_CONTRACTOR,f4614a66-d632-443a-bc77-afe92987b322,Common Data Service Field service Part Time Contractors +Dynamics 365 Field Service Contractor,D365_FIELD_SERVICE_CONTRACTOR,23e6e135-e869-4ce4-9ae4-5710cd69ac13,EXCHANGE_S_FOUNDATION,113feb6c-3fe4-4440-bddc-54d774bf0318,Exchange Foundation +Dynamics 365 Field Service Contractor,D365_FIELD_SERVICE_CONTRACTOR,23e6e135-e869-4ce4-9ae4-5710cd69ac13,POWERAPPS_DYN_APPS,874fc546-6efe-4d22-90b8-5c4e7aa59f4b,Power Apps for Dynamics 365 Dynamics 365 Field Service Contractor for Government,D365_FIELD_SERVICE_CONTRACTOR_GOV,e7965e3a-1f49-4d67-a3de-ad1ce460bbcc,CDS_FIELD_SERVICE_CONTRACTOR_GCC,2457fe40-65be-48a1-935f-924ad6e62dba,Common Data Service Field service Part Time Contractors for Government Dynamics 365 Field Service Contractor for Government,D365_FIELD_SERVICE_CONTRACTOR_GOV,e7965e3a-1f49-4d67-a3de-ad1ce460bbcc,EXCHANGE_S_FOUNDATION_GOV,922ba911-5694-4e99-a794-73aed9bfeec8,Exchange Foundation for Government Dynamics 365 Field Service Contractor for Government,D365_FIELD_SERVICE_CONTRACTOR_GOV,e7965e3a-1f49-4d67-a3de-ad1ce460bbcc,POWERAPPS_DYN_APPS_GOV,3089c02b-e533-4b73-96a5-01fa648c3c3c,PowerApps for Dynamics 365 for Government @@ -2246,6 +2249,93 @@ Microsoft 365 E5,SPE_E5,06ebc4ee-1bb5-47dd-8120-11324bc54e06,AAD_PREMIUM_P2,eec0 Microsoft 365 E5,SPE_E5,06ebc4ee-1bb5-47dd-8120-11324bc54e06,INTUNE_A,c1ec4a95-1f05-45b3-a911-aa3fa01094f5,Microsoft Intune Plan 1 Microsoft 365 E5,SPE_E5,06ebc4ee-1bb5-47dd-8120-11324bc54e06,FLOW_O365_P3,07699545-9485-468e-95b6-2fca3738be01,Power Automate for Office 365 Microsoft 365 E5,SPE_E5,06ebc4ee-1bb5-47dd-8120-11324bc54e06,POWER_VIRTUAL_AGENTS_O365_P3,ded3d325-1bdc-453e-8432-5bac26d7a014,Power Virtual Agents for Office 365 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MESH_AVATARS_FOR_TEAMS,dcf9d2f4-772e-4434-b757-77a453cfbc02,Avatars for Teams +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MESH_AVATARS_ADDITIONAL_FOR_TEAMS,3efbd4ed-8958-4824-8389-1321f8730af8,Avatars for Teams (additional) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,CDS_O365_P3,afa73018-811e-46e9-988f-f75d2b1b8430,Common Data Service for Teams +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,LOCKBOX_ENTERPRISE,9f431833-0334-42de-a7dc-70aa40db46db,Customer Lockbox +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,CustomerLockboxA_Enterprise,3ec18638-bd4c-4d3b-8905-479ed636b83e,Customer Lockbox (A) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MIP_S_Exchange,cd31b152-6326-4d1b-ae1b-997b625182e6,Data Classification in Microsoft 365 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,EXCHANGE_S_ENTERPRISE,efb87545-963c-4e0d-99df-69c6916d9eb0,Exchange Online (Plan 2) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,GRAPH_CONNECTORS_SEARCH_INDEX,a6520331-d7d4-4276-95f5-15c0933bc757,Graph Connectors Search with Index +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MESH_IMMERSIVE_FOR_TEAMS,f0ff6ac6-297d-49cd-be34-6dfef97f0c28,Immersive spaces for Teams +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,INFORMATION_BARRIERS,c4801e8a-cb58-4c35-aca6-f2dcc106f287,Information Barriers +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,Content_Explorer,d9fa6af4-e046-4c89-9226-729a0786685d,Information Protection and Governance Analytics - Premium +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,ContentExplorer_Standard,2b815d45-56e4-4e3a-b65c-66cb9175b560,Information Protection and Governance Analytics – Standard +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MIP_S_CLP2,efb0351d-3b08-4503-993d-383af8de41e3,Information Protection for Office 365 - Premium +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MIP_S_CLP1,5136a095-5cf0-4aff-bec3-e84448b38ea5,Information Protection for Office 365 - Standard +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MYANALYTICS_P2,33c4f319-9bdd-48d6-9c4d-410b750a4a5a,Insights by MyAnalytics +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,M365_ADVANCED_AUDITING,2f442157-a11c-46b9-ae5b-6e39ff4e5849,Microsoft 365 Advanced Auditing +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,OFFICESUBSCRIPTION,43de0ff5-c92c-492b-9116-175376d08c38,Microsoft 365 Apps for enterprise +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MCOMEETADV,3e26ee1f-8a5f-4d52-aee2-b81ce45c8f40,Microsoft 365 Audio Conferencing +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,M365_AUDIT_PLATFORM,f6de4823-28fa-440b-b886-4783fa86ddba,Microsoft 365 Audit Platform +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MICROSOFT_COMMUNICATION_COMPLIANCE,a413a9ff-720c-4822-98ef-2f37c2a21f4c,Microsoft 365 Communication Compliance +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MTP,bf28f719-7844-4079-9c78-c1307898e192,Microsoft 365 Defender +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,M365_LIGHTHOUSE_CUSTOMER_PLAN1,6f23d6a9-adbf-481c-8538-b4c095654487,Microsoft 365 Lighthouse (Plan 1) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MCOEV,4828c8ec-dc2e-4779-b502-87ac9ce28ab7,Microsoft 365 Phone System +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MICROSOFTBOOKINGS,199a5c09-e0ca-4e37-8f7c-b05d533e1ea2,Microsoft Bookings +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,CLIPCHAMP,a1ace008-72f3-4ea0-8dac-33b3a23a2472,Microsoft Clipchamp +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,COMMUNICATIONS_DLP,6dc145d6-95dd-4191-b9c3-185575ee6f6b,Microsoft Communications DLP +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,CUSTOMER_KEY,6db1f1db-2b46-403f-be40-e39395f08dbb,Microsoft Customer Key +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,ATP_ENTERPRISE,f20fedf3-f3c3-43c3-8267-2bfdd51c0939,Microsoft Defender for Office 365 (Plan 1) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,THREAT_INTELLIGENCE,8e0c0a52-6a6c-4d40-8370-dd62790dcd70,Microsoft Defender for Office 365 (Plan 2) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,EXCEL_PREMIUM,531ee2f8-b1cb-453b-9c21-d2180d014ca5,Microsoft Excel Advanced Analytics +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,FORMS_PLAN_E5,e212cbc7-0961-4c40-9825-01117710dcb1,Microsoft Forms (Plan E5) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,INFO_GOVERNANCE,e26c2fcc-ab91-4a61-b35c-03cdc8dddf66,Microsoft Information Governance +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,INSIDER_RISK,d587c7a3-bda9-4f99-8776-9bcf59c84f75,Microsoft Insider Risk Management +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,INSIDER_RISK_MANAGEMENT,9d0c4ee5-e4a1-4625-ab39-d82b619b1a34,Microsoft Insider Risk Management - Exchange +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,KAIZALA_STANDALONE,0898bdbb-73b0-471a-81e5-20f1fe4dd66e,Microsoft Kaizala Pro +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MICROSOFT_LOOP,c4b8c31a-fb44-4c65-9837-a21f55fcabda,Microsoft Loop +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,ML_CLASSIFICATION,d2d51368-76c9-4317-ada2-a12c004c432f,Microsoft ML-Based Classification +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,EXCHANGE_ANALYTICS,34c0d7a0-a70f-4668-9238-47f9fc208882,Microsoft MyAnalytics (Full) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,PROJECTWORKMANAGEMENT,b737dad2-2f6c-4c65-90e3-ca563267e8b9,Microsoft Planner +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,RECORDS_MANAGEMENT,65cc641f-cccd-4643-97e0-a17e3045e541,Microsoft Records Management +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MICROSOFT_SEARCH,94065c59-bc8e-4e8b-89e5-5138d471eaff,Microsoft Search +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,Deskless,8c7d2df8-86f0-4902-b2ed-a0458298f3b3,Microsoft StaffHub +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,STREAM_O365_E5,6c6042f5-6f01-4d67-b8c1-eb99d36eed3e,Microsoft Stream for Office 365 E5 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,INTUNE_O365,882e1d05-acd1-4ccb-8708-6ee03664b117,Mobile Device Management for Office 365 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,Nucleus,db4d623d-b514-490b-b7ef-8885eee514de,Nucleus +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,EQUIVIO_ANALYTICS,4de31727-a228-4ec3-a5bf-8e45b5ca48cc,Office 365 Advanced eDiscovery +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,ADALLOM_S_O365,8c098270-9dd4-4350-9b30-ba4703f3b36b,Office 365 Cloud App Security +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,PAM_ENTERPRISE,b1188c4c-1b36-4018-b48b-ee07604f6feb,Office 365 Privileged Access Management +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,SAFEDOCS,bf6f5520-59e3-4f82-974b-7dbbc4fd27c7,Office 365 SafeDocs +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,SHAREPOINTWAC,e95bec33-7c88-4a70-8e19-b10bd9d0c014,Office for the Web +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,PEOPLE_SKILLS_FOUNDATION,13b6da2c-0d84-450e-9f69-a33e221387ca,People Skills - Foundation +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,POWERAPPS_O365_P3,9c0dab89-a30c-4117-86e7-97bda240acd2,Power Apps for Office 365 (Plan 3) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,BI_AZURE_P2,70d33638-9c74-4d01-bfd3-562de28bd4ba,Power BI Pro +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,PREMIUM_ENCRYPTION,617b097b-4b93-4ede-83de-5f075bb5fb2f,Premium Encryption in Office 365 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,PROJECT_O365_P3,b21a6b06-1988-436e-a07b-51ec6d9f52ad,Project for Office (Plan E5) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,PURVIEW_DISCOVERY,c948ea65-2053-4a5a-8a62-9eaaaf11b522,Purview Discovery +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,Bing_Chat_Enterprise,0d0c0d31-fae7-41f2-b909-eaf4d7f26dba,RETIRED - Commercial data protection for Microsoft Copilot +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,COMMUNICATIONS_COMPLIANCE,41fcdd7d-4733-4863-9cf4-c65b83ce2df4,RETIRED - Microsoft Communications Compliance +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,DATA_INVESTIGATIONS,46129a58-a698-46f0-aa5b-17f6586297d9,Retired - Microsoft Data Investigations +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,PLACES_CORE,1fe6227d-3e01-46d0-9510-0acad4ff6e94,RETIRED - Places Core +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,SHAREPOINTENTERPRISE,5dbe027f-2339-4123-9542-606e4d348a72,SharePoint (Plan 2) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MCOSTANDARD,0feaeb32-d00e-4d66-bd5a-43b5b83db82c,Skype for Business Online (Plan 2) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,SWAY,a23b959c-7ce8-4e57-9140-b90eb88a9e97,Sway +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,BPOS_S_TODO_3,3fb82609-8c27-4f7b-bd51-30634711ee67,To-Do (Plan 3) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,VIVAENGAGE_CORE,a82fbf69-b4d7-49f4-83a6-915b2cf354f4,Viva Engage Core +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,VIVA_LEARNING_SEEDED,b76fb638-6ba6-402a-b9f9-83d28acb3d86,Viva Learning Seeded +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,WHITEBOARD_PLAN3,4a51bca5-1eff-43f5-878c-177680f191af,Whiteboard (Plan 3) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,YAMMER_ENTERPRISE,7547a3fe-08ee-4ccb-b430-5077c5041653,Yammer Enterprise +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,WINDEFATP,871d91ec-ec1a-452b-a83f-bd76c7d770ef,Microsoft Defender for Endpoint +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MICROSOFTENDPOINTDLP,64bfac92-2b17-4482-b5e5-a0304429de3e,Microsoft Endpoint DLP +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,UNIVERSAL_PRINT_01,795f6fe0-cc4d-4773-b050-5dde4dc704c9,Universal Print +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,WIN10_PRO_ENT_SUB,21b439ba-a0ca-424f-a6cc-52f954a5b111,Windows 10/11 Enterprise (Original) +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,Windows_Autopatch,9a6eeb79-0b4b-4bf0-9808-39d99a2cd5a3,Windows Autopatch +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,WINDOWSUPDATEFORBUSINESS_DEPLOYMENTSERVICE,7bf960f6-2cd9-443a-8046-5dbff9558365,Windows Update for Business Deployment Service +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,RMS_S_PREMIUM,6c57d4b6-3b23-47a5-9bc9-69f17b4947b3,Azure Information Protection Premium P1 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,RMS_S_PREMIUM2,5689bec4-755d-4753-8b61-40975025187c,Azure Information Protection Premium P2 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,RMS_S_ENTERPRISE,bea4c11e-220a-4e6d-8eb8-8ea15d019f90,Azure Rights Management +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,DYN365_CDS_O365_P3,28b0fa46-c39a-4188-89e2-58e979a6b014,Common Data Service +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,Defender_for_Iot_Enterprise,99cd49a9-0e54-4e07-aea1-d8d9f5f704f5,Defender for IoT - Enterprise IoT Security +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,MFA_PREMIUM,8a256a2b-b617-496d-b51b-e76466e88db0,Microsoft Azure Multi-Factor Authentication +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,ADALLOM_S_STANDALONE,2e2ddb96-6af9-4b1d-a3f0-d6ecfd22edb2,Microsoft Defender for Cloud Apps +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,ATA,14ab5db5-e6c4-4b20-b4bc-13e36fd2227f,Microsoft Defender for Identity +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,AAD_PREMIUM,41781fb2-bc02-4b7c-bd55-b576c07bb09d,Microsoft Entra ID P1 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,AAD_PREMIUM_P2,eec0eb4f-6444-4f95-aba0-50c24d67f998,Microsoft Entra ID P2 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,INTUNE_A,c1ec4a95-1f05-45b3-a911-aa3fa01094f5,Microsoft Intune Plan 1 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,FLOW_O365_P3,07699545-9485-468e-95b6-2fca3738be01,Power Automate for Office 365 +Microsoft 365 E5 (no Teams),Microsoft_365_E5_(no_Teams),18a4bd3f-0b5b-4887-b04f-61dd0ee15f5e,POWER_VIRTUAL_AGENTS_O365_P3,ded3d325-1bdc-453e-8432-5bac26d7a014,Power Virtual Agents for Office 365 Microsoft 365 E5 EEA (no Teams) with Calling Minutes,Microsoft_365_E5_EEA_(no_Teams)_with_Calling_Minutes,6ee4114a-9b2d-4577-9e7a-49fa43d222d3,CDS_O365_P3,afa73018-811e-46e9-988f-f75d2b1b8430,Common Data Service for Teams Microsoft 365 E5 EEA (no Teams) with Calling Minutes,Microsoft_365_E5_EEA_(no_Teams)_with_Calling_Minutes,6ee4114a-9b2d-4577-9e7a-49fa43d222d3,LOCKBOX_ENTERPRISE,9f431833-0334-42de-a7dc-70aa40db46db,Customer Lockbox Microsoft 365 E5 EEA (no Teams) with Calling Minutes,Microsoft_365_E5_EEA_(no_Teams)_with_Calling_Minutes,6ee4114a-9b2d-4577-9e7a-49fa43d222d3,MIP_S_Exchange,cd31b152-6326-4d1b-ae1b-997b625182e6,Data Classification in Microsoft 365 @@ -5127,6 +5217,11 @@ Power Apps Premium,POWERAPPS_PER_USER,b30411f5-fea1-4a59-9ad9-3db7c7ead579,DYN36 Power Apps Premium,POWERAPPS_PER_USER,b30411f5-fea1-4a59-9ad9-3db7c7ead579,CDSAICAPACITY_PERUSER,91f50f7b-2204-4803-acac-5cf5668b8b39,DO NOT USE - AI Builder capacity Per User add-on Power Apps Premium,POWERAPPS_PER_USER,b30411f5-fea1-4a59-9ad9-3db7c7ead579,POWERAPPS_PER_USER,ea2cf03b-ac60-46ae-9c1d-eeaeb63cec86,Power Apps per User Plan Power Apps Premium,POWERAPPS_PER_USER,b30411f5-fea1-4a59-9ad9-3db7c7ead579,Flow_PowerApps_PerUser,dc789ed8-0170-4b65-a415-eb77d5bb350a,Power Automate for Power Apps per User Plan +Power Apps Premium embedded,POWERAPPS_PER_USER_ISVEMB,2a6fb3c6-30cc-4558-a69d-032425c1a3ba,Power_Pages_Internal_User,60bf28f9-2b70-4522-96f7-335f5e06c941,Power Pages Internal User +Power Apps Premium embedded,POWERAPPS_PER_USER_ISVEMB,2a6fb3c6-30cc-4558-a69d-032425c1a3ba,EXCHANGE_S_FOUNDATION,113feb6c-3fe4-4440-bddc-54d774bf0318,Exchange Foundation +Power Apps Premium embedded,POWERAPPS_PER_USER_ISVEMB,2a6fb3c6-30cc-4558-a69d-032425c1a3ba,DYN365_CDS_P2,6ea4c1ef-c259-46df-bce2-943342cd3cb2,Common Data Service +Power Apps Premium embedded,POWERAPPS_PER_USER_ISVEMB,2a6fb3c6-30cc-4558-a69d-032425c1a3ba,POWERAPPS_PER_USER,ea2cf03b-ac60-46ae-9c1d-eeaeb63cec86,Power Apps per User Plan +Power Apps Premium embedded,POWERAPPS_PER_USER_ISVEMB,2a6fb3c6-30cc-4558-a69d-032425c1a3ba,Flow_PowerApps_PerUser,dc789ed8-0170-4b65-a415-eb77d5bb350a,Power Automate for Power Apps per User Plan Power Apps Premium for Government,POWERAPPS_PER_USER_GCC,8e4c6baa-f2ff-4884-9c38-93785d0d7ba1,CDSAICAPACITY_PERUSER,91f50f7b-2204-4803-acac-5cf5668b8b39,AI Builder capacity Per User add-on Power Apps Premium for Government,POWERAPPS_PER_USER_GCC,8e4c6baa-f2ff-4884-9c38-93785d0d7ba1,CDSAICAPACITY_PERUSER_NEW,74d93933-6f22-436e-9441-66d205435abb,AI Builder capacity Per User add-on Power Apps Premium for Government,POWERAPPS_PER_USER_GCC,8e4c6baa-f2ff-4884-9c38-93785d0d7ba1,DYN365_CDS_P2_GOV,37396c73-2203-48e6-8be1-d882dae53275,Common Data Service for Government diff --git a/Modules/CippExtensions/Public/HIBP/New-BreachTenantSearch.ps1 b/Modules/CippExtensions/Public/HIBP/New-BreachTenantSearch.ps1 index 96ce636256d0..971111aa4003 100644 --- a/Modules/CippExtensions/Public/HIBP/New-BreachTenantSearch.ps1 +++ b/Modules/CippExtensions/Public/HIBP/New-BreachTenantSearch.ps1 @@ -30,7 +30,12 @@ function New-BreachTenantSearch { #Add user breaches to table if ($usersResults) { - $entity = Add-CIPPAzDataTableEntity @Table -Entity $usersResults -Force - return $LatestBreach.Result + try { + $null = Add-CIPPAzDataTableEntity @Table -Entity $usersResults -Force + return $LatestBreach.Result + } catch { + Write-Error "Failed to add breaches to table: $($_.Exception.Message)" + return $null + } } } diff --git a/Modules/CippExtensions/Public/New-CippExtAlert.ps1 b/Modules/CippExtensions/Public/New-CippExtAlert.ps1 index 28a4b957c2cb..bae549bf0719 100644 --- a/Modules/CippExtensions/Public/New-CippExtAlert.ps1 +++ b/Modules/CippExtensions/Public/New-CippExtAlert.ps1 @@ -6,13 +6,13 @@ function New-CippExtAlert { ) #Get the current CIPP Alerts table and see what system is configured to receive alerts $Table = Get-CIPPTable -TableName Extensionsconfig - $Configuration = (Get-CIPPAzDataTableEntity @Table).config | ConvertFrom-Json -Depth 10 + $Configuration = (Get-CIPPAzDataTableEntity @Table).config | ConvertFrom-Json -Depth 10 -ErrorAction SilentlyContinue $MappingTable = Get-CIPPTable -TableName CippMapping foreach ($ConfigItem in $Configuration.psobject.properties.name) { switch ($ConfigItem) { 'HaloPSA' { - If ($Configuration.HaloPSA.enabled) { + if ($Configuration.HaloPSA.enabled) { $MappingFile = Get-CIPPAzDataTableEntity @MappingTable -Filter "PartitionKey eq 'HaloMapping'" $TenantId = (Get-Tenants | Where-Object defaultDomainName -EQ $Alert.TenantId).customerId Write-Host "TenantId: $TenantId" @@ -24,7 +24,7 @@ function New-CippExtAlert { } } 'Gradient' { - If ($Configuration.Gradient.enabled) { + if ($Configuration.Gradient.enabled) { New-GradientAlert -Title $Alert.AlertTitle -Description $Alert.AlertText -Client $Alert.TenantId } } diff --git a/Modules/CippExtensions/Public/NinjaOne/Invoke-NinjaOneTenantSync.ps1 b/Modules/CippExtensions/Public/NinjaOne/Invoke-NinjaOneTenantSync.ps1 index aeb7cabfd00d..155a400aea6a 100644 --- a/Modules/CippExtensions/Public/NinjaOne/Invoke-NinjaOneTenantSync.ps1 +++ b/Modules/CippExtensions/Public/NinjaOne/Invoke-NinjaOneTenantSync.ps1 @@ -1575,7 +1575,7 @@ function Invoke-NinjaOneTenantSync { @{ Name = 'CIPP Tenant Dashboard' - Link = "https://$CIPPUrl?tenantFilter=$($Customer.defaultDomainName)" + Link = "https://$CIPPUrl/?tenantFilter=$($Customer.defaultDomainName)" Icon = 'fas fa-shield-halved' }, @{ diff --git a/Tools/IntuneWin/Get-IntuneWinPackageContents.ps1 b/Tools/IntuneWin/Get-IntuneWinPackageContents.ps1 new file mode 100644 index 000000000000..c5f2caf2e66b --- /dev/null +++ b/Tools/IntuneWin/Get-IntuneWinPackageContents.ps1 @@ -0,0 +1,32 @@ +param( + $PackagePath, + $MetadataPath +) + +# Example: .\tools\IntuneWin\Get-IntuneWinPackageContents.ps1 -PackagePath .\AddMSPApp\datto.intunewin -MetadataPath .\AddMSPApp\datto.app.xml + +$Metadata = [xml](Get-Content -Path $MetadataPath) +$Encryption = $Metadata.ApplicationInfo.EncryptionInfo + +$Package = Get-Item $PackagePath + +Write-Host "Decrypting IntuneWin package $($Package.FullName)" +Write-Host "Using encryption key: $($Encryption.EncryptionKey)" +Write-Host "Using initialization vector: $($Encryption.InitializationVector)" + +$DecoderParams = @( + "`"$($Package.FullName)`"" + "/key:`"$($Encryption.EncryptionKey)`"" + "/iv:`"$($Encryption.InitializationVector)`"" +) + +Start-Process -FilePath "$PSScriptRoot\IntuneWinAppUtilDecoder.exe" -ArgumentList $DecoderParams -Wait -NoNewWindow +# replace filename.intunewin with filename.decoded.zip +$NewFileName = "$($Package.BaseName -replace '\.intunewin$', '').decoded.zip" + +#Extract zip +Write-Host "Extracting decrypted IntuneWin package: $($Package.DirectoryName)\$NewFileName" +Expand-Archive -Path "$($Package.DirectoryName)\$NewFileName" -DestinationPath "$($Package.DirectoryName)\$($Package.BaseName)" -Force + +# Remove the decoded zip file +Remove-Item -Path "$($Package.DirectoryName)\$NewFileName" -Force diff --git a/AddChocoApp/IntunePackage/IntuneWinAppUtil.exe b/Tools/IntuneWin/IntuneWinAppUtil.exe similarity index 100% rename from AddChocoApp/IntunePackage/IntuneWinAppUtil.exe rename to Tools/IntuneWin/IntuneWinAppUtil.exe diff --git a/Tools/IntuneWin/IntuneWinAppUtilDecoder.exe b/Tools/IntuneWin/IntuneWinAppUtilDecoder.exe new file mode 100644 index 000000000000..b0c2644d14af Binary files /dev/null and b/Tools/IntuneWin/IntuneWinAppUtilDecoder.exe differ diff --git a/Tools/IntuneWin/New-IntuneWinPackage.ps1 b/Tools/IntuneWin/New-IntuneWinPackage.ps1 new file mode 100644 index 000000000000..f98bc3b64739 --- /dev/null +++ b/Tools/IntuneWin/New-IntuneWinPackage.ps1 @@ -0,0 +1,58 @@ +param( + [Parameter(Mandatory = $true)] + [string]$SourcePath, + [Parameter(Mandatory = $true)] + [string]$FileName, + [Parameter(Mandatory = $true)] + [string]$MetadataFileName +) + +# Example: .\Tools\IntuneWin\New-IntuneWinPackage.ps1 -SourcePath .\AddMSPApp\datto\ -FileName datto.intunewin -MetadataFileName datto.app.xml + +$Source = Get-Item -Path $SourcePath + +$Params = @( + "-c $SourcePath" + '-s install.ps1' + "-o $SourcePath" + '-q' +) + +Start-Process -FilePath "$PSScriptRoot\IntuneWinAppUtil.exe" -ArgumentList $Params -Wait -NoNewWindow + +Expand-Archive -Path "$SourcePath\install.intunewin" -DestinationPath $SourcePath -Force +Write-Host "IntuneWin package contents extracted to: $SourcePath" +Remove-Item -Path "$SourcePath\install.intunewin" -Force +Write-Host "Temporary IntuneWin package file removed from: $SourcePath\install.intunewin" + +# Extract IntunePackage.intunewin from Contents and move to the parent directory +$IntunePackagePath = Join-Path $SourcePath 'IntuneWinPackage\Contents\IntunePackage.intunewin' +if (Test-Path -Path $IntunePackagePath) { + Move-Item -Path $IntunePackagePath -Destination (Join-Path $Source.Parent.FullName $FileName) -Force + Write-Host "IntunePackage.intunewin moved to: $($Source.Parent.FullName)\$FileName" +} else { + Write-Host 'IntunePackage.intunewin not found in Contents directory.' +} + +# Copy the Metadata/Detection.xml file to the parent directory +$DetectionFilePath = Join-Path $SourcePath 'IntuneWinPackage\Metadata\Detection.xml' + +if (Test-Path -Path $DetectionFilePath) { + $MetadataXml = [xml](Get-Content -Path $DetectionFilePath) + $MetadataXml.ApplicationInfo.FileName = $FileName + $MetadataXml.Save($DetectionFilePath) + Write-Host "Detection.xml updated with FileName: $FileName" +} else { + Write-Host 'Detection.xml not found in Metadata directory.' +} + +if (Test-Path -Path $DetectionFilePath) { + Copy-Item -Path $DetectionFilePath -Destination (Join-Path $Source.Parent.FullName $MetadataFileName) -Force + Write-Host "Detection.xml copied to: $($Source.Parent.FullName)\$MetadataFileName" +} else { + Write-Host 'Detection.xml not found in Metadata directory.' +} + +# Clean up the Source directory +Remove-Item -Path $SourcePath -Recurse -Force +Write-Host "Temporary files cleaned up from: $SourcePath" diff --git a/cspell.json b/cspell.json index b931fc283665..56a59eca16f4 100644 --- a/cspell.json +++ b/cspell.json @@ -4,6 +4,7 @@ "dictionaryDefinitions": [], "dictionaries": [], "words": [ + "adminapi", "ADMS", "AITM", "Autotask", @@ -23,6 +24,7 @@ "gdap", "GDAP", "IMAP", + "innererror", "Intune", "locationcipp", "MAPI", diff --git a/version_latest.txt b/version_latest.txt index 6c9616431ff5..2bf50aaf17a6 100644 --- a/version_latest.txt +++ b/version_latest.txt @@ -1 +1 @@ -8.2.2 \ No newline at end of file +8.3.0